最近学习逆向,对OD本身做了个逆向,也算是一个小小的锻炼吧。呵呵,在这里以分析报告的形式贴出来,请大家批评指正。谢谢。

Ollydbg(以下均简称为OD)中的Int3断点的主要功能是:在需要下断点的执行代码处将原来的代码改成0xCC,程序执行到此处后会报一个Int3异常,由OD捕获并处理。当要执行该行代码时,将原来的代码改回来并执行,然后再恢复断点,这样就不会影响程序的正常运行了。
这里仅描述最常见的功能,其它的有兴趣的话可以分析一把。

先说明一下OD中的两个结构体,在IDA中,声明为如下格式:
t_bpoint用来保存Int断点的相关信息
00000000 t_bpoint        struc ; (sizeof=0x11)
00000000 addr         dd ?                    ; // Address of breakpoint
00000004 dummy      dd ?                    ; // Always 1
00000008 type         dd ?                    ; // Type of breakpoint, TY_xxx
0000000C cmd         db ?                    ; // Old value of command
0000000D passcount    dd ?                    ; // Actual pass count
00000011 t_bpoint        ends
其中:addr为断点的地址,dummy始终为1,type为断点的类型,cmd为要用0xCC替换的指令码,passcount为需要断下的次数,0表示每次都断下。

t_sorted结构体保存了一种有序的数据:
00000000 ; Descriptor of sorted table
00000000 t_sorted        struc ; (sizeof=0x138)
00000000 name[MAXPATH]   db 260 dup(?)     ; char  Name of table, as appears in error messages
00000104 n               dd ?                    ; int  Actual number of entries
00000108 nmax            dd ?                    ; int  Maximal number of entries
0000010C selected        dd ?                    ; int  Index of selected entry or -1
00000110 seladdr         dd ?                    ; ulong  Base address of selected entry
00000114 itemsize        dd ?                    ; int  Size of single entry
00000118 version         dd ?                    ; ulong  Unique version of table
0000011C data            dd ?                    ; void*  Elements, sorted by address
00000120 sortfunc        dd ?                  ; SORTFUNC  Function which sorts data or NULL
00000124 destfunc        dd ?                    ; DESTFUNC  Destructor function or NULL
00000128 sort            dd ?                    ; int  Sorting criterium (column)
0000012C sorted          dd ?                    ; int  Whether indexes are sorted
00000130 index           dd ?                    ; int  Indexes, sorted by criterium
00000134 suppresserr     dd ?                    ; int  Suppress multiple overflow errors
00000138 t_sorted        ends
name是结构体的名称,用来区别不同类型的结构体
n是数组元素的个数
itemsize是数组元素的大小
data 是指向各种数据结构数组的指针,这里使用的是int3断点,所以现在保存的是int3断点数据结构体数组的指针
---------------------------------------------------------------------------------------------------------------------------------

1)Int3断点的设置
int3断点的设置是通过消息来处理的,对应右键点击菜单中的切换,其对应的消息为1E;此外还有热键F2、双击反汇编窗口等也能切换int3断点,转入的函数虽然不同,但是调用设置int3断点的函数都是同一个,就不再举例了。传入相应参数调用函数00419974,改变int3断点,该函数的调用处如下:
004237C8   .  51                    push    ecx    ; |Arg6
004237C9   .  50                    push    eax    ; |Arg5 => 00000002
004237CA   .  6A 00                 push    0      ; |Arg4 = 00000000
004237CC   .  6A 00                 push    0      ; |Arg3 = 00000000
004237CE   .  6A 71                 push    71     ; |Arg2 = 00000071
004237D0   .  52                    push    edx    ; |Arg1 => 0040100C
004237D1   .  E8 9E61FFFF           call    00419974   ; \OLLYDBG.00419974

下面主要来分析上述的00419974这个函数,经过分析可知大致主要流程为:

