逆向常见反调试合集
反调试前文写过,花指令通常干扰静态分析,而反调试与之相反,主要为了干扰动态调试。
1.反调试简介
反调试是一种用于阻碍程序动态调试的技术,首先大致说明一下反调试的工作原理。
在操作系统内部提供了一些API,用于调试器调试。当调试器调用这些API时系统就会在被调试的进程内存中留下与调试器相关的信息。一部分信息是可以被抹除的,也有一部分信息是难以抹除的。
当调试器附加到目标程序后,用户的很多行为将优先被调试器捕捉和处理。其中大部分是通过异常捕获通信的,包括断点的本质就是异常。如果调试器遇到不想处理的信息,一种方式是忽略,另一种方式是交给操作系统处理。
那么目前为止,程序就有两种方式检测自己是否被调试:
[*]检测内存中是否有调试器的信息。
[*]通过特定的指令或触发特定异常,检测返回结果。
通常来说,存在反调试的程序,当检测到自身处于调试状态时,就会控制程序绕过关键代码,防止关键代码被调试,或者干脆直接退出程序。
2.API反调试
Windows内部提供了一些用于检测调试器的API。
其中一个API是 IsDebuggerPresent,原型为:
BOOL IsDebuggerPresent(); 返回值为1表示当前进程被调试的状态,反之为0.
另一个常用的API是CheckRemoteDebuggerPresent,原型为:
BOOL CheckRemoteDebuggerPresent(HANDLE hProcess, PBOOL pbDebuggerPresent); 返回值为1表示当前进程被调试的状态,反之为0.
3.PEB反调试
当程序处于3环(低权限)时, FS: 寄存器指向TEB(Thread Environment Block),即线程环境块结构体,TEB向后偏移0x30字节的位置保存的是PEB(Process Environment Block ),即进程环境块的结构体地址。PEB中的部分成员是与调息相关的成员,当调试器通过 Windows提供的API调试目标程序时,Windows会将一部分调试信息写人这个结构体中。
kd>dt_TEB
nt! _TEB
...
+0x030 ProcessEnvironmentBlock :Ptr32_PEB
...
kd>dt_TEB
...
+0x002 BeingDebugged :UChar
...
+Ox018 ProcessHeap :Ptr32 Void
...
+0x068 NtGlobalF1ag :Uint4B
... 本处只介绍这两个结构体中几个重要的成员,若是想在实际调试时查看其他成员的具体内容,其中一种方法是使用WinDbg调试内核。
在PEB结构体中中,BeingDebugged、ProcessHeap、NtGlobalFlag是与调试信息相关的三个重要成员。
[*]BeingDebugged:当进程处于被调试状态时,值为1,否则为0。
[*]ProcessHeap:指向Heap结构体,偏移0xC处为Flags成员,偏移0x10处为ForceFlags成员。通常情况下,Flags的值为2.ForceFlags的值为0,当进程被调试时会发生改变
[*]NGlobalFlag:占四个字节,默认值为0。当进程处于被调试状态时,第一个字节会被置为0x70。
通过FS.Base能够定位到TEB,再通过TEB+0x30能够定位PEB。通过在内存中检测或修改相关成员的值,便可达到反试、反反调试的效果。
4.TLS反调试
TLS (Thread Local Storage),即线程局部存储是Windows提供的一种处理机制,每进行一次线程切换,便会调用一次TLS回调。它本意是想给每个线程都提供访问全局变量的机会。例如,需要统计当前程序进行了多少次线程切换,但并不想让其他线程访问到这个计数变量,使用TLS进行计数,便能够解决这个问题,一个程序能设置多个TLS.
由于进程在启动时至少需要创建一个线程来运行,因此在调用main函数前就会调用一次 TLS 回调。利用这个特点,在TLS回调中写入与反调试相关的代码,便可悄无声息地令调试器失效。
#include #include // TLS回调函数定义void NTAPI TLS_Callback(PVOID DllHandle, DWORD Reason, PVOID Reserved) { if (Reason == DLL_PROCESS_ATTACH) { // 检测是否存在调试器 if (IsDebuggerPresent()) { std::cout
页:
[1]