安全中国首页 > 文章中心 > 基础知识
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:download.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

2.1.5 断点(1)

更新时间:2008-8-21 0:18:16
责任编辑:流火
热 点:

2.1.5  断点

常用的断点有INT 3断点、硬件断点、内存断点等。调试时,合理使用断点,能大大提高效率。

1.INT 3断点

当执行一个INT 3断点时,该地址处的内容被调试器用INT 3指令替换了,此时OllyDbg将INT 3隐藏了,显示出来仍是下断前的指令,如图2.12按F2键下的断点。实际上,4013A5处的指令68已被替换成CC了:

  004013A5       CC D0404000 

这个INT 3指令,其机器码是CCh,也常称为CC指令。当被调试进程执行INT 3指令导致一个异常时,调试器就会捕捉这个异常从而停在断点处,然后将断点处的指令恢复成原来指令。当然,如果自己写调试器,也可用其他一些指令代替INT 3来触发异常。

用INT 3断点的好处是可以设置无数个断点,缺点是改变了原程序指令,容易被软件检测到。例如为了防范API被下断,一些软件会检测API的首地址是否为CCh,以此来判断是否被下了断点。在这用C语言来实现这个检测,方法是取得检测函数的地址,然后读取它的第一个字节,判断它是否等于“CCh”。下面这段代码就是对MessageBoxA函数进行的断点检测:

 FARPROC Uaddr ;
BYTE Mark = 0;
(FARPROC&) Uaddr =GetProcAddress ( LoadLibrary("user32.dll"), "MessageBoxA");
Mark = *((BYTE*)Uaddr);  // 取MessageBoxA函数第一字节
if(Mark ==0xCC)           // 如该字节为CC,则认为MessageBoxA函数被下断
return TRUE              // 发现断点

程序编译后,对MessageBoxA设断,程序将会发现自己被设断跟踪。当然躲过检测的方法是将断点下在函数内部或末尾,例如可以将断点下在函数入口的下一行,就可躲过检测了。

2.硬件断点

硬件断点和DRx调试寄存器有关。从Intel CPU体系架构手册中,可以找到DRx调试寄存器的介绍,如图2.25所示。

 
图2.25  Intel调试寄存器示意图

DRx调试寄存器总共有8个,从DR0到DR7。每个寄存器的特性如下:
 
DR0~DR3:调试地址寄存器,保存需要监视的地址,如设置硬件断点;
 
DR4~DR5:保留,未公开具体作用;
 
DR6:调试寄存器组状态寄存器;
 
DR7:调试寄存器组控制寄存器。

硬件断点原理是使用4个调试寄存器(DR0,DR1,DR2,DR3)来设定地址,以及DR7设定状态,因此最多只能设置4个断点。

OllyDbg支持调试寄存器,其称为硬件断点。设断方法是在指定的代码行单击鼠标右键,执行“Breakpoint/Hardware,on execution(断点/硬件执行)”命令。

为了便于理解,这里演示一下。加载实例TraceMe.exe,右键单击寄存器面板窗口,执行“View debug registers(查看调试寄存器)”,接着在4013AA这行设置硬件断点。按F9键执行程序,程序就会中断在4013AA这一行,查看调试寄存器,会发现DR0的值为4013AA,如图2.26所示。

 
(点击查看大图)图2.26  演示硬件断点

设置断点后,OllyDbg实际上就是将DR0~DR3其中的一个设置为44013AA,然后在DR7中设定相应的控制位。这样当被调试进程运行到4013AA时,CPU就会给OllyDbg发送异常信息,OllyDbg将该信息做初步处理后,中断下来,让用户继续进行操作。

硬件断点删除稍有些麻烦,单击菜单“Debug/Hardware breakpoints(调试/硬件断点)”,打开硬件断点面板,如图2.27所示,然后单击“Delete”按钮删除相应的硬件断点。

 
图2.27  删除硬件断点

OllyDbg提供了一个快捷键F4,可以执行到光标所在的行,也是利用调试寄存器原理,中断后自动删除,相当于一次性硬件断点。

硬件断点优点是速度快,在INT 3断点容易被发现的地方,使用硬件断点来代替会有很好的效果;缺点就是最多能使用4个断点。

3.内存断点

OllyDbg可以设置内存访问断点或内存写入断点,原理是对所设的地址设为不可访问/不可写属性,这样当访问/写入的时候就会产生异常,OllyDbg截获异常后比较异常地址是不是断点地址,如果是就中断,让用户继续进行操作。

内存断点会降低OllyDbg速度,因为每次异常时都要通过比较来确定是否应该停下,也许OllyDbg可能在速度上做了考虑而只实现一个内存断点。

程序运行时会有3种状态:读取、写入、执行。

 004013D0    mov   dword ptr   [405528], edx    ;对[405528]地址处的内存写入
004013D0    mov   dword ptr   edx,[405528]     ;对[405528]地址处的内存读取
用OllyDbg加载实例TraceMe.exe,看到4013D0一行有一个写内存的指令:
004013D0  8915 28554000  mov    dword ptr [405528], edx

就用这个地址来演示一下如何下内存断点。在数据窗口对405528下内存写断点,方法是将光标移到405528地址处,选中需要下断点的地址区域,单击鼠标右键,执行“Breakpoint/Memory,on write(断点/内存写入)”,如图2.28所示。

 
(点击查看大图)图2.28  设置内存写入断点

下了内存写断点后,按F9键让程序跑起来,会马上中断在“4013D0 mov [405528], edx”这行。如果要清除内存断点,单击鼠标右键,执行“Breakpoint/Remove memory breakpoint(断点/删除内存断点)”。同样,内存访问断点操作类似。

