第16章 调试器OllyDBG的工作原理分析

在Windows平台下,大家耳熟能详的调试器当属OllyDBG,为了能够更加熟练地运用它,了解其工作原理是必不可少的。本章将对OllyDBG的断点工作原理、异常处理机制、调试文件的加载流程等进行详细分析。

OllyDBG的断点功能是基于异常处理来实现的,通过捕获程序执行过程中的异常信息来中断程序的执行流程。OllyDBG常用的断点类型有三种:INT3断点、内存断点、硬件断点。每种断点都是一种制造异常的方法,首先使程序在运行过程中产生错误,然后由OllyDBG的异常处理来接管,从而实现断点的功能。

16.1 INT3断点

INT3断点是最常用的断点,其工作流程是通过修改机器码为0xCC来制造异常。当程序执行0xCC代码时会触发INT3异常,OllyDBG将捕获此异常并等待用户的处理。跳过INT3断点则是将0xCC处的代码恢复,再次运行,以保证程序的正常运行。

OllyDBG设置INT3断点的快捷键是F2,这个快捷键将会出现在消息回调函数中。消息回调函数的首地址可通过查询窗口类的注册过程获取,先找到RegisterClass函数,然后顺藤摸瓜找到窗口类WNDCLASS中消息回调函数的赋值处。

在消息回调函数对快捷键F2的处理过程中可以找到INT3断点设置的函数地址为0x00419974处,将其重命名为SetINT3。使用IDA加载并分析OllyDBG,在IDA中使用地址查询快捷键G查看函数实现流程,如代码清单16-1所示。

代码清单16-1 SetINT3断点设置函数分析1—IDA分析


int__cdecl SetINT3(int arglist, int, int, int, int, int, int);函数类型识别

SetINT3 proc near;函数调用地址sub_41E604+130C↑sub_41E604+138D

00419974 buffer=byte ptr-408h;局部变量和参数地址标号定义

00419974 dest=byte ptr-208h

00419974 var_8=dword ptr-8

00419974 var_4=dword ptr-4

00419974 arglist=dword ptr 8

00419974 arg_4=dword ptr 0Ch

00419974 arg_8=dword ptr 10h

00419974 arg_C=dword ptr 14h

00419974 arg_10=dword ptr 18h

00419974 arg_14=dword ptr 1Ch

00419974 arg_18=dword ptr 20h

00419974

;部分代码分析略

00419980 mov edi,[ebp+arg_C]

00419983 mov ebx,[ebp+arglist];获取断点列表信息结构

00419986 cmp dword_4D57C4,0

0041998D jz short loc_4199ED;检查断点是否存在,存在则跳转

0041998F cmp[ebp+arg_4],71h;检查参数,此参数为键盘消息F2

00419993 jnz short loc_4199A7

00419995 cmp[ebp+arg_8],0

00419999 jnz short loc_4199A7

0041999B push ebx

;检查将要设置断点的地址处是否已经存在断点

0041999C call_Getbreakpointtype

004199A1 test ah,2;未设置断点,返回0x08

004199A4 pop ecx

004199A5 jnz short loc_4199ED;如果已设置断点,则跳转成功


代码清单16-1完成了前期的检查工作,这段代码中出现了一个断点结构类型arglist,此类型定义如下:


00000000 t_sorted struc;定义结构名称为t_sorted,结构大小为0x138

