当前位置: 首页 > news >正文

【第45节】windows程序的其他反调试手段上篇

目录

引言

一、通过窗口类名和窗口名判断

二、检测调试器进程

三、父进程是否是Explorer

四、RDTSC/GetTickCount时间敏感程序段

五、StartupInfo结构的使用

六、使用BeingDebugged字段

七、 PEB.NtGlobalFlag,Heap.HeapFlags,Heap.ForceFlags

八、DebugPort:CheckRemoteDebuggerPresent()/NtQueryInformationProcess()


引言

        在Windows程序的开发与安全领域中,反调试技术是保障程序安全、防止被恶意调试分析的重要手段。上一节我们已经了解了一些相关的反调试方法,而这还远远不够。接下来我们将进一步探索更多有效的反调试技巧,篇幅分上中下三篇来讲述,该节是第一篇。

一、通过窗口类名和窗口名判断

        FindWindow函数:使用`FindWindow`函数,能依据特定的类名或者窗口名去查找对应的窗口。
        EnumWindow函数:调用`EnumWindow`函数后,系统会逐个遍历所有的顶级窗口,每遍历到一个窗口,就会调用一次回调函数。在这个回调函数里,先用`GetWindowText`函数获取窗口的标题,接着利用`strstr`函数(该函数区分大小写,与之对应的`StrStrI`函数不区分大小写)等,在窗口标题中查找是否存在“ollydbg”这样的字符串。`StrStr`函数的作用是返回`str2`首次在`str1`中出现的位置,要是没找到,就会返回`NULL`。
        GetForeGroundWindow函数:`GetForeGroundWindow`函数会返回桌面上当前处于激活状态的窗口。当程序处于被调试状态时,调用这个函数能够获取到“ollydbg”窗口的句柄,得到句柄后,就能向这个窗口发送`WM_CLOSE`消息,从而将其关闭。

关键示例代码:

 // FindWindow相关代码void CDetectODDlg::OnWndcls() {HWND hWnd;if (hWnd = ::FindWindow("ollyDbg", NULL)) {MessageBox("   发现OD");::SendMessage(hWnd, WM_CLOSE, NULL, NULL);}else {MessageBox("   没发现OD");}}// EnumWindow相关代码// 包含头文件:#include "Shlwapi.h"BOOL CALLBACK EnumWindowsProc(//handle to parent windowLPARAM lParam //application - defined value) {char ch[100];CString str = "ollydbg";if (IsWindowVisible(hwnd)) {::GetWindowText(hwnd, ch, 100);//AfxMessageBox(ch);if (::StrStrI(ch, str)) {AfxMessageBox("发现OD");return FALSE;}}return TRUE;}void CDetectODDlg::OnEnumwindow() {EnumWindows(EnumWindowsProc, NULL);AfxMessageBox("枚举窗口结束,未提示发现OD,则没有OD");}

二、检测调试器进程

        为了防范逆向分析人员通过修改调试器可执行文件名来规避检测,可采取以下方法:先对进程列表进行枚举操作,在这个过程中仔细查看是否有诸如“OLLYDBG.EXE”“windbg.exe”这类常见调试器进程存在。

        同时,借助`kernel32!ReadProcessMemory()`函数读取各个进程的内存数据,在读取到的数据里查找像“OLLYDBG”这样与调试器相关的字符串。一旦发现相关进程或字符串,就可以判定可能存在调试行为。 

关键示例代码:

 // 需要头文件:#include"tlhelp32.h"void CDetectODDlg::OnEnumProcess() {HANDLE hwnd;PROCESSENTRY32 tp32; //结构体CString str = "OLLYDBG.EXE";BOOL bFindOD = FALSE;hwnd = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);if (INVALID_HANDLE_VALUE!= hwnd) {Process32First(hwnd, &tp32);do {if (0 == lstrcmp(str, tp32.szExeFile)) {AfxMessageBox("发现OD");bFindOD = TRUE;break;}} while (Process32Next(hwnd, &tp32));if (!bFindOD)AfxMessageBox("没有OD");}CloseHandle(hwnd);}

三、父进程是否是Explorer