1.    判断是否配置了“Warn when break not in code”为1,若为1的话,程序会先判断所下断点是否在代码区,不在的话,会显示警告消息,若用户选择继续,则会断下,否则退出;
2.    若没有配置上面的项目,则首先获得断点的类型(即断点类型的高位是否为2,若为2则删除断点,不为2则设置断点),检查该地址的断点是否已经存在,若存在,则删除断点,并退出;
3.    若该地址处无Int3断点,则设置断点,使用的是Setbreakpointext函数,其流程如下:
a)  获得断点在调试进程中实际地址;
b)  判断指令码是否有效(判断规则是:指令码与1F做与运算之后为3或13的时候,说明是断在指令码中间;1D、1E、1F是正常指令码,除此之外,其它的都是无法执行的指令码),无效的话,重新设置CPU窗口,显示错误提示并退出;
c)  在t_sorted结构中查找断点数据;
d)  若t_sorted中不存在该断点,则添加;
e)  判断被调试进程是否在运行状态,若在运行,则把所有的线程都挂起;
f)  读取断点所在地址的内存,若读取成功的话,调用WriteMemory写入0xCC断点(跟入WriteMemory后,发现是调用WriteProcessMemory这个API函数来下Int3断点的);
g)  恢复线程运行;
h)  显示信息在窗口中;
4.  若设置断点成功,则插入name并做其它的一些判断与显示操作,进而退出;

