|
<div><font size="2"> </font></div><div><font size="2"> </font></div><div><font size="2"> 本文中,我们将向读者介绍恶意软件用以阻碍对其进行逆向工程的各种反调试技术,以帮助读者很好的理解这些技术,从而能够更有效地对恶意软件进行动态检测和分析。</font></div><p><font size="2"> <strong>一、反调试技术</strong></font></p><p><font size="2"> 反调试技术是一种常见的反检测技术,因为恶意软件总是企图监视自己的代码以检测是否自己正在被调试。为做到这一点,恶意软件可以检查自己代码是否被设置了断点,或者直接通过系统调用来检测调试器。</font></p><p><font size="2"> <strong> 1.断点</strong></font></p><p><font size="2"> 为了检测其代码是否被设置断点,恶意软件可以查找指令操作码0xcc(调试器会使用该指令在断点处取得恶意软件的控制权),它会引起一个SIGTRAP。如果恶意软件代码本身建立了一个单独的处理程序的话,恶意软件也可以设置伪断点。用这种方法恶意软件可以在被设置断点的情况下继续执行其指令。</font></p><p><font size="2"> 恶意软件也可以设法覆盖断点,例如有的病毒采用了反向解密循环来覆盖病毒中的断点。相反,还有的病毒则使用汉明码自我纠正自身的代码。汉明码使得程序可以检测并修改错误,但是在这里却使病毒能够检测并清除在它的代码中的断点。</font></p><p><font size="2"> <strong> 2.计算校验和</strong></font></p><p><font size="2"> 恶意软件也可以计算自身的校验和,如果校验和发生变化,那么病毒会假定它正在被调试,并且其代码内部已被放置断点。VAMPiRE是一款抗反调试工具,可用来逃避断点的检测。VaMPiRE通过在内存中维护一张断点表来达到目的,该表记录已被设置的所有断点。该程序由一个页故障处理程序(PFH),一个通用保护故障处理程序(GPFH),一个单步处理程序和一个框架API组成。当一个断点被触发的时候,控制权要么传给PFH(处理设置在代码、数据或者内存映射I/O中的断点),要么传给GPFH(处理遗留的I/O断点)。单步处理程序用于存放断点,使断点可以多次使用。</font></p><p><font size="2"> <strong>3.检测调试器</strong></font></p><p><font size="2"> 在Linux系统上检测调试器有一个简单的方法,只要调用Ptrace即可,因为对于一个特定的进程而言无法连续地调用Ptrace两次以上。在Windows中,如果程序目前处于被调试状态的话,系统调用isDebuggerPresent将返回1,否则返回0。这个系统调用简单检查一个标志位,当调试器正在运行时该标志位被置1。直接通过进程环境块的第二个字节就可以完成这项检查,以下代码为大家展示的就是这种技术:</font></p><p><font size="2"> mov eax, fs:[30h]</font></p><p><font size="2"> move eax, byte [eax+2]</font></p><p><font size="2"> test eax, eax</font></p><p><font size="2"> jne @DdebuggerDetected</font></p><p><font size="2"> 在上面的代码中,eax被设置为PEB(进程环境块),然后访问PEB的第二个字节,并将该字节的内容移入eax。通过查看eax是否为零,即可完成这项检测。如果为零,则不存在调试器;否则,说明存在一个调试器。</font></p><p><font size="2"> 如果某个进程为提前运行的调试器所创建的,那么系统就会给ntdll.dll中的堆操作例程设置某些标志,这些标志分别是FLG_HEAP_ENABLE_TAIL_CHECK、FLG_HEAP_ENABLE_FREE_CHECK和FLG_HEAP_VALIDATE_PARAMETERS。我们可以通过下列代码来检查这些标志:</font></p><p><font size="2"> mov eax, fs:[30h]</font></p><p><font size="2"> mov eax, [eax+68h]</font></p><p><font size="2"> and eax, 0x70</font></p><p><font size="2"> test eax, eax</font></p><p><font size="2"> jne @DebuggerDetected</font></p><p><font size="2"> 在上面的代码中,我们还是访问PEB,然后通过将PEB的地址加上偏移量68h到达堆操作例程所使用的这些标志的起始位置,通过检查这些标志就能知道是否存在调试器。</font></p><p><font size="2"> 检查堆头部内诸如ForceFlags之类的标志也能检测是否有调试器在运行,如下所示:</font></p><p><font size="2"> mov eax, fs:[30h]</font></p><p><font size="2"> mov eax, [eax+18h] ;process heap</font></p><p><font size="2"> mov eax, [eax+10h] ;heap flags</font></p><p><font size="2"> test eax, eax</font></p><p><font size="2"> jne @DebuggerDetected</font></p><p><font size="2"> 上面的代码向我们展示了如何通过PEB的偏移量来访问进程的堆及堆标志,通过检查这些内容,我们就能知道Force标志是否已经被当前运行的调试器提前设置为1了 |
|