zoukankan      html  css  js  c++  java
  • 静态反调试技术

    @author: dlive

    0x01 PEB

    PEB中与反调试技术密切相关的成员有如下几个

    +0x002 BeingDebugged; UChar
    +0x00c Ldr 			; Ptr32 _PEB_LDR_DATA
    +0X018 ProcessHeap	; Ptr32 Void
    +0x068 NtGlobalFlag	; Uint4B
    

    BeingDebugged成员是一个标志,用来表示进程是否处于被调试状态

    Ldr, ProcessHeap, NtGloabFlag成员与被调试进程的堆内存特性相关

    获取PEB结构体的地址

    # 第一种方法
    MOV EAX, DWORD PTR FS:[0x30]
    # 第二种方法
    MOV EAX, DWORD PTR FS:[0x18]
    MOV EAX, DWORD PTR DS:[EAX+0x30]
    

    BeingDebugged(+0x2)

    通过IsDebuggerPresent() API可以获取PEB.BeingDebugged的值,若为1则表示进程处于被调试状态

    破解方法:只要借助OD,将PEB.BeingDebugged的值修改为0即可

    Ldr(+0xC)

    调试进程时其堆内存就会出现一些特殊标识,表示它正处于被调试状态

    其中最醒目的是,未使用的堆内存区域全部填充着0xFEEEFEEE,这证明正在调试进程

    PEB.Ldr成员是一个指向_PEB_LDR_DATA结构体的指针,而_PEB_LDR_DATA结构体恰好是在堆内存区域中创建的,所以扫描该区域即可轻松查找是否存在0xFEEEFEEE区域

    破解方法:只要将填充着0xFEEEFEEE的区域全部覆写为NULL即可

    注意:该方法只适用于WindowsXP,而在Windows Vista以后的系统中则无法使用。另外,利用附加功能将运行中的进程附加到调试器时,堆内存中并不出现上述标识。

    ProcessHeap(+0x18)

    PEB.ProcessHeap成员是指向HEAP结构体的指针

    GetProcessHeap() API可以获取PEB.ProcessHeap结构体的地址

    HEAP结构体中Flags(+0xC)和ForceFlags(+0x10)和反调试相关,进程正常运行时,Heap.Flags的值为0x2,Heap.ForceFlags的值为0x0,进程处于调试状态时这些值会改变。

    破解方法:修改HEAP.Flags =2, HEAP.ForceFlags=0

    注意:该方法只适用于WindowsXP,而在Windows Vista以后的系统中则无法使用。另外,利用附加功能将运行中的进程附加到调试器时,也不会有上述特征。

    NtGlobalFlag(0x68)

    调试进程时,PEB.NtGlobalFlag会被设置为0x70

    注意:利用附加功能将运行中的进程附加到调试器时,不会有上述特征。

    0x02 NtQueryInformationProcess API

    https://msdn.microsoft.com/en-us/library/ms684280(VS.85).aspx

    NTSTATUS WINAPI NtQueryInformationProcess(
      _In_      HANDLE           ProcessHandle,
      _In_      PROCESSINFOCLASS ProcessInformationClass,
      _Out_     PVOID            ProcessInformation,
      _In_      ULONG            ProcessInformationLength,
      _Out_opt_ PULONG           ReturnLength
    );
    

    为该函数第二个参数ProcessInformationClass设定特定的值,函数执行结果会保存在第三个参数ProcessInformation中

    第二个参数为枚举类型,其中与反调试相关的参数有ProcessDebugPort(0x07),ProcessDebugObject-Handle(0x1E),ProcessDebugFlags(0x1F)

    ProcessDebugPort(0x7)

    非调试状态下debugport == 0

    // ProcessDebugPort (0x7)
        DWORD dwDebugPort = 0;
        pNtQueryInformationProcess(GetCurrentProcess(),
                                   ProcessDebugPort,
                                   &dwDebugPort,
                                   sizeof(dwDebugPort),
                                   NULL);
        printf("NtQueryInformationProcess(ProcessDebugPort) = 0x%X
    ", dwDebugPort);
        if( dwDebugPort != 0x0  )  printf("  => Debugging!!!
    
    ");
        else                       printf("  => Not debugging...
    
    ");
    

    checkRemoteDebuggerPresent()API可以用来检测进程是否处于被调试状态,与IsDebuggerPresent API不同的是,它不仅可以检测当前进程是否处于被调试状态,也可检测其他进程。该函数的实现中使用了NtQueryInformationProcess(ProcessDebugPort) API

    ProcessDebugObjectHandle(0x1E)

    // ProcessDebugObjectHandle (0x1E)
        HANDLE hDebugObject = NULL;
        pNtQueryInformationProcess(GetCurrentProcess(),
                                   ProcessDebugObjectHandle,
                                   &hDebugObject,
                                   sizeof(hDebugObject),
                                   NULL);
        printf("NtQueryInformationProcess(ProcessDebugObjectHandle) = 0x%X
    ", hDebugObject);
        if( hDebugObject != 0x0  )  printf("  => Debugging!!!
    
    ");
        else                        printf("  => Not debugging...
    
    ");
    

    第二个参数为ProcessDebugObjectHandle时,第三个参数返回为被调试对象句柄,当返回NULL时说明进程处于非调试状态

    ProcessDebugFlags(0x1F)

        // ProcessDebugFlags (0x1F)
        BOOL bDebugFlag = TRUE;
        pNtQueryInformationProcess(GetCurrentProcess(),
                                   ProcessDebugFlags,
                                   &bDebugFlag,
                                   sizeof(bDebugFlag),
                                   NULL);
        printf("NtQueryInformationProcess(ProcessDebugFlags) = 0x%X
    ", bDebugFlag);
        if( bDebugFlag == 0x0  )  printf("  => Debugging!!!
    
    ");
        else                      printf("  => Not debugging...
    
    ");
    

    ProcessDebugFlags参数用于获取调试标识,DebugFlag的值若为0则处于调试状态,若为1则处于非调试状态

    破解方法

    patch函数/修改函数返回值/API Hook

    OD中advanced olly提供了对该API Hook的功能

    0x03 NtQuerySytemInformation API

    检测系统是否以调试模式运行(WinDbg调试需要)

    之前进程隐藏的章节中使用过ZwQuerySytemInformation API来获取进程列表,用户层Zw和Nt没什么区别,所以这里使用Zw也是可以的

    NTSTATUS WINAPI NtQuerySystemInformation(
      _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
      _Inout_   PVOID                    SystemInformation,
      _In_      ULONG                    SystemInformationLength,
      _Out_opt_ PULONG                   ReturnLength
    );
    

    SystemInformationClass参数中指定需要的系统信息类型,将某结构体的地址传递给SystemInformation参数,API结束时,该结构体中就填充着相关的信息

    SystemKernelDebuggerInformation(0x23)

    NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)  
                                    GetProcAddress(GetModuleHandle(L"ntdll"), 
                                                   "NtQuerySystemInformation");
    
        ULONG SystemKernelDebuggerInformation = 0x23;
        ULONG ulReturnedLength = 0;
        SYSTEM_KERNEL_DEBUGGER_INFORMATION DebuggerInfo = {0,};
    
        NtQuerySystemInformation(SystemKernelDebuggerInformation, 
                                 (PVOID) &DebuggerInfo, 
                                 sizeof(DebuggerInfo),      // 2 bytes
                                 &ulReturnedLength);
    
        printf("NtQuerySystemInformation(SystemKernelDebuggerInformation) = 0x%X 0x%X
    ", 
               DebuggerInfo.DebuggerEnabled, DebuggerInfo.DebuggerNotPresent);
        if( DebuggerInfo.DebuggerEnabled )  printf("  => Debugging!!!
    
    ");
        else                                printf("  => Not debugging...
    
    ");
    

    DebuggerInfo.DebuggerEnabled==1时为调试状态

    破解方法:Win XP: 编辑boot.ini 删除/debugport=com1 /baudrate=115200 /Debug

    ​ Win7: cmd执行 bcdedit /debug off

    0x04 NtQueryObject API

    系统中某个调试器调试进程时,会创建一个调试对象类型的内核对象。检测该对象是否存在即可判断是否有进程正在被调试

    ntdll!NtQueryObject可获得系统各种内核对象信息

    NTSTATUS NtQueryObject(
      _In_opt_  HANDLE                   Handle,
      _In_      OBJECT_INFORMATION_CLASS ObjectInformationClass,
      _Out_opt_ PVOID                    ObjectInformation,
      _In_      ULONG                    ObjectInformationLength,
      _Out_opt_ PULONG                   ReturnLength
    );
    

    该API与上面讲过的API使用方法类似

    typedef enum _OBJECT_INFORMATION_CLASS { 
      ObjectBasicInformation,
      ObjectTypeInformation,
      ObjectNameInformation,
      ObjectAllInformation, //3
      ObjectHandleInformation
    } OBJECT_INFORMATION_CLASS;
    

    ObjectAllInformation(0x3)

    使用ObjectAllInformation可获得系统所有对象信息,然后从中检测是否存在调试对象

    0x05 ZwSetInformationThread API

    强制分离被调试者与调试者的技术,使用该API被调试者可将自身从调试器中分离出来(使调试进程终止运行,同时终止自身进程),该API不会对正常运行的程序(非调试运行)产生任何影响。

    NTSTATUS ZwSetInformationThread(
            HANDLE ThreadHandle,
            THREAD_INFORMATION_CLASS ThreadInformationClass,
            PVOID ThreadInformation,
            ULONG ThreadInformationLength
    );
    

    ThreadHideFromDebugger(0x11)

    API第二个参数设置为ThreadHideFromDebugger即可达到分离调试进程的目的

    void DetachDebugger()
    {
        typedef enum _THREAD_INFORMATION_CLASS {
            ThreadBasicInformation,
            ThreadTimes,
            ThreadPriority,
            ThreadBasePriority,
            ThreadAffinityMask,
            ThreadImpersonationToken,
            ThreadDescriptorTableEntry,
            ThreadEnableAlignmentFaultFixup,
            ThreadEventPair,
            ThreadQuerySetWin32StartAddress,
            ThreadZeroTlsCell,
            ThreadPerformanceCount,
            ThreadAmILastThread,
            ThreadIdealProcessor,
            ThreadPriorityBoost,
            ThreadSetTlsArrayAddress,
            ThreadIsIoPending,
            ThreadHideFromDebugger           // 17 (0x11)
        } THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;
    
        typedef NTSTATUS (WINAPI* ZWSETINFORMATIONTHREAD)(
            HANDLE ThreadHandle,
            THREAD_INFORMATION_CLASS ThreadInformationClass,
            PVOID ThreadInformation,
            ULONG ThreadInformationLength
        );
    
        ZWSETINFORMATIONTHREAD pZwSetInformationThread = NULL;
        pZwSetInformationThread = (ZWSETINFORMATIONTHREAD)
                                  GetProcAddress(GetModuleHandle(L"ntdll.dll"), 
                                                 "ZwSetInformationThread");
    
        pZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
    
        printf("ZwSetInformationThread() -> Debugger detached!!!
    
    ");
    }
    

    0x06 其他

    检测当前运行环境是否处于一个逆向分析专用环境/系统

    1. 检测OD窗口 => FindWindow()
    2. 检测OD进程 => CreateToolhelp32Snapshot()
    3. 检查计算机名是否为TEST, ANALYSIS => GetComputerName()
    4. 检测程序运行路径是否存在TEST, SAMPLE => GetCommandLine()
    5. 检测是否为虚拟机环境 => VMWareService.exe , VMWareTray.exe, VMWareUser.exe
  • 相关阅读:
    placeholder 兼容处理
    扩展 jq 对象方法
    gulp 命令
    v-show、v-if、v-for的使用
    Vue中防抖和节流 --来自官方文档
    Python字符窜取值
    怎么安装redis桌面版?
    Python常用的基本数据类型
    MySQL常用语法
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/dliv3/p/6504311.html
Copyright © 2011-2022 走看看