静态反调试技术

@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\n", dwDebugPort);
    if( dwDebugPort != 0x0  )  printf("  => Debugging!!!\n\n");
    else                       printf("  => Not debugging...\n\n");

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\n", hDebugObject);
    if( hDebugObject != 0x0  )  printf("  => Debugging!!!\n\n");
    else                        printf("  => Not debugging...\n\n");

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

ProcessDebugFlags(0x1F)

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

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\n", 
           DebuggerInfo.DebuggerEnabled, DebuggerInfo.DebuggerNotPresent);
    if( DebuggerInfo.DebuggerEnabled )  printf("  => Debugging!!!\n\n");
    else                                printf("  => Not debugging...\n\n");

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!!!\n\n");
}

0x06 其他

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

  1. 检测OD窗口 => FindWindow()
  2. 检测OD进程 => CreateToolhelp32Snapshot()
  3. 检查计算机名是否为TEST, ANALYSIS => GetComputerName()
  4. 检测程序运行路径是否存在TEST, SAMPLE => GetCommandLine()
  5. 检测是否为虚拟机环境 => VMWareService.exe , VMWareTray.exe, VMWareUser.exe

转载于:https://www.cnblogs.com/dliv3/p/6504311.html

前言 上一次发布过的程序:【首发】检测文件的占用,具有学习和商业价值(By超级用户),可以使用,仿电脑管家 正文 对于怎么枚举文件句柄 ,上一帖子对此有介绍,核心代码大概如下:如果 (ZwQueryObject (handle, #ObjectTypeInformation, unicode, 0, size) ≠ #STATUS_INVALID_HANDLE )' 只要不是无效的,为什么,详细看下面的注释 ' 参数 ' Handle ' 对象的一个句柄来获取信息。 ' ObjectInformationClass ' 指定一个OBJECT_INFORMATION_CLASS返回值的类型决定了信息在ObjectInformation缓冲区。 ' ObjectInformation ' 一个指向caller-allocated缓冲接收请求的信息。 ' ObjectInformationLength ' 指定的大小,以字节为单位,ObjectInformation缓冲区。 ' ReturnLength ' 一个指向变量的指针,接收的大小,以字节为单位,请求的关键信息。如果NtQueryObject STATUS_SUCCESS返回,返回的变量包含的数据量。如果NtQueryObject返回STATUS_BUFFER_OVERFLOW或STATUS_BUFFER_TOO_SMALL,您可以使用变量的值来确定所需的缓冲区大小。 ' 返回值 ' NtQueryObject返回STATUS_SUCCESS或适当的错误状态。可能的错误状态码包括以下: ' 返回代码 描述 ' STATUS_ACCESS_DENIED ' 有足够的权限来执行该cha询。 ' STATUS_INVALID_HANDLE ' 提供对象句柄无效。 ' STATUS_INFO_LENGTH_MISMATCH ' 信息长度不足以容纳数据。 unicode = 取空白字节集 (size) ZwQueryObject (handle, #ObjectTypeInformation, unicode, size, 0)' 读取信息的unicode文本 RtlUnicodeStringToAnsiString (ansi, unicode, 真)' 编码转换 ' RtlUnicodeStringToAnsiString例程将给定Unicode字符串转换成一个ANSI字符串。 str = 指针到文本 (ansi.Buffer) ' RtlFreeAnsiString常规版本存储由RtlUnicodeStringToAnsiString分配。 ' 参数 ' AnsiString ' 指针ANSI字符串缓冲区由RtlUnicodeStringToAnsiString以前分配的。 RtlFreeAnsiString (ansi) str = “无法获取”' 无效的怎么获取…… 返回 (str) 这一次呢更新了一个RemoteCloseHandle ,大概的原理是什么呢? 同时也采用了一些比较骚的方法,这种方法的限制较多,但是对于32位进程就很有效果。 NtClose在MSDN的大概介绍 1. NtClose is a generic routine that operates on any type of object. 2. Closing an open object handle causes that handle to become invalid. The system also decrements the handle count for the object and checks whether the object can be deleted. The system does not actually delete the object until all of the object's handles are closed and no referenced pointers remain. 3. A driver must close every handle that it opens as soon as the handle is no longer required. Kernel handles, which are those that are opened by a system thread or by specifying the OBJ_KERNEL_HANDLE flag, can be closed only when the previous processor mo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值