第51章:静态反调试技术——API查询
NtQueryInformationProcess( )
将第二个参数设置为不同的特定值并调用该函数,就能将不同的数据写入到第三个参数。
该枚举类型与调试器探测有关的成员有 ProcessDebugPort ( 0x7 ) ,ProcessDebugObjectHandle ( 0x1E ),ProcessDebugFlags ( 0x1F )
ProcessDebugPort ( 0x7 )
当进程处于调试状态时,系统就会给它分配一个调试端口(Debug Port),当 ProcessInformationClass 被设置为 0x7 时,调用 NtQueryInformationProcess( ) 就能获取调试端口。若进程处于非调试状态,则变量 dwDebugPort 的值设置为0,否则为 FFFFFFFF
CheckRemoteDebuggerPresent()API 与 IsDebuggerPresent()类似,但是它还可以用来检测其它进程是否处于被调试状态。其内部代码:
ProcessDebugObjectHandle ( 0x1E )
调试进程时会生成调试对象(Debug Object),进程处于调试状态时,调试对象的句柄就存在,否则为 NULL
ProcessDebugFlags ( 0x1F )
若第三个参数返回值为0,则处于被调试状态;为1,则处于非调试状态。
调试-钩取API
改变函数运行流程到自己的代码中,并修改函数返回值:
NtQuerySystemInformation()
此 API 是用于基于调试环境检测的反调试技术。注意:此方法仅仅只显示当前系统是否处于调试模式!
第一个参数用于指定需要的系统信息类型,API 将返回的信息放在第二个参数。
向 SystemInformationClass 参数传入 SystemKernelDebuggerInformation( 0x23 ) ,即可判断处 OS 是否处于调试模式下运行.
破解之法:在 XP 系统中编辑 boot.ini 文件,删除 值,在 Win 7 中只能关闭调试模式。
NtQueryObject
同样的,第二个参数是枚举类型,第三个参数返回要查询的信息。调用该 API 有点麻烦:
然后还需要一个循环找到 “DebugObject”,并对当中的某个元素进行查验。应对方法是,钩取该 API ,将其第二个参数的值设为 0 .
ZwSetInformationThread()
利用此 API 可以将被调试者从调试器中分离出来。
第一个参数为当前线程的句柄,第二个参数 ThreadInformationClass 表示线程信息类型,若其值设置为 ThreadHideFromDebugger(0x11) ,调用后即可分离,并终止它们的运行,若无调试器,则不影响。
应对方法:在函数调用函数前将第二个参数设置为 0 即可,或者钩取 API 改。
TLS回调函数
前面有
ETC
FindWindow & GetWindowText
探测是否存在指定名称(OllyDbg、IDA Pro、WinDbg)的调试窗口。
主要的思路是:先利用 GetDesktopWindow() 返回桌面窗口句柄,再通过 GetWindow() 返回其子窗口句柄及子窗口的兄弟窗口句柄,然后利用 GetWindwosText() 返回窗口的标题,并与程序内的字符串进行对比。
应对方法:直接在 GetDesktopWindow() 调用后将其返回值(EAX)置 0,即可跳过。