00000000 name[MAXPATH db 260 dup(?);描述结构名称

0000104 n dd?;数组元素个数

00000108 nmax dd?

0000010C selected dd?

00000110 seladdr dd?

00000114 itemsize dd?;数组中每个元素的大小

00000118 version dd?

0000011C data dd?;保存各元素的指针

00000120 sortfunc dd?

00000124 destfunc dd?

00000128 sort dd?

0000012C sorted dd?

00000130 index dd?

00000134 suppresserr dd?

00000138 t_sorted ends


对于t_sorted,我们暂时只需要了解结构中的n、itemsize、data。n用于表示数组元素的个数,itemsize用于表示数组中每个元素的大小,data用于保存各元素的指针。_Getbreakpointtype函数会根据t_sorted结构中已经记录的INT3断点信息来判断当前所设置的断点操作是设置断点还是删除断点。在设置断点操作时(快捷键F2),如果在t_sorted结构中没有记录,代码清单16-1中最后的条件跳转将失败,代码顺序向下执行,进入设置断点的实现流程中,具体分析如代码清单16-2所示。

代码清单16-2 SetINT3断点设置函数分析2—IDA分析


loc_4199A7:;地址标号

004199A7 push ebx;压入断点列表信息

004199A8 call_Findmodule;查找断点所在的模块的信息

004199AD pop ecx

004199AE mov esi, eax

004199B0 test eax, eax;检查模块查询结果

004199B2 jz short loc_4199C3;查询模块失败跳转

004199B4 cmp ebx,[esi+0Ch]

004199B7 jb short loc_4199C3;比较断点地址是否在查询的模块内

004199B9 mov edx,[esi+0Ch]

004199BC add edx,[esi+10h]

004199BF cmp ebx, edx;检查断点是否在代码段中

004199C1 jb short loc_4199ED;如果在代码段中,则跳转

loc_4199C3:;地址标号,此流程中显示断点警告信息

004199C3 push 2124h;uType

004199C8 push offset aU;"可疑的断点"

;压入字符串"您设置的断点……关闭这个警告信息。"的首地址

004199CD push offset aLIZTSU_Int3USL

004199D2 mov ecx, hWnd

004199D8 push ecx;hWnd

004199D9 call MessageBoxA


代码清单16-2对设置断点的地址进行了检查,首先判断断点是否设置在分析程序的模块中,其次检查断点是否设置在代码段内。这就是使用OllyDBG时在非代码段中设置断点会有警告提示的原因。

到这里,前期的检查工作就结束了,下面正式进入到INT3断点的设置流程中,具体分析如代码清单16-3所示。

代码清单16-3 SetINT3断点设置函数分析3—IDA分析


loc_4199ED:;地址标号

;部分代码分析略

004199FD push ebx

004199FE call_Getbreakpointtype;获取设置INT3断点地址处的内存页属性

00419A03 test ah,2

00419A06 pop ecx

00419A07 jz short loc_419A1D;如果已经设置了断点,则跳转失败

00419A09 push 0

00419A0B lea edx,[ebx+1]

00419A0E push edx

00419A0F push ebx

00419A10 call_Deletebreakpoints;删除断点信息

00419A15 add esp,0Ch

00419A18 jmp loc_419D5E

loc_419A1D:;地址标号

00419A1D cmp[ebp+arg_8],0

00419A21 jnz short loc_419A6D

00419A23 push 0;int

00419A25 push 0;char

00419A27 push 20200h;int

00419A2C push ebx;arglist

00419A2D call_Setbreakpointext;设置断点

00419A32 add esp,10h

00419A35 lea esi,[ebx+1]

00419A38 push 38h

00419A3A push esi

00419A3B push ebx

;将INT3断点信息表中的name属性的值修改为0x38

00419A3C call_Deletenamerange

;部分代码分析略

00419A68 jmp loc_419D5E

;部分代码分析略

loc_419D5E:;地址标号

00419D5E push 0

00419D60 push 0

00419D62 push 474h

00419D67 call_Broadcast;发送消息通知所有子窗口更新

00419D6C add esp,0Ch

00419D6F xor eax, eax

loc_419D71:

;恢复线程,函数返回部分略

00419D77 retn

00419D77 SetINT3 endp


代码清单16-3展示了INT3断点的设置与删除过程。通过查询断点信息表中的信息,检查设置断点处是否已经设置了INT3断点,如果设置了断点,则会删除该断点。如果没有设置断点,则设置INT3断点。INT3断点的设置由_Setbreakpointext完成,具体分析如代码清单16-4所示。

代码清单16-4_Setbreakpointext内存断点设置—IDA分析


;int__cdecl Setbreakpointext(char arglist, int, char, int)函数参数分析

00419560_Setbreakpointext proc near;函数入口

00419560 var_2C=dword ptr-2Ch

;局部变量和参数标号定义略

00419560 arg_C=dword ptr 14h

00419560 push ebp

;部分代码分析略

00419570 mov edi, dword ptr[ebp+arglist]

00419573 push 0

00419575 push edi;设置断点地址

00419576 call_Finddecode;查找断点所在代码区的位置

0041957B add esp,8

;断点检查代码分析略

0041963C push edi

0041963D push offset byte_4D7EE1

00419642 call_Findsorteddata;查找断点信息表中的数据

;部分代码分析略

00419669 mov[ebp+var_1F],edx

0041966C push ecx;arglist

0041966D push offset byte_4D7EE1;src

00419672 call_Addsorteddata;将断点信息添加到断点信息表中

loc_419750:;地址标号

00419750 push 2;char

00419752 push 1;n

00419754 push edi;arglist

00419755 lea eax,[ebx+0Ch];读取目标的1字节数据到缓冲区[ebx+0Ch]中

00419758 push eax;src

00419759 call_Readmemory;读取目标的内存信息

0041975E add esp,10h

00419761 cmp eax,1;检查读取结果

00419764 jz short loc_419799;如果读取失败,则删除断点信息表中的信息

;删除断点信息表操作略

loc_4197C9:;地址标号

004197C9 push 2;char

004197CB push 1;nSize

004197CD push edi;arglist

004197CE lea edx,[ebp+Buffer];将0xCC写入目标断点的地址中

004197D1 push edx;lpBuffer

004197D2 call_Writememory;写入INT3断点信息

004197D7 add esp,10h

004197DA cmp eax,1

004197DD jz short loc_419814;如果写入失败,则删除断点信息表中的信息,否则跳转

;部分代码分析略

loc_419814:;地址标号

00419814 or dword ptr[ebx+8],100h

0041981B jmp loc_4198A2;跳转到地址标号loc_4198A2处

;部分代码分析略

loc_4198A2:;地址标号

;部分代码分析略

004198F4 push(offset aNoaccess+8)

004198F9 push 38h

004198FB push edi

004198FC call_Insertname;设置INT3断点信息

00419901 add esp,0Ch

;操作同上,分析略

;刷新窗口,并还原环境,结束函数调用,分析略

00419973 retn

00419973_Setbreakpointext endp


以上分析了INT3断点的设置与删除过程。INT3断点是如何被触发的呢?要了解INT3断点的触发过程,需要掌握异常处理机制的相关知识,因此本书将触发过程与异常处理机制的内容(16.4节)放在一起进行详细分析。

OllyDBG实现INT3断点的主要流程为:检查INT3断点是否在记录的断点信息表中→将INT3断点信息记录到表中→记录INT3断点处的机器码信息→将INT3断点处的机器码修改为0xCC→设置断点信息表。