- 原理说明:一般来说,当我们通过双击的方式来运行程序时,这个程序进程的父进程通常是`explorer.exe`。要是情况并非如此,那就很有可能意味着该程序正处于被调试的状态。
   - 具体实现步骤:
     1. 可以通过`TEB(TEB.ClientId)`,也能使用`GetCurrentProcessId()`函数,来获取当前进程的PID(进程标识符)。
     2. 运用`Process32First/Next()`函数,能够获取到所有进程的列表。在这个列表里,要留意`explorer.exe`对应的PID,它可从`PROCESSENTRY32.szExeFile`中获取,同时还要关注当前进程的父进程PID,这个可通过`PROCESSENTRY32.th32ParentProcessID`来获取。另外,`Explorer`进程的ID还可以依据桌面窗口的类和名称来得到。
     3. 要是父进程的PID和`explorer.exe`、`cmd.exe`、`Services.exe`的PID都不相同,那么这个目标进程极有可能正在被调试。
   - 应对策略:`Olly Advanced`采用的办法是让`Process32Next()`函数一直返回`fail`,这样进程枚举就无法正常进行,PID检查也就会被跳过。实现这一操作,是通过对`kernel32!Process32NextW()`的入口代码打补丁(把`EAX`值设置为0后直接返回)来达成的。

   // (1)通过桌面类和名称获得Explorer的PID源码DWORD ExplorerID;::GetWindowThreadProcessId(::FindWindow("Progman", NULL), &ExplorerID);// (2)通过进程列表快照获得Explorer的PID源码void CDetectODDlg::OnExplorer() {HANDLE hwnd;PROCESSENTRY32 tp32; //结构体CString str = "Explorer.EXE";DWORD ExplorerID;DWORD SelfID;DWORD SelfParentID;SelfID =GetCurrentProcessId();::GetWindowThreadProcessId(::FindWindow("Progman", NULL), &ExplorerID);hwnd = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);if (INVALID_HANDLE_VALUE!= hwnd) {Process32First(hwnd, &tp32);do {if (0 == lstrcmp(str, tp32.szExeFile)) {//ExplorerID=tp32.th32ProcessID;//AfxMessageBox("aaa");}if (SelfID == tp32.th32ProcessID) {SelfParentID = tp32.th32ParentProcessID;}} while (Process32Next(hwnd, &tp32));str.Format("本进程:%d父进程:%d Explorer进程:%d", SelfID, SelfParentID, ExplorerID);MessageBox(str);if (ExplorerID == SelfParentID)AfxMessageBox("没有OD");elseAfxMessageBox("发现OD");CloseHandle(hwnd);}}

四、RDTSC/GetTickCount时间敏感程序段

原理阐述

        在进程正常运行时,CPU循环相对稳定。然而,一旦进程处于被调试状态,调试器的事件处理代码以及步过指令等操作会占用CPU资源,这就使得进程中相邻指令执行所耗费的时间大幅增加。因此,若发现相邻指令间耗时远超正常水平,基本可以判定该进程正被调试。

RDTSC操作说明

        `RDTSC`指令的功能是将计算机自启动以来CPU的运行周期数存储到`EDX:EAX`中,其中`EDX`存储高位数据,`EAX`存储低位数据。但要注意,如果`CR4`中的`TSD(time stamp disabled)`标志被置位,那么在`ring3`级别运行`rdtsc`指令会引发异常,因为这属于特权指令。为解决这个问题,需要进入`ring0`级别,将该标志置位。接着,对`OD`(OllyDbg调试器)的`WaitForDebugEvent`进行`Hook`操作,以此拦截异常事件。当捕获到异常代码属于特权指令时,读取异常处的操作码(`opcode`)进行检查。若确定是`rdtsc`指令,将指令指针(`eip`)值增加2,并通过`SetThreadContext`函数进行相关设置,此时`edx:eax`的返回值可由开发者自主决定。

GetTickCount方法

        获取当前系统时间,其原理是:当程序被调试时,调试器的事件处理代码、单步执行等操作会占用 CPU 时间,导致代码执行的时间变长。通过记录两段代码执行前后的系统时间(以毫秒为单位),计算时间差,若这个时间差超过了正常情况下的预期值(这里设定为 100 毫秒),就认为程序可能正在被调试,即检测到了调试器。