保存现场,提升栈帧空间,用于存放局部变量:
00419974  /$  55         push    ebp
00419975  |.  8BEC       mov     ebp, esp
00419977  |.  81C4 F8FBF>add     esp, -408
0041997D  |.  53         push    ebx
0041997E  |.  56         push    esi
0041997F  |.  57         push    edi
检查OD配置文件中的   “Warn when break not in code”是否为1,1为真,0为假,若上面的配置为0,则跳到下面的处理代码中:
00419980  |.  8B7D 14    mov     edi, dword ptr [ebp+14]
00419983  |.  8B5D 08    mov     ebx, dword ptr [ebp+8]
00419986  |.  833D C4574>cmp     dword ptr [4D57C4], 0  ; 4D57C4--Warn when break not in code
0041998D  |.  74 5E      je      short 004199ED  ;为0则跳转到下面的处理代码
检查传入的参数是否正确:
0041998F  |.  837D 0C 71 cmp     dword ptr [ebp+C], 71
00419993  |.  75 12      jnz     short 004199A7
00419995  |.  837D 10 00 cmp     dword ptr [ebp+10], 0
00419999  |.  75 0C      jnz     short 004199A7
先在t_sorted列表中查找int3断点,找不到返回8,并跳过取得模块信息部分:
0041999B  |.  53         push    ebx
0041999C  |.  E8 D703000>call    _Getbreakpointtype
004199A1  |.  F6C4 02    test    ah, 2    ;检查返回值是否为20h
004199A4  |.  59         pop     ecx
004199A5  |.  75 46      jnz     short 004199ED  ;不是则跳过取得模块信息部分
取得模块信息,并检查是否取得,若取得失败则跳转到显示断点错误消息分支:
004199A7  |>  53         push    ebx                       ; /Arg1
004199A8  |.  E8 6B44040>call    _Findmodule               ; \_Findmodule
004199AD  |.  59         pop     ecx
004199AE  |.  8BF0       mov     esi, eax
004199B0  |.  85C0       test    eax, eax  ;检查是否得到模块信息
004199B2  |.  74 0F      je      short 004199C3  ;没有得到则跳转到下面错误显示分支
004199B4  |.  3B5E 0C    cmp     ebx, dword ptr [esi+C]
004199B7  |.  72 0A      jb      short 004199C3
004199B9  |.  8B56 0C    mov     edx, dword ptr [esi+C]
004199BC  |.  0356 10    add     edx, dword ptr [esi+10]
004199BF  |.  3BDA       cmp     ebx, edx
004199C1  |.  72 2A      jb      short 004199ED
显示断点错误消息,若用户选择Yes则继续,否则退出:
004199C3  |>  68 2421000>push    2124                              ; /Style = MB_YESNO
004199C8  |.  68 40274B0>push    004B2740                           ; |Title
004199CD  |.  68 C3284B0>push    004B28C3                          ; |Text
004199D2  |.  8B0D 7C3B4>mov     ecx, dword ptr [4D3B7C]              ; |
004199D8  |.  51         push    ecx                               ; |hOwner
004199D9  |.  E8 385B090>call    <jmp.&USER32.MessageBoxA>           ; \MessageBoxA
004199DE  |.  8BF0       mov     esi, eax
004199E0  |.  83FE 06    cmp     esi, 6
004199E3  |.  74 08      je      short 004199ED
004199E5  |.  83C8 FF    or      eax, FFFFFFFF  ;返回-1
004199E8  |.  E9 8403000>jmp     00419D71  ;跳转到结束处
得到该地址int3断点的属性,如果已经设置了int3断点,则将其删除并跳转到结束处;若没有设置,则跳过删除部分代码:
004199ED  |>  837D 0C 71 cmp     dword ptr [ebp+C], 71
004199F1  |.  0F85 24020>jnz     00419C1B
004199F7  |.  837D 10 00 cmp     dword ptr [ebp+10], 0
004199FB  |.  75 20      jnz     short 00419A1D
004199FD  |.  53         push    ebx
004199FE  |.  E8 7503000>call    _Getbreakpointtype  ;得到该地址的int3断点的属性
00419A03  |.  F6C4 02    test    ah, 2    ;比较是否已经设置了int3断点
00419A06  |.  59         pop     ecx
00419A07  |.  74 14      je      short 00419A1D  ;若没有设置则跳过删除断点的代码
00419A09  |.  6A 00      push    0                         ; /Arg3 = 00000000
00419A0B  |.  8D53 01    lea     edx, dword ptr [ebx+1]    ; |
00419A0E  |.  52         push    edx                       ; |Arg2
00419A0F  |.  53         push    ebx                       ; |Arg1
00419A10  |.  E8 03FBFFF>call    _Deletebreakpoints        ; \_Deletebreakpoints
00419A15  |.  83C4 0C    add     esp, 0C
00419A18  |.  E9 4103000>jmp     00419D5E
在这里有对传入参数的一个比较,但是测试时无法得到0以外的值,所以无法确定该参数的作用,这样也无法测试00419A6D后面的代码所表示的意义:
00419A1D  |>  837D 10 00 cmp     dword ptr [ebp+10], 0
00419A21  |.  75 4A      jnz     short 00419A6D
这里就是设置int3断点的函数,将地址传入即可:
00419A23  |.  6A 00      push    0                         ; /Arg4 = 00000000
00419A25  |.  6A 00      push    0                         ; |Arg3 = 00000000
00419A27  |.  68 0002020>push    20200                     ; |Arg2 = 00020200
00419A2C  |.  53         push    ebx                       ; |Arg1
00419A2D  |.  E8 2EFBFFF>call    _Setbreakpointext         ; \_Setbreakpointext
00419A32  |.  83C4 10    add     esp, 10
设置完int3断点后,删除int3断点处的name属性为38h、3Ch、3Bh以及30h的name,然后跳转到结束广播处:
00419A35  |.  8D73 01    lea     esi, dword ptr [ebx+1]
00419A38  |.  6A 38      push    38                        ; /Arg3 = 00000038
00419A3A  |.  56         push    esi                       ; |Arg2
00419A3B  |.  53         push    ebx                       ; |Arg1
00419A3C  |.  E8 87B5040>call    _Deletenamerange          ; \_Deletenamerange
00419A41  |.  83C4 0C    add     esp, 0C
00419A44  |.  6A 3C      push    3C                        ; /Arg3 = 0000003C
00419A46  |.  56         push    esi                       ; |Arg2
00419A47  |.  53         push    ebx                       ; |Arg1
00419A48  |.  E8 7BB5040>call    _Deletenamerange          ; \_Deletenamerange
00419A4D  |.  83C4 0C    add     esp, 0C
00419A50  |.  6A 3B      push    3B                        ; /Arg3 = 0000003B
00419A52  |.  56         push    esi                       ; |Arg2
00419A53  |.  53         push    ebx                       ; |Arg1
00419A54  |.  E8 6FB5040>call    _Deletenamerange          ; \_Deletenamerange
00419A59  |.  83C4 0C    add     esp, 0C
00419A5C  |.  6A 30      push    30                        ; /Arg3 = 00000030
00419A5E  |.  56         push    esi                       ; |Arg2
00419A5F  |.  53         push    ebx                       ; |Arg1
00419A60  |.  E8 63B5040>call    _Deletenamerange          ; \_Deletenamerange
00419A65  |.  83C4 0C    add     esp, 0C
00419A68  |.  E9 F102000>jmp     00419D5E      ;跳转到结束广播处
这部分省略的代码无法得知其调用的必要条件,不过里面的内容跟上面相似,都是通过判断来设置int3断点,这里就不再详细说明了:
……
……
向子窗体发送广播,要求更新所有子窗口:
00419D5E  |>  6A 00      push    0                         ; /Arg3 = 00000000
00419D60  |.  6A 00      push    0                         ; |Arg2 = 00000000
00419D62  |.  68 7404000>push    474                       ; |Arg1 = 00000474
00419D67  |.  E8 0807040>call    _Broadcast                ; \_Broadcast
00419D6C  |.  83C4 0C    add     esp, 0C
最后返回0,并且恢复现场:
00419D6F  |.  33C0       xor     eax, eax
00419D71  |>  5F         pop     edi
00419D72  |.  5E         pop     esi
00419D73  |.  5B         pop     ebx
00419D74  |.  8BE5       mov     esp, ebp
00419D76  |.  5D         pop     ebp
00419D77  \.  C3         retn

