反调试
@TOC
核心原理的最后一章是反调试,闲的无聊,在宿舍又读了一遍反调试,做了个总结。
静态反调试
PEB
PEB被广泛运用于反调试手段
这里一共有四个标志
1 | 0x2 BeingDebugged |
首先说第一个BeingDebugged
这个标志对应的api就是我们最常见的IsDebuggerPresent,当其值为1的时候代表程序被调试,破解方法就是在内存中把1修改为0即可。
第二个Ldr标志,这个只适用于xp操作系统
PEB.Ldr指向PEB_LDR_DATA结构体,这个结构体实在堆内存创建的,而在调试的时候,未使用的堆内存会被填充为0xfeeefeee,只要我们把其修改为null,既可绕过反调试
第三个标志ProcessHeap,同样只使用于xp系统
ProcessHeap可以通过GetProcessHeap()这个api来获取
它指向HEAP结构体的指针
第三个flags成员和第四个ForceFlags成员被用作反调试
正常情况下flags的值为2 ForceFlags的值为0
第四个标志就是NtGlobaFlag
这个在调试的时候,他的值会被设置为0x70,而在附加调试器的时候他的值不会改变
把其值调会0,即可绕过反调试
NtQueryInformationProcess()
NtQueryInformationProcess()是与进程相关联的api,通过调用这个api可以获取与进程相关的调试信息,当为这个api的第二个参数设置特定值,并调用这个函数的时候,相关信息会返回到第三个参数上。
第二个参数是枚举类型
与反调试相关联的就是注释的这三个参数
ProcesDebugPort
进程调试的时候,系统会为其分配一个端口,调用此api就可以返回端口
当第二个参数设置为7时,非调式时第三个参数为0,调试的时候,其值为 0xffffffff
ProcessDebugObjectHandle
当第二个参数设置为0x1e时,第三个参数可以获取调试对象的句柄
非调试 句柄值为0 调试时句柄的值存在
ProcessDebuggFlags
当第二个参数设置为0x1f的时候,第三个参数为调试的标志
被调试为1 不被调试为0
NtQuerysystemInformation()
NtQuerysystemInformation() 基于调试环境检测的反调试技术,利用这个api可以判断当前操作系统是否在调试模式下
这个api的第一个参数是枚举类型,当向第一个参数穿入0x23时,第二个参数在调试的时候会被设置为1
NtQueryObject()
系统中某个调试器调试进程时,会创建1个调试对象类型的内核对象。检测该对象是否存在即可判断是否有进程在被调试
和之前的类型,第二个参数时枚举类型,传入特定值后,相关信息会被返回到第三个参数
为了防止反调试,只需保证第二个参数的值不是3而是0即可
ZwSetInformationThread()
调用这个api可以把被调试的程序从调试的程序中分离出来
只要第二个参数设置为0x11即可
破解方法就是把第二个参数的值修改为0
这个api的工作原理是把线程隐藏起来,从而使调试器接受不到信息,就无法进行调试
还有一个和此类似的api是DebugActiveProcessStop(),它的原理是分离调试器和被调试的进程
TLS回调函数
这个就比较常见了,TLS回调函数会优先于EP代码执行,可以在前面加一些反调试,或者smc等手段,就可以达到反调试的效果,曾经被这个题目折磨过一次,记忆尤新
ETC
这方法就很鸡贼了
动态反调试
异常
关于异常的东西,我会在下篇博客里好好学习一下,因为最近遇到太多异常的题目了
关于异常的反调试,就是说,在正常的程序执行中,遇到一个异常(比如说除0),如果程序是没有被调试的,那么他就会交给特定的异常处理代码,而程序如果是在被调试,那么就会交给调试器来处理。
这里有一个api是SetUnhandledExtionFilter()
当程序发生异常时,SEH没有处理或者没有SEH的时候,就会调用这个api来执行程序的最后一个异常处理器(TOP Level Exception Filter),就是我们平常遇见终止程序的那个框框
而当程序在调试的时候,可以通过这个api来修改最后的异常处理器,新的异常处理器,修改修改eip
就可以到达反调试的效果
Timing Check
时间检测,这个还是可以很好理解的,调试的时候两条指令直接消耗很长的时间,而正常运行的时候,就一下子跑过去了。
x86中有一个叫TSC的64位寄存器,可以用来保存时间,
RDTSC这条汇编指令可以把TSC寄存器的值读到EDX:EAX
INT 2d
这条指令有两个特性
1忽略下一个字节
2可以一直运行到断点处
这两条是我在书上总结出来的,当然我还在网上发现了不一样的说法
第 0x2d(45) 号中断处理函数是 KiDebugService,执行Int 2dh指令时,进程如果没有处于被调试状态,将会抛出异常,如果在被调试状态则能够正常执行。我们可以利用这一点检测调试器的存在。
如果有调试器,调试器就会接管这个 int 2dh 产生的异常从而不走我们设置的异常回调处理函数,当然如果调试器选择不接管这个异常我们是无法检测出调试器的
0xcc
我们调试的时候,经常会在一些api上下断点,那么只要检查api的首位是不是0xcc就可以判断是否在调试,当然这种判断不是特别严谨,破解方法的话就是不在api的首位下断点,或者不下0xcc断点。
同时,这里还有一种校验和的判断手法,就是把这一些代码的汇编加起来,看看有没有改变等手段
高级反调试
PE保护器
就是加壳
垃圾代码
一句话用5句话来说,就要耗死逆,ollvm就是这么干的
扰乱代码对齐
花指令
加密/解密
在壳中或者smc字解码里常见
api重定向
把程序的api保护起来,放在其他的地方,这样使我们不能够通过下断点来寻找api
Debug Blocker
双进程保护,这种保护手段在最近的题目中经常遇见,
就是自己的一部分代码作为调试器调试自己的另一部分代码 那么在运行程序的时候不能附加调试器了
然后在里面设置一些smc或者修改一些eip的手段,达到混淆代码的目的
当时记录的一些笔记
总结
反调试的题目反正我遇见的不是很多,但是学习一下还是很有必要的
继续解决异常处理的问题