关键代码示例:

 void CDetectODDlg::OnGetTickCount() {//TOD0:Add your control notification handler code hereDWORD dTimel;DWORD dTime2;dTimel =GetTickCount();GetCurrentProcessId();GetCurrentProcessId();GetCurrentProcessId();GetCurrentProcessId();dTime2 =GetTickCount();if (dTime2 - dTimel>100) {AfxMessageBox("发现OD");}else {AfxMessageBox("没有OD");}}

五、StartupInfo结构的使用

        原理说明:在Windows操作系统里,当`explorer.exe`创建进程时,它会将`STARTUPINFO`结构里的值设置为0 。然而,要是进程不是由`explorer.exe`创建的,那么在创建过程中就会忽略`STARTUPINFO`结构中的值,这就意味着该结构里的值不会是0 。基于这个特性,我们能够以此来判断是否有像OD这样的调试器正在调试程序。 

关键示例代码:

   // 结构体定义typedef struct _STARTUPINFO {DWORD cb;           // 0000PSTR lpReserved;       // 0004PSTR lpDesktop;        // 0008PSTR lpTitle;           // 000DDWORD dwX;          // 0010DWORD dwY;          // 0014DWORD dwXSize;       // 0018DWORD dwYSize;       // 001DDWORD dwXCountChars; // 0020DWORD dwYCountChars; // 0024DWORD dwFillAttribute;  // 0028DWORD dwFlags;        // 002DWORD wShowWindow;   // 0030WORD cbReserved2;      // 0034PBYTE lpReserved2;      // 0038HANDLE hStdInput;      // 003DHANDLE hStd0utput;     // 0040HANDLE hStdError;       // 0044} STARTUPINFO, * LPSTARTUPINFO;void CDetectODDlg::OnGetStartupInfo() {STARTUPINFO info;GetStartupInfo(&info);if (info.dwX!= 0 || info.dwY!= 0|| info.dwXCountChars!= 0 || info.dwYCountChars!= 0|| info.dwFillAttribute!= 0 || info.dwXSize!= 0 || info.dwYSize!= 0)AfxMessageBox("发现OD");elseAfxMessageBox("没有OD");}

六、使用BeingDebugged字段

原理阐述

        在Windows系统环境下,存在一个名为`kernel32!IsDebuggerPresent()`的API函数,它的作用是通过检测进程环境块(PEB)里的`BeingDebugged`标志,来判断当前进程是否正被用户模式的调试器调试。每个进程都有自己的PEB结构,一般来说,获取PEB地址得借助线程环境块(TEB)。具体来说,`Fs:[0]`这个地址指向的是当前线程的TEB结构。在TEB结构里,偏移量为0的位置是线程信息块结构TIB 。而在TIB结构中,偏移18H的地方有个`self`字段,它其实是TIB的反身指针,这个指针又指向TIB(同时也是PEB)的起始地址。另外,在TEB偏移30H处,有一个指针指向PEB结构。在PEB结构里,偏移2H的位置就是`BeingDebugged`字段,其数据类型为`Uchar` 。

检测方式介绍

        1. 调用函数间接读取:可以调用`IsDebuggerPresent`函数,该函数会间接地读取`BeingDebugged`字段的值,以此判断进程是否被调试。
        2. 直接地址读取:也能通过获取的地址,直接去读取`BeingDebugged`字段,进而判断进程的调试状态 。

应对策略说明

        1. 手动修改标志值:在数据窗口中,通过按下`Ctrl + G`组合键,输入`fs:[30]`,就可以查看PEB数据。找到`PEB.BeingDebugged`标志后,将其值设置为0 。
        2. 使用Ollyscript命令:利用`Ollyscript`中的“`dbh`”命令,能够对`PEB.BeingDebugged`这个标志进行补丁操作 。

关键代码示例:

   void CDetectODDlg::OnIsdebuggerpresent() {if (IsDebuggerPresent())MessageBox("发现OD");elseMessageBox("没有OD");}

七、 PEB.NtGlobalFlag,Heap.HeapFlags,Heap.ForceFlags

        PEB.NtGlobalFlag情况:一般情况下,程序没被调试时,PEB里有个成员叫`NtGlobalFlag`(偏移量是0x68),它的值是0。要是进程正在被调试,这个值通常会变成0x70,这代表下面这些标志被设置了:
        - `FLG_HEAP_ENABLE_TAIL_CHECK(0X10)`
        - `FLG_HEAP_ENABLE_FREE_CHECK(0X20)`
        - `FLG_HEAP_VALIDATE_PARAMETERS(0X40)`
        这些标志是在`ntdll!LdrpInitializeExecutionOptions()`函数里进行设置的。要注意,`PEB.NtGlobalFlag`的默认值可以通过`gflags.exe`工具来修改,也能在注册表的`HKLM\Software\Microsoft\Windows Nt\CurrentVersion\Image File Execution Options`位置创建条目进行修改。下面是相关的汇编代码:

mov eax,fs:[30h]
mov eax,[eax+68h]
and eax,70h

        堆标志情况:因为设置了`NtGlobalFlag`标志,堆也会开启几个标志,这个变化能在`ntdll!Rt1CreateHeap()`函数里观察到。正常情况下,系统给进程创建第一个堆时,会把`Flags`设为2(也就是`HEAP_GROWABLE`),把`ForceFlags`设为0。要是进程正在被调试,这两个标志通常会分别设为50000062(具体数值取决于`NtGlobalFlag`)和0x40000060(这个值等于`Flags AND 0x6001007D`)。下面是相关的汇编代码:

assume fs:nothing
mov ebx,fs:[30h]     ;ebx指向PEB
mov eax,[ebx+18h]   ;PEB.ProcessHeap
cmp dword ptr [eax+0ch],2    ;PEB.ProcessHeap.Flags
jne debugger_found
cmp dword ptr [eax+10h],0          ;PEB.ProcessHeap.ForceFlags
jne debugger_found

        这些标志位的变化都是由`BeingDebugged`引发的。系统在创建进程时,会将`BeingDebugged`设为`TRUE`。之后,`NtGlobalFlag`会依据这个标记,设置如`FLGVALIDATEPARAMETERS`等标记。在为进程创建堆时,受`NtGlobalFlag`影响,堆的`Flags`会被设置一些标记。这些标记随后会被填入`ProcessHeap`的`Flags`和`ForceFlags`中。同时,堆里会被填入很多类似“BAADFOOD”的内容(也就是`HeapMagic`,也能用于检测调试情况)。若要一次性解决这些状态相关问题,可查看《加密解密》413页。 

关键示例代码:

 typedef NTSTATUS(_stdcall *ZwQueryInformationProcess)(HANDLE ProcessHandle,PROCESSINFOCLASS ProcessInformationClass,PVOID ProcessInformation,ULONG ProcessInformationLength,PULONG ReturnLength);//定义函数指针void CDetectODDlg::OnPebFlags() {//定义函数指针变量ZwQueryInformationProcess MyZwQueryInformationProcess;HANDLE hProcess = NULL;PROCESS_BASIC_INFORMATION pbi = {0};LLONG peb = 0;LLONG cnt = 0;LLONG PebBase = 0;LLONG AddrBase;BOOL blFoundOD = FALSE;WORD flag;DWORD dwFlag;DWORD bytesrw;DWORD Processld = GetCurrentProcessId();hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processld);if (hProcess!= NULL) {//函数指针变量赋值MyZwQueryInformationProcess=(ZwQueryInformationProcess)GetProcAddress(LoadLibrary("ntdll.dll"),"ZwQueryInformationProcess");//函数指针变量调用if (MyZwQueryInformationProcess(hProcess,ProcessBasicInformation,&pbi,sizeof(PROCESS_BASIC_INFORMATION),&cnt) == 0) {PebBase = (ULONG)pbi.PebBaseAddress;  //获取PEB地址AddrBase = PebBase;//读内存地址if (ReadProcessMemory(hProcess, (LPCVOID)(PebBase + 0x68), &flag, 2, &bytesrw) && bytesrw == 2) {//PEB.NtGlobalFlagif (0x70 == flag) {bFoundOD = TRUE;if (ReadProcessMemory(hProcess, (LPCVOID)(PebBase + 0x18), &dwFlag, 4, &bytesrw) && bytesrw == 4) {AddrBase = dwFlag;if (ReadProcessMemory(hProcess, (LPCVOID)(AddrBase + 0x0c), &flag, 2, &bytesrw) && bytesrw == 2) {//PEB.ProcessHeap.Flagsif (2!= flag) {bFoundOD = TRUE;if (ReadProcessMemory(hProcess, (LPCVOID)(AddrBase + 0x10), &flag, 2, &bytesrw) && bytesrw == 2) {//PEB.ProcessHeap.Forceflagsif (0!= flag) {bFoundOD = TRUE;}}}}}}if (bFoundOD == FALSE)AfxMessageBox("没有OD");else {AfxMessageBox("发现OD");}}CloseHandle(hProcess);}}}

八、DebugPort:CheckRemoteDebuggerPresent()/NtQueryInformationProcess()

        `Kernel32!CheckRemoteDebuggerPresent()` 这个函数的作用是判断有没有调试器附加到某个进程上。它的函数原型如下: 

BOOL CheckRemoteDebuggerPresent(HANDLE hProcess,PBOOL pbDebuggerPresent
);

        `Kernel32!CheckRemoteDebuggerPresent()` 函数需要 2 个参数。第一个参数是进程句柄,第二个参数是指向 `boolean` 变量的指针。要是进程正在被调试,这个变量就会是 `TRUE`。这个 API 实际上是调用了 `ntdll!NtQuery InformationProcess()` 来完成检测。

        下面定义函数指针:

typedef BOOL(WINAPI*CHECK_REMOTE_DEBUGGER_PRESENT)(HANDLE, PBOOL);

        检测函数的实现:

void CDetectODDlg::OnCheckremotedebuggerpresent() {HANDLE hProcess;HINSTANCE hModule;BOOL bDebuggerPresent = FALSE;CHECK_REMOTE_DEBUGGER_PRESENT CheckRemoteDebuggerPresent; // 建立函数指针变量hModule = GetModuleHandleA("Kernel32"); // 地址要从模块中动态获得CheckRemoteDebuggerPresent = (CHECK_REMOTE_DEBUGGER_PRESENT)GetProcAddress(hModule, "CheckRemoteDebuggerPresent"); // 获取地址hProcess = GetCurrentProcess();CheckRemoteDebuggerPresent(hProcess, &bDebuggerPresent); // 调用if (bDebuggerPresent == TRUE) {AfxMessageBox("发现OD");}else {AfxMessageBox("没有OD");}
}

        `ntdll!NtQueryInformationProcess()` 函数有 5 个参数。要是想检测调试器是否存在,得把 `ProcessInformationclass` 参数设置成 `ProcessDebugPort(7)`。`NtQueryInformationProcess()` 会去获取内核结构 `EPROCESS` 里的 `DebugPort` 成员,这个成员其实是系统和调试器进行通信时用的端口句柄。要是 `DebugPort` 成员的值不是 0,就说明进程正在被用户模式的调试器调试。这种情况下,`ProcessInformation` 会被设为 `0xFFFFFFFF`;要是没有被调试,`ProcessInformation` 就会被设为 0。

        `ZwQueryInformationProcess` 函数的原型如下:

ZwQueryInformationProcess(IN HANDLE ProcessHandle,IN PROCESSINFOCLASS ProcessInformationClass,OUT PVOID ProcessInformation,IN ULONG ProcessInformationLength,OUT PULONG ReturnLength OPTIONAL
);

        定义函数指针如下:

typedef NTSTATUS(__stdcall *ZW_QUERY_INFORMATION_PROCESS)(HANDLE ProcessHandle,PROCESSINFOCLASS ProcessInformationClass, // 该参数也需要上面声明的数据结构PVOID ProcessInformation,ULONG ProcessInformationLength,PULONG ReturnLength
);

        检测函数的实现:

void CDetectODDlg::OnZwqueryinfomationprocess() {//TODO: Add your control notification handler code hereHANDLE hProcess;HINSTANCE hModule;DWORD dwResult;ZW_QUERY_INFORMATION_PROCESS MyFunc;hModule = GetModuleHandle("ntdll.dll");MyFunc = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");hProcess = GetCurrentProcess();MyFunc(hProcess, ProcessDebugPort, &dwResult, 4, NULL);if (dwResult != 0) {AfxMessageBox("发现OD");}else {AfxMessageBox("没有OD");}
}

 

 

相关文章:

【第45节】windows程序的其他反调试手段上篇

目录 引言 一、通过窗口类名和窗口名判断 二、检测调试器进程 三、父进程是否是Explorer 四、RDTSC/GetTickCount时间敏感程序段 五、StartupInfo结构的使用 六、使用BeingDebugged字段 七、 PEB.NtGlobalFlag,Heap.HeapFlags,Heap.ForceFlags 八、DebugPort:CheckRem…...

通过人类和机器人演示进行联合逆向和正向动力学的机器人训练

25年3月来自哥伦比亚大学的论文“Train Robots in a JIF: Joint Inverse and Forward Dynamics with Human and Robot Demonstrations”。 在大型机器人演示数据集上进行预训练是学习各种操作技能的强大技术,但通常受到收集以机器人为中心数据的高成本和复杂性限制…...

云渗透三(对象存储攻防)

对象存储是什么? 对象存储(Cloud Object Storage,COS)是腾讯云提供的⼀种存储海量⽂件的分布式存储服务,⽤户可通过⽹络随时存储和查看数据。腾讯云 COS 使所有⽤户都能使⽤具备⾼扩展性、低成本、可靠和安全的数据存储服务。 COS 通过控制台、API、SDK 和⼯具等多样化⽅…...

ShenNiusModularity项目源码学习(17:ShenNius.Admin.Mvc项目分析-2)

ShenNiusModularity项目的后台管理主页面如下图所示,该页面为ShenNius.Admin.Mvc项目的Views\Home\Index.cshtml,使用的是layuimini后台模板(参考文献2),在layuimini的GitHub主页中提供有不同样式的页面模版链接&#…...

Sentinel源码—2.Context和处理链的初始化一

大纲 1.Sentinel底层的核心概念 2.Sentinel中Context的设计思想与源码实现 3.Java SPI机制的引入 4.Java SPI机制在Sentinel处理链中的应用 5.Sentinel默认处理链ProcessorSlot的构建 1.Sentinel底层的核心概念 (1)资源和规则 (2)Context (3)ProcessorSlot (4)Node (1…...

嵌入式C语言高级编程:OOP封装、TDD测试与防御性编程实践

一、面向对象编程(OOP) 尽管 C 语言并非面向对象编程语言,但借助一些编程技巧,也能实现面向对象编程(OOP)的核心特性,如封装、继承和多态。 1.1 封装 封装是把数据和操作数据的函数捆绑在一起,对外部隐藏…...

C++中unique_lock和lock_guard区别

目录 1.自动锁定与解锁机制 2.灵活性 3.所有权转移 4.可与条件变量配合使用 5.性能开销 在 C 中&#xff0c;std::unique_lock 和 std::lock_guard 都属于标准库 <mutex> 中的互斥锁管理工具&#xff0c;用于简化互斥锁的使用并确保线程安全。但它们存在一些显著区别…...

C# 使用.NET内置的 IObservable<T> 和 IObserver<T>-观察者模式

核心概念 IObservable<T> 表示 可观察的数据源&#xff08;如事件流、实时数据&#xff09;。 关键方法&#xff1a;Subscribe(IObserver<T> observer)&#xff0c;用于注册观察者。 IObserver<T> 表示 数据的接收者&#xff0c;响应数据变化。 三个核心…...

多模态大模型文字识别 vs OCR识别模型

论文简述 多模态大语言模型(Multimodal Large Language Models,简称多模态LLMs)具有高度通用性,能够处理图像描述、文档分析和自动化内容生成等多种任务。这种广泛的适用性使其在不同工业领域都受到了大量关注。 在OCR方面,多模态LLMs的表现超过了专门为OCR设计的模型。这…...

[区块链] 持久化运行区块链 | 并通过HTTP访问

实验目标 以Web服务的形式持久化运行区块链&#xff0c;并通过HTTP接口的形式实现对区块链的操作。 实验内容 构建区块链的区块对象和区块链对象。使用Flask等Web服务框架运行持久化的进程&#xff0c;实现基于HTTP接口实现新区块的添加功能以及传递区块索引查询区块链中的区…...

批量将不同位置的多个文件复制到一个文件夹

在日常工作中&#xff0c;我们常常需要将多个位置的文件集中到一个文件夹中。手动一个个复制文件既费时又容易出错。使用批量文件复制工具&#xff0c;您可以轻松将不同位置的多个文件快速复制到一个文件夹中&#xff0c;大大提高工作效率。 今天给大家介绍一下批量将不同位置的…...

CentOS 下 Zookeeper 常用命令与完整命令列表

一、服务管理命令 常用服务命令 启动 Zookeeper 服务: systemctl start zookeeper # 使用 systemd (推荐) /usr/local/zookeeper/bin/zkServer.sh start # 直接启动 停止服务: systemctl stop zookeeper /usr/local/zookeeper/bin/zkServer.sh stop 查看服务状态…...

React与Vue:哪个框架更适合入门?

React与Vue&#xff1a;选择哪个框架入门&#xff1f; 一、框架定位与发展趋势 1.1 技术背景对比 ‌React‌&#xff1a;Meta&#xff08;原Facebook&#xff09;2013年推出&#xff0c;采用声明式编程范式&#xff0c;专注构建用户界面‌Vue‌&#xff1a;尤雨溪2014年推出…...

突发!GitHub 被曝封禁中国区 IP

昨天&#xff0c;大量国内开发者发现&#xff0c;未登录状态下访问GitHub官网时&#xff0c;页面显示“Access to this site has been restricted.”&#xff0c;中国大陆及香港IP均被限制。尽管GitHub官方尚未发布声明&#xff0c;但多方实测显示&#xff1a; 猪哥亲测在已登…...

向量数据库Qdrant 安装 不使用docker

一、导读 环境&#xff1a;Ubuntu 24.04、Windows 10、WSL 2、Qdrant 1.13.4 背景&#xff1a;换了新工作&#xff0c;使用qdrant作为向量库&#xff0c;需要不使用docker安装 时间&#xff1a;20250415 说明&#xff1a;初入职&#xff0c;不了解&#xff0c;暂且记下 二、…...

基于坐标的神经表示实现零样本学习以用于快速三维多参数定量磁共振成像|文献速递-深度学习医疗AI最新文献

Title 题目 Coordinate-based neural representation enabling zero-shot learning for fast 3D multiparametric quantitative MRI 基于坐标的神经表示实现零样本学习以用于快速三维多参数定量磁共振成像 01 文献速递介绍 定量磁共振成像&#xff08;qMRI&#xff09;能…...

每日算法-250415

每日算法 - 2024-04-15: 今天我们来解决两道 LeetCode 上关于在旋转排序数组中寻找最小值的问题。 153. 寻找旋转排序数组中的最小值 题目描述 思路 核心思想是利用 二分查找。 解题过程 一个升序排序的数组&#xff08;无重复元素&#xff09;在经过若干次旋转后&#xf…...

第16届蓝桥杯省赛JavaB组真题回顾

第16届蓝桥杯省赛已经结束了&#xff0c;第一次参加也是坐牢了4个小时&#xff0c;现在还是来总结一下吧&#xff08;先声明以下的解法&#xff0c;大家可以当作一种思路来看&#xff0c;解法不一定是正解&#xff0c;只是给大家提供一种能够正常想到的思路吧&#xff09; 试题…...

[Godot] C#人物移动抖动解决方案

在写一个2D平台跳跃的游戏代码发现&#xff0c;移动的时候会抖动卡顿的厉害&#xff0c;后来研究了一下抖动问题&#xff0c;有了几种解决方案 1.垂直同步和物理插值问题 这是最常见的可能导致画面撕裂和抖动的原因&#xff0c;大家可以根据自己的需要调整项目设置&#xff0…...

hackmyvm-quick2

收集信息 arp-scan -l nmap 192.168.66.74 dirsearch -u http://192.168.66.74 -e * 访问file.php 尝试查看/etc/passwd 抓包 payload: /file.php?file=/home/andrew/.bashrc /file.php?file=/home/nick/.bashrc...

TDengine 与其他时序数据库对比:InfluxDB/TimescaleDB 选型指南(一)

引言 在当今数字化时代&#xff0c;物联网&#xff08;IoT&#xff09;、工业互联网等领域蓬勃发展&#xff0c;产生了海量的时序数据。这些数据记录了设备状态、传感器读数、业务指标等随时间变化的信息&#xff0c;对于企业的运营监控、数据分析和决策制定起着关键作用。为了…...

Jetson agx Orin 适配Xenomai4

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 目录 文章目录 前言 一、Xenomai 4 二、使用步骤 1.获取源码 2.编译源码 3、安装 4、运行效果 5、libevl 总结 前言 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了…...

Terraform - 理解 Count 和 For_Each 循环

概述 使用 Terraform 时&#xff0c;您可能需要为同一资源创建多个实例。这时 count 和 for_each 循环就派上用场了。这些循环允许您创建具有相同配置但具有不同值的多个资源。本指南将讲解如何在 Terraform 中使用 count 和 for_each 循环。 Terraform 中的 Count Terraform …...

Git本地更新和远端同步操作

1、将远端文件夹拉取到本地 从0—1&#xff1a;克隆 在文件夹空白处点击&#xff1a;Git克隆 输入url&#xff0c;克隆到本地 输入远端文件地址&#xff0c;点击确定&#xff0c;远端文件即可克隆到本地 2、拉取远端更新到本地 文件克隆到本地后&#xff0c;每次更新需…...

激活函数Softmax在不同维度上的应用 | dim参数理解

理解 Softmax 在不同维度上的应用&#xff0c;关键在于明确 Softmax 的作用以及 dim 参数的意义。Softmax 的目标是将输入张量的某些元素转换为概率分布&#xff0c;使得这些元素的和为1。dim 参数决定了沿着哪个维度进行归一化操作。 1. Softmax 的基本原理 Softmax 函数的公…...

地理人工智能中位置编码的综述:方法与应用

以下是对论文 《A Review of Location Encoding for GeoAI: Methods and Applications》 的大纲和摘要整理&#xff1a; A Review of Location Encoding for GeoAI: Methods and Applications 摘要&#xff08;Summary&#xff09; 本文系统综述了地理人工智能&#xff08;G…...

6.DJI-PSDK:psdk订阅无人机高度/速度/GPS/RTK/时间/经纬度等消息及问题解决

DJI-PSDK:psdk订阅无人机高度/速度/GPS/RTK/时间/经纬度等消息 消息订阅可以获取绝大多数无人机的动态信息,包括无人机的姿态、速度、加速度、角速度、高度、GPS 位置、云 台的角度和状态、飞行模式和飞行状态、电机和电池等各类关键信息。 这些信息并不会“一股脑儿地”全部…...

ArrayList的subList的数据仍是集合

ArrayList的subList结果不可强转成ArrayList&#xff0c;否则会抛出 ClassCastException异常 • 级别&#xff1a; 【CRITICAL】 • 规约类型&#xff1a;BUG • 最坏影响&#xff1a; 程序错误&#xff0c;抛出异常 说明&#xff1a;subList 返回的是ArrayList的内部类SubL…...

VS Code怎么换颜色

在 VS Code 中&#xff0c;你可以通过修改主题或自定义编辑器颜色来将界面改为白色。以下是几种方法&#xff1a; 方法 1&#xff1a;切换浅色主题&#xff08;推荐&#xff09; VS Code 自带多个浅色主题&#xff0c;可以直接切换&#xff1a; 按快捷键 Ctrl K&#xff0c;…...

jupyter中切换Anaconda虚拟环境

jupyter中切换虚拟环境 查看python路径 import sys sys.executable这个命令会输出当前 Notebook 使用的 Python 可执行文件的路径 2. 查看安装的包和环境信息&#xff08;前两步都在jupyter notebook中进行&#xff09; !which python !python --version !pip list将你的虚拟…...

红帽认证 Linux安全 级别

红帽认证体系将安全能力划分为‌专项技能认证‌与‌架构师路径‌&#xff0c;覆盖从基础加固到企业级安全架构设计。以下是安全相关认证的级别、核心内容及职业定位&#xff1a; 一、红帽安全认证分级与定位‌ 认证名称级别考试代码核心方向考试难度适用人群‌Red Hat Certif…...

从服务器多线程批量下载文件到本地

1、客户端安装 aria2 下载地址&#xff1a;aria2 解压文件&#xff0c;然后将文件目录添加到系统环境变量Path中&#xff0c;然后打开cmd&#xff0c;输入&#xff1a;aria2c 文件地址&#xff0c;就可以下载文件了 2、服务端配置nginx文件服务器 server {listen 8080…...

WPF 中的元素继承层次结构 ,以下是对图中内容的详细说明:

顶层基类 DispatcherObject&#xff1a;处于继承体系最顶端&#xff0c;是一个抽象类。它为 WPF 元素提供了与 Dispatcher&#xff08;调度器&#xff09;交互的能力&#xff0c;Dispatcher 负责管理线程间的消息传递&#xff0c;确保 UI 操作在正确的线程&#xff08;通常是 …...

ROS IkFast运动学插件

ikFast运动学插件生成 环境安装ROS依赖安装openrave依赖下载依赖代码工具使用urdf转dae设置精度查看模型关节生成代码生成插件 环境 ubuntu 20.04ROS版本&#xff1a;noetic 安装ROS依赖 sudo apt-get install -y ros-noetic-desktop ros-noetic-collada-urdf ros-noetic-mov…...

Flink Hive Catalog最佳实践

Flink Hive Catalog 最佳实践 一、配置与初始化 依赖管理 Hive Connector 版本对齐&#xff1a;需确保 flink-sql-connector-hive 版本与 Hive 版本严格匹配&#xff08;如 Hive 3.1.3 对应 flink-sql-connector-hive-3.1.3_2.12&#xff09;&#xff0c;同时添加 Hadoop 遮蔽…...

接口测试之postman使用指南

一、为何使用postman postman是一款简单高效的接口测试工具&#xff0c;能够很方便发送接口请求&#xff0c;易于保存接口请求脚本&#xff0c;postman提供接口响应数据比对功能&#xff0c;可以设置预期结果作断言&#xff0c;还能把测试用例放在一个集合中批量执行&#xff…...

小豆包api:支持GPT-4.1全新系列模型api,纯官逆向可用!

今天凌晨&#xff0c;OpenAI正式发布GPT-4.1系列模型&#xff0c;带来标准版GPT-4.1&#xff0c;更轻量快速GPT-4.1 mini和极致性价比的GPT-4.1 nano三款模型&#xff0c;全面超越GPT-4o&#xff0c;更聪明、更便宜。 小豆包api已全面支持GPT-4.1全新系列模型&#xff01; 与前…...

GPTNet如何革新创意与效率

引言 人工智能正在以前所未有的速度改变我们的工作与生活方式&#xff0c;从智能写作到视觉创作&#xff0c;AI工具已成为不可或缺的伙伴。在众多平台中&#xff0c;GPTNet以其强大的功能整合和直观体验崭露头角。它不仅汇集了GPT系列、Claude、Grok、Gemini等顶级对话模型&am…...

3.3.6 VO-O语法- 流程算子

流程算子是VO语言最重要的一类语法算子。它们是VO语言特有的语法概念。这些算子属于容器算子&#xff0c;用于组织管理各类流程。这样的设计有利于流程的复用。可以将流程视为一个大级别的函数模块&#xff0c;在更大的业务逻辑中复用&#xff0c;从而降低业务开发的复杂度&…...

c++引入nacos,详细步骤

以下是将Nacos引入C项目的详细步骤&#xff0c;包括安装、配置和代码实现&#xff1a; 1. 安装Nacos服务器 下载Nacos服务器安装包&#xff0c;可以从Nacos官网获取最新版本。 解压安装包并启动Nacos服务器&#xff1a; cd nacos/bin sh startup.sh -m standalone 这将启动…...

OpenAI 最新发布的 GPT-4.1 系列在 API 中正式上线

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

亚远景-自动驾驶时代,ASPICE评估标准将如何演进?

自动驾驶时代&#xff0c;ASPICE评估标准将从以下几个方面演进&#xff1a; 1. 技术领域的扩展 增加硬件工程和机器学习过程组 &#xff1a;ASPICE 4.0版本新增了硬件工程和机器学习过程组&#xff0c;以应对自动驾驶系统中硬件与软件深度融合的需求。自动驾驶功能的实现不仅依…...

Redis 数据类型全解析:从基础到实战应用

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 Redis 作为高性能的键值对存储系统&#xff0c;其丰富的数据类型是实现复杂业务逻辑的核心优势。本文将深入解析 Redis 六大核心数据类型及扩展类型&#xff…...

基于JSP+MySQL实现用户注册登录及短信发送功能

用户注册信息存入数据库 内容介绍 此项目是前台用户的登录和注册&#xff0c;,这部分包括两个子部分:其中一个为新用户注册&#xff0c;在这里我们可以通过注册浏览更多信息;另一个是用户登录&#xff0c;用户登录系统是一.般 Web 网站都有的子系统,其作用是限制该网站某些资…...

Spark-SQL核心编程2

路径问题 相对路径与绝对路径&#xff1a;建议使用绝对路径&#xff0c;避免复制粘贴导致的错误&#xff0c;必要时将斜杠改为双反斜杠。 数据处理与展示 SQL 风格语法&#xff1a;创建临时视图并使用 SQL 风格语法查询数据。 DSL 风格语法&#xff1a;使用 DSL 风格语法查询…...

Flink SQL SavePoint最佳实践

以下是 Flink SQL Savepoint 最佳实践&#xff0c;涵盖配置、触发、恢复及注意事项&#xff0c;高效管理作业状态&#xff1a; 一、Savepoint 的配置与触发 1. 基础配置 存储路径&#xff1a;在 flink-conf.yaml 中全局设置 Savepoint 存储目录&#xff0c;避免每次手动指定路…...

【无标题】Spark-SQL编程(2)

以下是今天学习的知识点以及代码测试&#xff1a; Spark-SQL核心编程&#xff08;四&#xff09; 实验内容&#xff1a; 利用IDEA开发Spark-SQL。 实验步骤&#xff1a; 利用IDEA开发Spark-SQL 创建子模块Spark-SQL&#xff0c;并添加依赖 <dependency> <grou…...

django项目之添加资产信息功能

资产信息功能添加 目录 1.创建表格 2.页面展示 3.新增和修改 4.删除 这篇文章的内容, 就是在前面做的基础上, 新增一个资产管理功能, 其实就和之前的部门信息还有员工信息差不多的, 但是这篇文章里面, 会有些细节的地方, 和之前的不太一样。那下面我们就开始讲述这篇文章…...

macos下 ~/.zshrc~ 和 ~/.zshrc

macos下 ~/.zshrc~ 和 ~/.zshrc ~/.zshrc通常是备份文件或临时文件&#xff0c;可能由编辑器&#xff08;如vim&#xff09;创建&#xff0c;通常可以安全删除&#xff0c;不会影响系统运行。 在Mac下&#xff0c;这种带~后缀的备份文件通常是由以下情况产生&#xff1a; 常…...

Kotlin集合操作陷阱与突围:如何优雅地边遍历边修改MutableList

在Kotlin开发中&#xff0c;MutableList是我们常用的集合类型之一。但当我们需要在遍历列表的同时修改它&#xff08;添加或删除元素&#xff09;时&#xff0c;很多开发者会遇到ConcurrentModificationException异常。本文将详细介绍如何安全高效地实现这一需求。 一、问题场…...