2)Int3断点的处理
Int3断点处理的大致流程是:
1、  获取当前寄存器的信息,重点是Eip的值
2、  根据Int3断点的类型(0xCC或0xCD03)回溯指针地址
3、  触发Int3异常,被调试程序断下;
4、  若继续跑的话,则恢复原有的指令,让被调试程序正确执行指令;
5、  走完正确指令之后,再重新设置指令为Int3断点;

在OD中有一个处理所有异常的函数42EBD0,从该函数入手分析Int3断点的处理。
0042EBD0  /$  55            push    ebp
0042EBD1  |.  8BEC          mov     ebp, esp
0042EBD3  |.  81C4 04F0FFFF add     esp, -0FFC
0042EBD9  |.  50            push    eax
0042EBDA  |.  81C4 00F5FFFF add     esp, -0B00
0042EBE0  |.  53            push    ebx
0042EBE1  |.  56            push    esi
0042EBE2  |.  57            push    edi  ;以上是开栈帧代码

0042EBE3  |.  8B35 1C574D00 mov     esi, dword ptr [4D571C] ;4D571C为全局变量,保存的是DebugEvent.dwThreadId
0042EBE9  |.  56            push    esi
0042EBEA  |.  E8 5DF8FFFF   call    0042E44C
函数42E44C的主要功能描述如下:

函数功能:通过GetThreadContext的方法获得线程的上下文环境,把该环境保存
          至OD线程信息结构中的reg字段中,若oldreg已经失效,则复制reg的
          值到oldreg中。
          若被调试进程有多个线程,则循环取值,保存至OD线程信息结构数组中。
传入参数:DebugEvent.dwThreadId 
返回值  :成功时为保存在OD线程信息结构中的当前寄存器信息结构的指针(主线程)
          失败返回0

