静态反调试技术

静态反调试技术

PEB

BeingDebugged

IsDebuggerPresent函数根据PEB中的BeingDebugged来判断进程是否被调试。实例代码如下:

cpp 复制代码
#include "Windows.h"

int main(int argc, char const *argv[])
{
	if (IsDebuggerPresent())
	{
		MessageBoxW(NULL, L"DebuggerPresent!", L"INFO", MB_OK);
	}
	else
	{
		MessageBoxW(NULL, L"Good Job!", L"INFO", MB_OK);
	}

	return 0;
}

当有调试器时:

txt 复制代码
0:000> dt _PEB 0000005c`33d6d000
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 '' // 该值为1,TRUE

破解方法:

修改IsDebuggerPresent的返回值或者修改BeingDebugged字段为0即可:

txt 复制代码
0:000> r $peb // 获取PEB的地址
$peb=0000005c33d6d000

0:000> db 0000005c33d6d000 // 查看PEB内存块
0000005c`33d6d000  00 00 01 04 00 00 00 00-ff ff ff ff ff ff ff ff  ................
0000005c`33d6d010  00 00 96 00 f7 7f 00 00-20 29 91 d4 fb 7f 00 00  ........ )......
0000005c`33d6d020  b0 59 15 cb d6 01 00 00-00 00 00 00 00 00 00 00  .Y..............
0000005c`33d6d030  00 00 15 cb d6 01 00 00-80 27 91 d4 fb 7f 00 00  .........'......
0000005c`33d6d040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000005c`33d6d050  03 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000005c`33d6d060  00 00 00 00 00 00 00 00-00 00 fb ca d6 01 00 00  ................
0000005c`33d6d070  00 00 00 00 00 00 00 00-20 e2 90 d4 fb 7f 00 00  ........ .......

0:000> ? 0000005c33d6d000+0x2 // 定位到BeingDebugged
Evaluate expression: 396006707202 = 0000005c`33d6d002

0:000> db 0000005c`33d6d002
0000005c`33d6d002  01 04 00 00 00 00 ff ff-ff ff ff ff ff ff 00 00  ................
0000005c`33d6d012  96 00 f7 7f 00 00 20 29-91 d4 fb 7f 00 00 b0 59  ...... ).......Y
0000005c`33d6d022  15 cb d6 01 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000005c`33d6d032  15 cb d6 01 00 00 80 27-91 d4 fb 7f 00 00 00 00  .......'........
0000005c`33d6d042  00 00 00 00 00 00 00 00-00 00 00 00 00 00 03 00  ................
0000005c`33d6d052  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000005c`33d6d062  00 00 00 00 00 00 00 00-fb ca d6 01 00 00 00 00  ................
0000005c`33d6d072  00 00 00 00 00 00 20 e2-90 d4 fb 7f 00 00 01 00  ...... .........

0:000> eb 0000005c`33d6d002 0 // 修改为0

