第十六章 反调试技术
反调试技术,恶意代码用它识别是否被调试,或者让调试器失效。恶意代码编写者意识
到分析人员经常使用调试器来观察恶意代码的操作,因此他们使用反调试技术尽可能地
延长恶意代码的分析时间。
1.探测Windows调试器
<1>使用Windows API 使用Windows API函数探测调试器是否存在是最简单的反调试技术。
IsDebuggerPresent 查询进程环境块中的IsDebugged标志
CheckRemoteDebuggerPresent 用来检测本地机器中的一个进程是否运行在调试器中。
NtQueryInfomationProcess 这个函数是Ntdll.dll中的一个原生态API,它用来提取一个给
定进程的信息。它的第一个参数是进程句柄,第二个参数告诉我们它需要提取进程信息
的类型,例如将该参数位置为ProcessDebugPort,将会告诉这个句柄标识的进程是否正
在被调试。
OutPutDebugString 这个函数的作用是在调试器中显示一个字符串。同时,它也可以用来
探测调试器的存在。如代码清单16-1所示,使用SetLastError函数,将当前的错误码设置
为一个任意值。如果进程没有被调试器附加,调用OutputDebugString函数,获取的错误
码不是我们所设置的。
<2>手动检测数据结构
[1].检测BeingDebugged属性,Windows操作系统维护每个正在运行的进程PEB结构,
恶意代码通过检查BeingDebugged标志,判断是否正在被调试
OllyDbg的一些插件可以帮助我们修改BeingDebugged标志。Hide Dbugger,Hidedebug
和phantom。
[2].检测ProcessHeap属性 这个属性位于PEB结构的0x18处。第一个堆头部有一个属性
字段,它告诉内核这个堆是否在调试器中被创建。这些属性叫做ForceFlags和Flags。
对付这种反调试技术最简单的方法就是手动修改ProcessHeap标志,或者使用调试器
隐藏调试插件。如果你使用的调试器是WinDbg,则以禁用调试堆栈来启动进程。例如
windbg-hd notepad.exe命令在与调试模式相反的正常模式下启动程序,在这个模式下,
上文讨论的flags标志不会被设置。
[3].检测NTGlobalFlag 由于调试器中启动进程与正常模式下启动进程有些不同,所以它
们创建内存堆的方式也不同。系统使用PEB结构偏移量0X68处的一个未公开位置,来
决定如何创建堆结构。如果这个位置的值是0x70,我们就知道进程运行在调试器中。
<3>系统痕迹检测 可以通过查看注册表表项,判断是否被调试。
HKLM\SOFEWARE\Microsoft\Windows NT\CurrentVersion\AeDebug
2.识别调试器的行为。 在逆向工程中,为了帮助恶意代码分析人员进行分析,可以使用调试器
设置一个断点,或是单步执行一个进程。然而,在调试器中执行这些操作时,它们会修改进程
中的代码。
<1>INT扫描 调试器断点的基本机制是用软件中断指令INT 3,临时替换运行程序中的一条指令
,然后当程序运行到这条指令时,调用调试异常处理例程。INT 3指令的机器码是0XCC,因
此无论何时,使用调试器设置一个断点,都会插入一个0XCC来修改代码。
恶意代码常用的一种反调试技术是在它的代码中查找机器码0XCC,来扫描调试器对它代码
的INT 3修改。
<2>执行代码校验和检查 恶意代码可以计算代码段的校验并实现与扫描中断相同的目的。与
扫描0XCC不同,这种检查执行恶意代码机器码CRC(循环冗余校验)或者MD5校验和检查。
<3>时钟检测 被调试时,进程的运行速度大大降低。
- 记录执行一段操作前后的时间戳,比较俩个时间戳,如果存在滞后,则可以认为存在调试器。
- 记录触发一个异常前后的时间戳。如果不调试进程,可以很快地处理完异常,因为调试器处
理异常的速度非常慢。默认情况下,调试器处理异常需要人为干预。
- 使用 rdstc 时钟检测方法。 操作码0X0F31
- 使用 queryPerformanceCounter和GetTickCount。 这俩个Windows API函数被用来执行一
个反调试的时钟检测。调用俩次查询计数器。判断调用时间差。
3.干扰调试器功能。 除了上文介绍的反调试方法外,恶意代码还可以用一些技术来干扰调试器
的正常运行。例如,线程本地存储(TLS)回调,异常,插入中断等。这些技术当且仅当程序处于
调试器控制之下时才试图扰乱程序的运行。
使用TLS回调。
使用异常 调试器利用中断产生异常,来执行类似于断点的操作。恶意代码可以使用异常来破坏
或者探测调试器。调试器捕获异常后,并不会立即将处理权返回调试进程处理,大多数利用异
常的反调试技术和往往据此来探测调试器。多数调试器默认的设置是捕获异常后不将异常传递
给应用程序。如果调试器不能将异常结果正确返回到调试进程,那么这种异常失效可以被进程
内部的异常处理机制探测。
插入中断 在合法指令序列中插入中断,破坏程序的正常运行。
插入INT 3。双字节操作码0XCD03也可以产生INT 3中断,这是恶意代码干扰WinDbg调试器的
有效方法。在调试器外,0XCD03指令产生一个STATUS_BREAKPOINT异常。然而,在
WINDBG调试器内,由于断点通常是单字节机器码0XCC,因此WinDbg会捕获这个断点,然
后将EIP加一字节。这可能导致程序在被正常运行的WINdbg调试时,执行不同的指令集。
插入INT 2D断点
插入ICE断点 片内仿真器(ICE)断点指令icebp(操作码0XF1)是Intel未公开的指令之一。由于
使用ICE难以在任意位置设置断点,因此icebp指令被设计用来降低使用ICE设置断点的难度。
4.调试器漏洞。 与所有软件一样,调试器也存在漏洞,有时恶意代码编写者为了防止被调试,或攻击这些漏洞。
实验
Lab16-1
使用一款调试器分析Lab16-01.exe中的恶意代码。Lab16-01.exe是与Lab09-01.exe相同的恶意代码,不同点在于Lab16-01.exe加入了反调试技术。
问题
1.这个恶意代码使用了那些反调试技术?
检测isDebugged,ProcessHeap,
2.当每种反调试技术成功执行时,有什么现象?
调用函数删除自身
3.如何应对这些反调试技术?
使用插件隐藏这些标志位。
4.如何在调试过程中手动修改检测的数据结构?
isDugged在OD的命令行输入“ dump fs:[30]+0x02,然后去修改内容
ProcessHeap在命令行输入 "dump ds:[fs:[30]+0x18]+0x10,然后去修改内容
5.哪一种OllyDbg插件可以帮你逃避恶意代码的反调试技术?
phantom隐藏插件
Lab16-2
使用调试器分析恶意代码Lab16-02.exe。本实验的目的是找出正确的密码。另外,恶意代码放弃使用攻击负载(payload)
问题
1.命令行中运行Lab16-02.exe,会发生什么?
2.当使用猜测的命令行参数运行Lab16-02.exe,会发生什么?
猜测答案错误。
3.命令行密码是什么?
4.使用IDA Pro加载Lab16-02.exe。在main函数的何处可以找到strncmp函数?
5.在默认设置下,将这个恶意代码加载到OllyDbg中会发生什么?
直接退出
6.Lab16-02.exe中PE结构的独特之处是什么?
使用了在资源段有TLS段,回调。
7.回调(callback)发生在那些位置?(提示:IDA PRO使用ctrl+e组合键)
8.恶意代码使用哪一种反调试技术使它在调试器中立即终止运行?如何避免这种检查?
由于调试器的功能比较强大,所以直接显示了正确结果。隐藏了ollydbg的存在。
9.当你禁用反调试技术后,你在调试器中看到的命令行密码是什么?
10.调试器中找到的密码在命令行中运行有效吗?
如果没有隐藏在调试器是无效的,因为会动态修改密码。
11.那种反调试技术为调试器和命令行设置不同的密码?如何防御它们?
判断是否在调试器中打开,并且改变密码。
Lab16-3
使用调试器分析Lab16-03.exe中的恶意代码。这个恶意代码在Lab09-02.exe中相似,不同之处是做了一些修改,引入了反调试技术。
问题
1.当使用静态分析法分析这个二进制文件时,你看到了那些字符串?
2.当运行这个二进制文件时会发生什么?
运行之后没有任何活动。
3.如何重命名它,才能使这个二进制文件正常运行?
比较之后发现需要重命名为peo.exe。 如果调试器没有隐藏会出现不同的字符。
4.这个恶意代码使用了那些反调试技术?
-
第一次调用是用来动态修改判断的文件名。
-
第二次调用
-
第三次调用
5.对每一种反调试技术而言,如果恶意代码确定它运行在调试器中,它将做什么?
第一次失败会修改判断的文件名,来阻碍分析。
第二次失败会引发内存访问错误,中断运行
第三次失败会调用命令行删除自身。
6.为什么反调试技术在这个恶意代码中能够成功?
使用除0异常,因为在调试器中处理这个异常会比正常运行要慢,所以通过比较时间来判断是否在调试器中运行。
7.恶意代码使用了什么域名?