这里说到了两个结构体,在IDA中描述如下:
00000000 t_reg           struc ; (sizeof=0x196)    ;保存寄存器信息
00000000 r_modified      dd ?                    ; // Some regs modified, update context
00000004 r_modifiedbyuser dd ?                   ; // Among modified, some modified by user
00000008 r_singlestep    dd ?                    ; // Type of single step, SS_xxx
0000000C r_EAX           dd ?
00000010 r_ECX           dd ?
00000014 r_EDX           dd ?
00000018 r_EBX           dd ?
0000001C r_ESP           dd ?
00000020 r_EBP           dd ?
00000024 r_ESI           dd ?
00000028 r_EDI           dd ?
0000002C r_EIP           dd ?                    ; // Instruction pointer (EIP)
00000030 r_EFlags        dd ?                    ; // Flags
00000034 r_top           dd ?                    ; // Index of top-of-stack
00000038 r_long_double   db 80 dup(?)            ; // Float registers, f[top] - top of stack
00000088 r_tag           db 8 dup(?)             ; // Float tags (0x3 - empty register)
00000090 r_fst           dd ?                    ; // FPU status word
00000094 r_fcw           dd ?                    ; // FPU control word
00000098 r_ES            dd ?
0000009C r_CS            dd ?
000000A0 r_SS            dd ?
000000A4 r_DS            dd ?
000000A8 r_FS            dd ?
000000AC r_GS            dd ?
000000B0 r_base          dd 6 dup(?)             ; // Segment bases
000000C8 r_limit         dd 6 dup(?)             ; // Segment limits
000000E0 r_big           db 6 dup(?)             ; // Default size (0-16, 1-32 bit)
000000E6 r_dr6           dd ?                    ; // Debug register DR6
000000EA r_threadid      dd ?                    ; // ID of thread that owns registers
000000EE r_lasterror     dd ?                    ; // Last thread error or 0xFFFFFFFF
000000F2 r_ssevalid      dd ?                    ; // Whether SSE registers valid
000000F6 r_ssemodified   dd ?                    ; // Whether SSE registers modified
000000FA r_ssereg        db 128 dup(?)           ; // SSE registers
0000017A r_mxcsr         dd ?                    ; // SSE control and status register
0000017E r_selected      dd ?                    ; // Reports selected register to plugin
00000182 r_dr0           dd ?                    ; // Debug registers DR0..DR3
00000186 r_dr1           dd ?
0000018A r_dr2           dd ?
0000018E r_dr3           dd ?
00000192 r_dr7           dd ?                    ; // Debug register DR7
00000196 t_reg           ends
00000196
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 t_thread        struc ; (sizeof=0x66C)  ;保存线程信息,同时有新旧两份reg值,可以用来在OD的寄存器窗口为改变的值设置颜色等功能。
00000000 th_threadid     dd ?                    ; // Thread identifier
00000004 th_dummy        dd ?                    ; // Always 1
00000008 th_type         dd ?                    ; // Service information, TY_xxx
0000000C th_thread       dd ?                    ; // Thread handle
00000010 th_datablock    dd ?                    ; // Per-thread data block
00000014 th_entry        dd ?                    ; // Thread entry point
00000018 th_stacktop     dd ?                    ; // Working variable of Listmemory()
0000001C th_stackbottom  dd ?                    ; // Working variable of Listmemory()
00000020 th_context      CONTEXT ?               ; // Actual context of the thread
000002EC th_reg          t_reg ?                 ; // Actual contents of registers
00000482 th_regvalid     dd ?                    ; // Whether reg is valid
00000486 th_oldreg       t_reg ?                 ; // Previous contents of registers
0000061C th_oldregvalid  dd ?                    ; // Whether oldreg is valid
00000620 th_suspendcount dd ?                    ; // Suspension count (may be negative)
00000624 th_usertime     dd ?                    ; // Time in user mode, 1/10th ms, or -1
00000628 th_systime      dd ?                    ; // Time in system mode, 1/10th ms, or -1
0000062C th_reserved     dd 16 dup(?)            ; // Reserved for future compatibility
0000066C t_thread        ends