0:000> db 0000005c`33d6d002 // 确定修改
0000005c`33d6d002  00 04 00 00 00 00 ff ff-ff ff ff ff ff ff 00 00  ................
0000005c`33d6d012  96 00 f7 7f 00 00 20 29-91 d4 fb 7f 00 00 b0 59  ...... ).......Y
0000005c`33d6d022  15 cb d6 01 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000005c`33d6d032  15 cb d6 01 00 00 80 27-91 d4 fb 7f 00 00 00 00  .......'........
0000005c`33d6d042  00 00 00 00 00 00 00 00-00 00 00 00 00 00 03 00  ................
0000005c`33d6d052  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000005c`33d6d062  00 00 00 00 00 00 00 00-fb ca d6 01 00 00 00 00  ................
0000005c`33d6d072  00 00 00 00 00 00 20 e2-90 d4 fb 7f 00 00 01 00  ...... .........

Ldr

《逆向工程核心原理》中写道:

调试进程时,其堆内存中会出现一些特殊标识,表示它正处于被调试状态。其中最醒目的是未使用的堆内存中充斥着0xFEEEFEEE。Ldr指向一个结构体,该结构就是在堆中创建的,扫描该区域并检查其中是否存在0xFEEEFEEE,就可以检测进程是否被调试。

这个方法仅在Windows XP中有效,在更高版本的Windows系统中,这个值有其它含义。

0xFEEEFEEE(俗称"Fee Fee"或"No Man's Land"标记)确实是 Windows 堆管理器使用的特殊填充值,但它与进程是否被调试器附加没有必然的因果关系。它的出现是为了帮助检测堆内存错误,而不是作为调试器存在的标志。

这个魔数主要用于堆内存的"未分配"或"已释放"区域,目的是在调试阶段暴露程序逻辑错误。

场景 填充值 含义
未分配堆块 0xFEEEFEEE 堆管理器用此值填充未分配的内存,标记为"未初始化"。
已释放堆块 0xFEEEFEEE 释放后的内存通常被填充此值,标记为"已回收"。
堆块间隙 0xFDFDFDFD 用于堆块之间的"No Man's Land"保护带,防止越界。

触发机制 :如果你在代码中读取到 0xFEEEFEEE,通常意味着你正在访问一块未初始化的堆内存已释放的指针(Dangling Pointer)。这是堆管理器给你的"错误提示",而不是调试器留下的"足迹"。


NtGlobalFlag

当进程被调试时,该字段为:

txt 复制代码
   +0x0bc NtGlobalFlag     : 0x70
cpp 复制代码
#include "Windows.h"
#include "winternl.h"

#define OFFSET_NTGLOBALFLAG 0xbc
#define DEBUGGED_FLAG 0x70

int main(int argc, char const *argv[])
{
	PTEB pTEB = NtCurrentTeb();
	PPEB pPEB = pTEB->ProcessEnvironmentBlock;
	PBYTE pNtGlobalFlag = reinterpret_cast<PBYTE>(pPEB) + OFFSET_NTGLOBALFLAG;
	DWORD dwNtGlobalFlag = *(reinterpret_cast<DWORD *>(pNtGlobalFlag));

	if (dwNtGlobalFlag == DEBUGGED_FLAG)
	{
		MessageBoxW(NULL, L"Debugged!!!", L"INFO", MB_OK);
	}
	else
	{
		MessageBoxW(NULL, L"Good Job!!!", L"INFO", MB_OK);
	}

	return 0;
}

修改该字段为0即可过反调试。

txt 复制代码
0:000> .process
Implicit process is now 0000000c`efa09000

0:000> r $peb
$peb=0000000cefa09000

0:000> dt _PEB 0000000c`efa09000 NtGlobalFlag
ntdll!_PEB
   +0x0bc NtGlobalFlag : 0x70

0:000> ? 0000000c`efa09000+0x0bc
Evaluate expression: 55559884988 = 0000000c`efa090bc

0:000> db 0000000c`efa090bc
0000000c`efa090bc  70 00 00 00 00 80 9b 07-6d e8 ff ff 00 00 10 00  p.......m.......
0000000c`efa090cc  00 00 00 00 00 20 00 00-00 00 00 00 00 00 01 00  ..... ..........
0000000c`efa090dc  00 00 00 00 00 10 00 00-00 00 00 00 01 00 00 00  ................
0000000c`efa090ec  10 00 00 00 40 3f 0b a3-f8 7f 00 00 00 00 00 00  ....@?..........
0000000c`efa090fc  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000000c`efa0910c  00 00 00 00 98 c8 0a a3-f8 7f 00 00 0a 00 00 00  ................
0000000c`efa0911c  00 00 00 00 58 66 00 00-02 00 00 00 03 00 00 00  ....Xf..........
0000000c`efa0912c  06 00 00 00 00 00 00 00-00 00 00 00 ff 00 00 00  ................

0:000> ed 0000000c`efa090bc 0

0:000> db 0000000c`efa090bc
0000000c`efa090bc  00 00 00 00 00 80 9b 07-6d e8 ff ff 00 00 10 00  ........m.......
0000000c`efa090cc  00 00 00 00 00 20 00 00-00 00 00 00 00 00 01 00  ..... ..........
0000000c`efa090dc  00 00 00 00 00 10 00 00-00 00 00 00 01 00 00 00  ................
0000000c`efa090ec  10 00 00 00 40 3f 0b a3-f8 7f 00 00 00 00 00 00  ....@?..........
0000000c`efa090fc  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000000c`efa0910c  00 00 00 00 98 c8 0a a3-f8 7f 00 00 0a 00 00 00  ................
0000000c`efa0911c  00 00 00 00 58 66 00 00-02 00 00 00 03 00 00 00  ....Xf..........
0000000c`efa0912c  06 00 00 00 00 00 00 00-00 00 00 00 ff 00 00 00  ................