对代码也可下内存访问断点。在OllyDbg里重新加载实例,随意定位一行代码,如4013D6,单击鼠标右键,执行“Breakpoint/Memory,on access(断点/内存访问)”,如图2.29所示。

  
(点击查看大图)图2.29  设置内存访问断点

当然要执行内存地址4013D6的代码时需要“访问”它,因此按F9键让实例在OllyDbg里跑起来,就会中断在4013D6这行所下的内存访问断点上。这个实验表明内存执行的地方,也可以用内存访问中断。

内存断点不修改原代码。它不会像INT 3断点那样,因为修改代码被程序校验而导致中断失败,因此在遇到代码校验,并且硬件断点失灵情况下,可以用内存断点来代替。

4.内存访问一次性断点

Windows对内存使用段页式的管理,在OllyDbg里按“Alt+M”键显示内存,可以看到许多段,每个段都有不可访问、读、写、执行属性。在相应的段上单击右键,如图2.30所示,会发现一个命令“Set break-on-access(在访问上设置断点)”,其快捷键是F2键,对整个内存块设置该类断点。这个断点是一次性断点,当所在段被读取或执行时就中断,中断发生以后,断点将被删除。想捕捉调用或返回到某个模块时,如后面章节中的脱壳时,该类断点就显得特别有用。右键中的“Set memory breakpoint on access(设置内存访问断点)”和“Set break-on-access”功能一样,所不同的是它不是一次性断点。这类断点仅在NT架构下可用。

  
(点击查看大图)图2.30  对区块设置内存断点

5.消息断点

Windows本身是由消息驱动的,如果调试时没有合适的断点,可以尝试消息断点。消息断点使得当某个特定窗口函数接收到某个特定消息时程序中断。

当用户单击一个按钮、移动鼠标或向文本框中键入文字时,一条消息就会被发送给当前的窗体。所有发送的消息都有4个参数:一个窗口句柄(hwnd),一个消息编号(msg),还有两个32位长度(Long)的参数。Windows通过句柄来标识它代表的对象,比如单击某个按钮,Windows就是通过句柄来判断是点击了哪一个按钮,然后发送相应的消息通知程序。

用实例TraceMe.exe来演示一下如何下消息断点。在OllyDbg里运行实例,输入用户名与序列号,单击菜单“View/Windows(查看/窗口)”或单击工具栏中的   按钮,列出窗口相关参数,如图2.31所示。如果界面无内容显示,此时执行鼠标右键菜单中的“Actualize(刷新)”命令。

  
(点击查看大图)图2.31  列出窗口相关参数
这里用于列出所有属于被调试程序窗口及其窗口相关的重要参数,比如按钮、对应的ID以及句柄(Handle)等。现在要对Check按钮下断点,当单击按钮时中断。在Check条目上单击鼠标右键,如图2.32所示。
 
图2.32  设置消息断点
在弹出的右键菜单中,执行“Message breakpoint on ClassProc(在ClassProc上设置消息断点)”,会弹出如图2.33所示的设置窗口。
当用鼠标左键单击按钮并松开时,会发送WM_LBUTTONUP这个消息,单击图2.33中的下拉菜单选择“202 WM_LBUTTONUP”,再单击“OK”按钮,至此消息断点设置好了。
 
图2.33  在WinProc上设消息断点
回到TraceMe界面,单击“Check”按钮,鼠标松开时,将会中断在Windows系统代码里。代码如下(不同版本的系统,代码会不同的):
77D3B00E [ESP+8]==WM_LBUTTONUP  mov     edi, edi
77D3B010                            push    ebp
77D3B011                          mov     ebp, esp
77D3B013                          mov     ecx, dword ptr [ebp+8]
77D3B016                         push    esi
77D3B017                          call    77D184D0

现在消息捕捉到了,但处于系统底层代码里,这时企图使用“Alt+F9”键或“Ctrl+F9”键返回到TraceMe程序的领空代码里是徒劳的。

VC可执行文件的执行代码是存放在代码段里的,本例就是.text区块里。当从系统代码回到应用程序代码段的时候,正是代码段的执行,因此对代码段下内存断点就能返回应用程序的代码领空。按“Alt+M”键打开内存窗口,对.text区块下内存访问断点,执行右键菜单命令“Set break-on-access(在访问上设置断点)”或按快捷键F2,如图2.34所示。

 
(点击查看大图)图2.34  对代码段下内存访问断点
现在按F9键运行程序,立即中断在程序的空间004010D0处,这里正是程序的消息循环处。
004010D0   sub     esp, 0F4
004010D6   push    esi
004010D7   push    edi
004010D8   mov     ecx, 5
004010DD   mov     esi, 00405060
……
00401132   sub     eax, 10          ;  Switch (cases 10..111)
00401135   mov     dword ptr [esp+10], edx
00401139   movs    byte ptr es:[edi], byte ptr [esi]
0040113A   je      00401314
00401140   sub     eax, 100
00401145   je      004012CD
0040114B   dec     eax
0040114C   jnz     004012C0

这段代码是一个消息循环,不停地处理TraceMe主界面的各类消息,此时可能不是直接处理按钮事件,如果单步跟踪,可能会跟进系统代码里去。


可以重复这个过程,在几次中断后到达处理按钮的事件代码。很快就能发现“Check”按钮事件的代码了:


最后,可以将消息断点删除,方法是按“Alt+B”键切换到断点窗口,选中消息断点,直接删除,如图2.35所示。

 
(点击查看大图)图2.35  删除消息断点

 
相关文章
一日一文章
 
一日一软件
一日一动画