下面的代码是:
0042EBEF  |.  8BF8               mov     edi, eax
0042EBF1  |.  8B45 08            mov     eax, dword ptr [ebp+8]
0042EBF4  |.  59                 pop     ecx
0042EBF5  |.  8938               mov     dword ptr [eax], edi
0042EBF7  |.  8B15 14574D00      mov     edx, dword ptr [4D5714]   ; 4D5714中保存的是调试事件的代码DebugEvent.dwDebugEventCode
0042EBFD  |.  83FA 09            cmp     edx, 9                 ;  Switch (cases 1..9)
0042EC00  |.  0F87 EE270000      ja      004313F4
0042EC06  |.  FF2495 0DEC4200    jmp     dword ptr [edx*4+42EC0D]  ; 这里是switch跳转
用来判断是何种异常:
0042EC0D  |.  F4134300           dd      OLLYDBG.004313F4  ; EXCEPTION_DEBUG_EVENT
0042EC11  |. |35EC4200           dd      OLLYDBG.0042EC35
0042EC15  |. |FF0C4300           dd      OLLYDBG.00430CFF
0042EC19  |. |D70D4300           dd      OLLYDBG.00430DD7
0042EC1D  |. |3F0F4300           dd      OLLYDBG.00430F3F
0042EC21  |. |37104300           dd      OLLYDBG.00431037
0042EC25  |. |2D114300           dd      OLLYDBG.0043112D
0042EC29  |. |B7114300           dd      OLLYDBG.004311B7
0042EC2D  |. |76124300           dd      OLLYDBG.00431276
0042EC31  |. |C7134300           dd      OLLYDBG.004313C7

0042EC35  |> \8B0D 0C364E00      mov     ecx, dword ptr [4E360C]              ;  Case 1 of switch 0042EBFD
0042EC3B  |.  33C0               xor     eax, eax
0042EC3D  |.  894D EC            mov     dword ptr [ebp-14], ecx
0042EC40  |.  A3 0C364E00        mov     dword ptr [4E360C], eax
0042EC45  |.  C745 A4 20574D00   mov     dword ptr [ebp-5C], 004D5720
0042EC4C  |.  85FF               test    edi, edi  ;检查主线程中是否有当前寄存器的信息;
0042EC4E  |.  75 0D              jnz     short 0042EC5D
0042EC50  |.  8B55 A4            mov     edx, dword ptr [ebp-5C]
0042EC53  |.  33DB               xor     ebx, ebx
0042EC55  |.  8B4A 0C            mov     ecx, dword ptr [edx+C]
0042EC58  |.  894D D8            mov     dword ptr [ebp-28], ecx
0042EC5B  |.  EB 22              jmp     short 0042EC7F
0042EC5D  |>  8B47 2C            mov     eax, dword ptr [edi+2C]              ;  ntdll.7C921231
0042EC60  |.  8945 D8            mov     dword ptr [ebp-28], eax  ;这一段是把异常地址赋给局部变量ebp-28
0042EC63  |.  837D EC 00         cmp     dword ptr [ebp-14], 0
0042EC67  |.  8B5F 10            mov     ebx, dword ptr [edi+10]
0042EC6A  |.  74 13              je      short 0042EC7F