NtQueryInformationProcess

使用该函数可以获取各种与进程相关的调试信息。第二个参数是需要获取的信息,并在第三个参数返回。

cpp 复制代码
typedef enum _PROCESS_INFORMATION_CLASS {
  ProcessMemoryPriority,
  ProcessMemoryExhaustionInfo,
  ProcessAppMemoryInfo,
  ProcessInPrivateInfo,
  ProcessPowerThrottling,
  ProcessReservedValue1,
  ProcessTelemetryCoverageInfo,
  ProcessProtectionLevelInfo,
  ProcessLeapSecondInfo,
  ProcessMachineTypeInfo,
  ProcessOverrideSubsequentPrefetchParameter,
  ProcessMaxOverridePrefetchParameter,
  ProcessInformationClassMax
} PROCESS_INFORMATION_CLASS;

ProcessDebugPort

CheckRemoteDebuggerPresent函数在内部调用了NtQueryInformationProcess函数,可以用来检测进程是否被调试。

ProcessDebugObject-Handle

ProcessDebugFlags


对于使用NtQueryInformationProcess函数反调试的函数,有多种方法可以绕过:

  • 修改函数的返回值
  • Hook该函数

NtQuerySystemInformation

使用该函数可以检测系统是否在调试模式下运行。

NtQueryObject

系统中某个调试器在调试进程时,会创建一个调试对象类型的内核对象。通过查询这个对象是否存在即可判断是否有进程被调试。

cpp 复制代码
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
  [in, optional]  HANDLE                   Handle,
  [in]            OBJECT_INFORMATION_CLASS ObjectInformationClass,
  [out, optional] PVOID                    ObjectInformation,
  [in]            ULONG                    ObjectInformationLength,
  [out, optional] PULONG                   ReturnLength
);

第二个参数为需要查询的类型,通过第三个参数返回:

cpp 复制代码
typedef enum _OBJECT_INFORMATION_CLASS {
  ObjectBasicInformation = 0,
  ObjectTypeInformation = 2
} OBJECT_INFORMATION_CLASS;

绕过方法同上。

ZwSetInformationThread

使用该函数可以将调试者与被调试者强行分离开来:

cpp 复制代码
NTSYSAPI NTSTATUS ZwSetInformationThread(
  [in] HANDLE          ThreadHandle,
  [in] THREADINFOCLASS ThreadInformationClass,
  [in] PVOID           ThreadInformation,
  [in] ULONG           ThreadInformationLength
);

TLS回调函数

TLS回调函数会先于EP执行,所以可以在TLS回调函数中执行IsDebuggerPresent来判断进程是否被调试。

ETC

除了检测进程本身是否被调试,还可以检测调试器是否存在,比如可以使用FindWindow函数来检测OD或者windbg是否运行,使用快照检测调试器是否运行等等。

相关推荐
胡耀超6 天前
Web Crawling 网络爬虫全景:技术体系、反爬对抗与全链路成本分析
前端·爬虫·python·网络爬虫·数据采集·逆向工程·反爬虫
阿昭L7 天前
CodePatch hook api
hook·逆向工程
阿昭L9 天前
《逆向工程核心原理》使用调试函数hook api的实验在64位Windows11上的复现
hook·逆向工程
阿昭L14 天前
PE文件之资源表
pe结构·逆向工程
Pure_White_Sword1 个月前
bugku-reverse题目-NoString
网络安全·ctf·reverse·逆向工程
阿昭L1 个月前
PE文件之导入表(一):导入函数调用机制、导入表基本结构
逆向工程·pe文件
Pure_White_Sword2 个月前
bugku-reverse题目-游戏过关
游戏·网络安全·ctf·reverse·逆向工程
阿昭L2 个月前
C++异常处理机制反汇编(三):32位下的异常结构分析
c++·windows·逆向工程
明洞日记2 个月前
【软考每日一练030】软件维护:逆向工程与再工程的区别与联系
c++·软件工程·软考·逆向工程