标 题: 【原创】OllyDBG分析报告系列(1)---Int3断点 作 者: driverox 时 间: 2008-05-19,16:45:43 链 接: http://bbs./showthread.php?t=65094 最近学习逆向,对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 |
|
来自: herowuking > 《Cracker》