以下代码是判断产生中断的指令码是0xCC还是0xCD03,若是0xCC,则指令码要回溯1(因为Eip指向下一条指令,回溯后才是正确的断点地址),若是0xCD03,则指令码要回溯2,其它的不回溯。
0042EC6C  |.  F647 31 01         test    byte ptr [edi+31], 1
0042EC70  |.  74 0D              je      short 0042EC7F
0042EC72  |.  8167 30 FFFEFFFF   and     dword ptr [edi+30], FFFFFEFF
0042EC79  |.  C707 01000000      mov     dword ptr [edi], 1
0042EC7F  |>  8B45 A4            mov     eax, dword ptr [ebp-5C]
0042EC82  |.  8138 03000080      cmp     dword ptr [eax], 80000003          ;  Int3中断
0042EC88  |.  74 07              je      short 0042EC91
0042EC8A  |.  33D2               xor     edx, edx
0042EC8C  |.  8955 DC            mov     dword ptr [ebp-24], edx
0042EC8F  |.  EB 79              jmp     short 0042ED0A
0042EC91  |>  6A 02              push    2                         ; /Arg4 = 00000002
0042EC93  |.  6A 01              push    1                         ; |Arg3 = 00000001
0042EC95  |.  8B4D D8            mov     ecx, dword ptr [ebp-28]             ; |
0042EC98  |.  49                 dec     ecx                   ; |减一是让指令码回溯
0042EC99  |.  51                 push    ecx                              ; |Arg2
0042EC9A  |.  8D45 BB            lea     eax, dword ptr [ebp-45]              ; |
0042EC9D  |.  50                 push    eax                              ; |Arg1
0042EC9E  |.  E8 69260300        call    _Readmemory                 ; \_Readmemory
0042ECA3  |.  83C4 10            add     esp, 10
0042ECA6  |.  83F8 01            cmp     eax, 1
0042ECA9  |.  74 07              je      short 0042ECB2
0042ECAB  |.  33D2               xor     edx, edx
0042ECAD  |.  8955 DC            mov     dword ptr [ebp-24], edx
0042ECB0  |.  EB 58              jmp     short 0042ED0A
0042ECB2  |>  33C0               xor     eax, eax
0042ECB4  |.  8A45 BB            mov     al, byte ptr [ebp-45]
0042ECB7  |.  3D CC000000        cmp     eax, 0CC
0042ECBC  |.  75 09              jnz     short 0042ECC7
0042ECBE  |.  C745 DC 01000000   mov     dword ptr [ebp-24], 1        ;  若Readmemory在断点地址读的指令码是CC的话,ebp-24设为1---ebp-24保存的是要回溯的指令长度
0042ECC5  |.  EB 43              jmp     short 0042ED0A
0042ECC7  |>  83F8 03            cmp     eax, 3
0042ECCA  |.  74 07              je      short 0042ECD3
0042ECCC  |.  33D2               xor     edx, edx
0042ECCE  |.  8955 DC            mov     dword ptr [ebp-24], edx
0042ECD1  |.  EB 37              jmp     short 0042ED0A
0042ECD3  |>  6A 02              push    2                         ; /Arg4 = 00000002
0042ECD5  |.  6A 01              push    1                         ; |Arg3 = 00000001
0042ECD7  |.  8B4D D8            mov     ecx, dword ptr [ebp-28]      ; |
0042ECDA  |.  83E9 02            sub     ecx, 2                     ; |指令码回溯2
0042ECDD  |.  51                 push    ecx                       ; |Arg2
0042ECDE  |.  8D45 BB            lea     eax, dword ptr [ebp-45]        ; |
0042ECE1  |.  50                 push    eax                        ; |Arg1
0042ECE2  |.  E8 25260300        call    _Readmemory              ; \_Readmemory
0042ECE7  |.  83C4 10            add     esp, 10
0042ECEA  |.  83F8 01            cmp     eax, 1
0042ECED  |.  75 0D              jnz     short 0042ECFC
0042ECEF  |.  33D2               xor     edx, edx
0042ECF1  |.  8A55 BB            mov     dl, byte ptr [ebp-45]
0042ECF4  |.  81FA CD000000      cmp     edx, 0CD
0042ECFA  |.  74 07              je      short 0042ED03
0042ECFC  |>  33C9               xor     ecx, ecx
0042ECFE  |.  894D DC            mov     dword ptr [ebp-24], ecx
0042ED01  |.  EB 07              jmp     short 0042ED0A
0042ED03  |>  C745 DC 02000000   mov     dword ptr [ebp-24], 2
0042ED0A  |>  8B45 DC            mov     eax, dword ptr [ebp-24]
0042ED0D  |.  2945 D8            sub     dword ptr [ebp-28], eax      ;  回溯指令码,才是正确的断点地址
0042ED10  |.  8B15 08574D00      mov     edx, dword ptr [4D5708]
0042ED16  |.  3B55 D8            cmp     edx, dword ptr [ebp-28]   ;  ntdll.DbgBreakPoint
0042ED19  |.  75 08              jnz     short 0042ED23
0042ED1B  |.  3B1D 0C574D00      cmp     ebx, dword ptr [4D570C]
0042ED21  |.  74 16              je      short 0042ED39

0042ED23  |> \33C9          xor     ecx, ecx
0042ED25  |.  8B45 D8       mov     eax, dword ptr [ebp-28]
0042ED28  |.  890D 10574D00 mov     dword ptr [4D5710], ecx
0042ED2E  |.  A3 08574D00   mov     dword ptr [4D5708], eax       ;  把当前断点地址保存到全局变量4D5708中
0042ED33  |.  891D 0C574D00 mov     dword ptr [4D570C], ebx
0042ED39  |>  8B55 A4       mov     edx, dword ptr [ebp-5C]
0042ED3C  |.  8B0A          mov     ecx, dword ptr [edx]

