反调试技术
在开始前,先用一张图简要说明反调试技术的种类
静态反调试
承接之前学习的PEB结构体,该结构体的成员主要有以下四个更值得关注,且都与反调试有关。
首先复习下如何访问PEB结构体
利用上面的方式再加上上面四个成员的偏移量就可以访问对应的成员了。
BeingDebugged
该成员在值为1是表示进程处在调试状态,为0则正常运行。
Ldr
在进程处于调试状态的堆区中,未使用的内存区域全部由0xFEEEFEEE填充,若将其用NULL覆盖即表明程序处于正常运行状态。
Process Heap
该成员是指向Heap结构体的指针
其中,在正常运行时,Flags和Force Flags成员会被设置为特定的值,分别为0x2,0x0
NtGlobalFlag
调试状态时该成员的值会被设置为0x70,正常运行状态则值为0.
NtQueryInformationProcess
此处的例子出于将会多次涉及参数数值的更改,会很麻烦,所以涉及api hook,
NtQureyObject
首先,当进程处于调试状态时,系统会分配一个调试对象类型的内核对象,通过检测该对象是否存在,即可判断是否处于调试状态。
简要说明检测过程:
通过NtQureyObject()api获取系统各种内核对象的信息。定义如下
通过书中的调试案例可更快理解该反调试的具体过程
ZwSetInformationThread
该api会将线程隐藏,调试器无法接受信息,从而无法调试。
可以让该api不调用,可以解决问题,或者在调用该api之前,将存储在栈中的的第二个参数ThreadInformationClass的值设置为0即可。
and so on
再者就是之前介绍过的TLS回调函数实现反调试了,很经典。
书中最后还介绍了一种通过检测程序运行所处的系统是否为你想专用的系统来决定是否实现反调试。
动态反调试技术
异常是常被用来实现动态反调试的手段之一。本章前面所描述的部分内容可在SEH那章找到。
而当进程中未注册SEH或SEH未处理异常时
Timing Check
原理为当程序在正常运行时和调试时所耗费的时间不同来检测进程是否处于调试状态。
如下是检测方法
RDTSC反调试的破解方式如图
TF与单步
前面的章节提到,Windows中存在单步异常。
在单步执行模式中,该异常触发后,TF会自动清零
需要注意TF的值无法修改
大体流程如下
调试例子可以通过将-1修改为0x401036的值也可绕过反调试。
INT 2D
该异常比较有意思,在调试状态时,解析了该指令后,系统会忽略下一条指令的第一个字节,导致之后指令解析错误。
跟着例子调一下就明白了。
0xCC探测
断点对应的x86指令为0xCC
如果只是扫面0xCC就判断在调试状态,那大概率会“误伤”
故一般会检测一些API函数的第一个字节是否0xCC,所以尽量别在API函数开头设置软件断点,往后一点,或者设置硬件断点即可规避该反调试手段。
比较校验和
程序中如若设置了断电会导致校验和的值不相同,具体可结合书中实例查看
我们只需不在会被计算校验和的代码区设置断点即可规避
高级逆向分析技术
- 垃圾代码(花指令)
- 扰乱代码对齐(向代码插入经过精巧设计的不必要的代码来降低反汇编代码可读性),该方法会使调试器生成错误的反汇编代码,具体请阅读书籍。
- 对关键数据和代码加解密(此中可能会参杂花指令使可读性降低)
- Stolen Bytes(在转储进程内存时将EP代码的部分剪切到其他地方,从而使转储的文件无法正常运行反转储技术,被剪切的代码最后会添加jmp指令跳转到还存在的EP代码继续执行)
- API重定向;这跟上面那个差不多,API重定向是把所有EP代码都复制到其他区域,再将要保护的代码修改,从而使被复制的代码得以执行(该技术支持反转储)
最后的
Last
这几章的内容都很注重实操,不应该停留在阅读理解。