以上找到了回溯后的断点地址。然后下面是做一些别的操作,这里暂不予考虑。
这时因为触发了Int3异常,被调试程序断了下来,且已经回溯了地址,这时按下F9的话,OD就会重新跑起来,这时OD对用户设置的Int3断点又做了两件事:
1、恢复原有的指令,让被调试程序正确执行指令;
2、走完正确指令之后,再重新设置指令为0xCC;
在OD的函数Go中:
先通过Findthread获得线程结构体:
00434A45  |> \8B4D 08       mov     ecx, dword ptr [ebp+8]
00434A48  |.  51            push    ecx            ; ecx 是Go函数传入参数,为被调试线程的ID
00434A49  |.  E8 2A3F0400   call    _Findthread      ; 返回OD中t_thread线程结构体
00434A4E  |.  59            pop     ecx
00434A4F  |.  8945 DC       mov     dword ptr [ebp-24], eax ;把t_thread线程结构体的地址赋给ebp-24
然后通过线程结构体获得当前要执行的指令的地址(这个地址在前面已经用回溯法修正过了)
00434AB9  |.  8B99 18030000 mov     ebx, dword ptr [ecx+t_thread.th_reg.r_EIP]

00434B2A  |.  53            push    ebx       ; ebx = t_thread.th_reg.r_EIP
00434B2B  |.  E8 7C49FEFF   call    004194AC   ; 把Eip传给函数004194AC

在函数004194AC中,先获得Int3断点结构体数组:
004194EC  |> \56            push    esi             ;Eip
004194ED  |.  68 E17E4D00   push    004D7EE1       ; |Arg1 = 004D7EE1 ASCII "Table of breakpoints"
004194F2  |.  E8 19C00300   call    _Findsorteddata    ; 返回Int3断点结构体数组首地址
OD的Int3断点结构体中有断点处的原始指令码,用来恢复被调试程序原先的指令码。把该地址作为参数传给函数00418C4C
00419505  |.  50            push    eax
00419506  |.  8915 20D64C00 mov     dword ptr [4CD620], edx
0041950C  |.  E8 3BF7FFFF   call    00418C4C
在函数00418C4C中层层调用,最终是调用WriteProcessMemory函数恢复指令码的:
00461814  |> \6A 00         push    0                             ; /pBytesWritten = NULL
00461816  |.  A1 685A4D00   mov     eax, dword ptr [4D5A68]       ; |
0046181B  |.  8B55 10       mov     edx, dword ptr [ebp+10]       ; |
0046181E  |.  52            push    edx                           ; |BytesToWrite
0046181F  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]        ; |
00461822  |.  51            push    ecx                           ; |Buffer
00461823  |.  56            push    esi                           ; |Address
00461824  |.  50            push    eax                           ; |hProcess => 0000026C (window)
00461825  |.  E8 F8D90400   call    <jmp.&KERNEL32.WriteProcessMe>; \WriteProcessMemory

在运行完该指令之后,OD又把该指令码设为Int3断点,即0xCC
关键Call是函数41B5A4,在这个函数里,调用WriteMemory设置0xCC断点,而WriteMemory最终调用的还是上面所说的WriteProcessMemory(这里就不再详细描述了)
0041B6A4  |.  C60424 CC     |mov     byte ptr [esp], 0CC      ;直接设置0xCC断点
0041B6A8  |.  6A 02         |push    2                              ; /Arg4 = 00000002
0041B6AA  |.  6A 01         |push    1                              ; |Arg3 = 00000001
0041B6AC  |.  55            |push    ebp                            ; BreakPointAddress
0041B6AD  |.  8D5424 0C     |lea     edx, dword ptr [esp+C]             ; [esp+C] = 0xCC
0041B6B1  |.  52            |push    edx                            ; [edx] = 0xCC
0041B6B2  |.  E8 71600400   |call    _Writememory                    ; \_Writememory

通过以上方法,OD实现了在运行时动态处理Int3断点的功能。


                              武汉科锐学员: driverox
                                2008-5-19