这是分析报告的第六部分,请多多指教。
Ollydbg(以下均简称为OD)中对各类断点、各类热键以及菜单消息的处理都是通过一个不停循环的消息线程处理的,当然对于调试程序异常的处理也包括在里面。
在OD中该循环位于WinMain函数的中间部分,先说一下其流程,然后具体分析代码:
1. 得到开机到现在的时间,根据时间间隔更新日志文件,流程如下:
a) 检查是否创建了记录OD调试信息日志文件,若有创建继续检查距离上次更新是否超过30秒,若超过则将调试信息新增部分写入到文件中;
b) 检查是否创建了运行跟踪结果的日志文件,若有创建继续检查距离上次更新是否超过30秒,若超过则将运行跟踪结果新增部分写入到文件中;
c) 保存最后一次更新时间;
2. 检查是否需要刷新反汇编窗口、主窗口,若需要则检查刷新间隔是否超过0.5秒,超过则刷新,并记录该次刷新的时间,否则就不刷新;
3. 接受线程的消息队列,并将消息信息放入MSG结构体中,并检查消息是否接受成功,若失败跳过第4步对消息的处理;
4. 转换子窗体中的快捷键消息,若转换成功则跳过对其他消息的处理;
5. ……一般消息的判断以及消息收发,这里跳过,没有分析;
6. 对EXE、DLL做相应的检查以及设置(添加临时断点、添加插件栏);
7. 使用WaitForDebugEvent函数接收异常,若没有接收到则发送NULL消息,跳转到第20步;
8. 先让插件处理异常,然后检查接收到的异常信息中进程Id是否为调试进程的Id,异常码是否为EXCEPTION_DEBUG_EVENT,若不是则调用ContinueDebugEvent 继续调试程序,其ContinueStatus 参数设置为DBG_CONTINUE,跳转到第1步继续循环;这一步比较关键,当异常为调试异常时并没有调用ContinueDebugEvent函数让函数继续执行,这样调试程序就被断下了;
9. 修改调试程序的状态标识为被调试状态,调用函数检查int3断点,若是运行状态则设置断点;
10. 检查异常、线程的信息结构体,设置标识,调用一个函数处理所有的异常;
11. 检查更新线程信息,检查是否开了运行跟踪文件,若开了则更新文件,将调试程序状态设置回去;
12. 检查异常信息以及平台情况,做相应的错误处理;
13. 下面开始为对调试功能的处理,一共有9种情况(0-No animation、1-Animate into、Animate over、Execute till RET、Skip RET instruction、Execute till user code、Run trace in、Run trace over、Gracefully stop animation),有一个全局变量标识其状态;
14. 对Execute till RET(执行到返回)进行处理,检查调试寄存器信息,检查第一个机器码是否为C2(retn…)C3(retn)CA(retf…)CB(retf)或者CF(iretd),若不是则跳过对其处理的部分;若是则开始处理:
a) 调用_GO函数检查线程信息,设置断点,检查其合法性,根据检查结果设置提示信息;
b) 调整优先级,对调试程序结构体信息、OD界面显示信息以及插件信息的更新
c) 发送广播到子窗体,要求更新窗口内容,设置窗口获得焦点,最后返回到第1步
15. 对Execute till user code(执行到用户代码)进行处理,检查eip所在的模块是否为系统模块,若不是则调整其优先级,若是则跳过设置优先级;根据ebx判断是否调用函数设置所有断点;
16. 对Gracefully stop animation(停止自动跟踪)处理,检查线程信息,检查完毕后设置OD的界面,发送子窗体更新广播,更新插件;
17. 对于其他操作调用_GO函数进行处理,除了线程ID,其他参数都设0值;
18. 对No animation(无自动跟踪)进行处理,调用Set_all_Bpoint函数处理,然后跳转到第20步,函数中调用ContinueDebugEvent 函数继续调试程序,其ContinueStatus 参数设置为DBG_EXCEPTION_NOT_HANDLED;
19. ……部分省略,是一些对OD操作的处理,跟上面雷同,一般都是调用_GO函数执行代码;检查更新标识;用_Broadcast函数发送更新广播;检查是否有自动跟踪文件,对其进行更新等等;
20. 两个对系统时间全局变量的判断以及设置,更新窗口,跳转到循环体开始处
得到开机到现在的时间,检查是否创建了记录OD调试信息的日志文件以及运行跟踪结果的日志文件,根据结果进行相应的跳转:
00439077 > E8 566>call <jmp.&KERNEL32.GetTickCount>
0043907C . |8945 C>mov dword ptr [ebp-34], eax
0043907F . |833D E>cmp dword ptr [4D55E8], 0 ; 检查调试信息日志文件指针是否存在
00439086 . |75 09 jnz short 00439091 ; 存在则跳转到下面检查暂停时间
00439088 . |833D 4>cmp dword ptr [4D9E40], 0 ; 检查运行跟踪的文件指针是否存在
0043908F . |74 43 je short 004390D4 ; 存在则跳转到下面检查暂停时间
这里为检查时间处,根据时间更新文件:
00439091 > |A1 283>mov eax, dword ptr [4E3B28]
00439096 . |05 307>add eax, 7530 ; 上次更新时间增加30秒
0043909B . |3B45 C>cmp eax, dword ptr [ebp-34] ;与当前时间比较,检查是否间隔超过30秒
0043909E . |73 34 jnb short 004390D4 ; 超过则跳过文件的更新
004390A0 . |833D E>cmp dword ptr [4D55E8], 0 ; 检查调试信息日志文件指针是否存在
004390A7 . |74 0D je short 004390B6 ; 不存在跳过更新被调试的文件
更新调试信息日志文件:
004390A9 . |8B15 E>mov edx, dword ptr [4D55E8]
004390AF . |52 push edx
004390B0 . |E8 B3B>call 004A4F68 ; _fflush 更新调试信息日志文件
004390B5 . |59 pop ecx
检查运行跟踪文件指针是否存在,不存在则跳过更新运行跟踪文件:
004390B6 > |833D 4>cmp dword ptr [4D9E40], 0
004390BD . |74 0D je short 004390CC ; 不存在则跳过更新运行跟踪文件
004390BF . |8B0D 4>mov ecx, dword ptr [4D9E40]
004390C5 . |51 push ecx
004390C6 . |E8 9DB>call 004A4F68 ; _fflush 更新运行跟踪文件
004390CB . |59 pop ecx
将开机到当前的时间赋值给一个全局变量,保存文件最后一次更新时间:
004390CC > |8B45 C>mov eax, dword ptr [ebp-34]
004390CF . |A3 283>mov dword ptr [4E3B28], eax
检查是否需要刷新反汇编窗口,若不需要则跳过刷新:
004390D4 > |833D 3>cmp dword ptr [4E3B34], 0 ; [4E3B34] 反汇编窗口刷新标识
004390DB . |74 26 je short 00439103
检查刷新窗口的间隔时间是否超过0.5秒,若超过则刷新所有窗口,并设置刷新时间以及刷新标识:
004390DD . |8B55 C>mov edx, dword ptr [ebp-34]
004390E0 . |2B15 3>sub edx, dword ptr [4E3B38] ; 得到两次刷新窗口的时间间隔
004390E6 . |81FA F>cmp edx, 1F4 ; 比较时间间隔是否低于等于0.5秒
004390EC . |76 15 jbe short 00439103 ; 若低于0.5秒则跳过刷新
004390EE . |E8 D95>call _Redrawdisassembler ; 刷新反汇编窗口
004390F3 . |8B4D C>mov ecx, dword ptr [ebp-34]
004390F6 . |33C0 xor eax, eax
004390F8 . |890D 3>mov dword ptr [4E3B38], ecx ; 记录刷新最后时间
004390FE . |A3 343>mov dword ptr [4E3B34], eax ; 将刷新标识置零
检查主窗口更新标识,若不需要更新则将当前时间设置到记录主窗口更新时间的全局变量,然后跳过对主窗口的刷新:
00439103 > |833D C>cmp dword ptr [4D57C0], 0 ; 检查主窗口更新标识
0043910A . |74 09 je short 00439115
0043910C . |833D 3>cmp dword ptr [4E3B30], 0 ; 检查nNumber是否为0
00439113 . |75 0B jnz short 00439120
00439115 > |8B55 C>mov edx, dword ptr [ebp-34] ; 记录更新时间
00439118 . |8915 2>mov dword ptr [4E3B2C], edx
0043911E . |EB 55 jmp short 00439175 ; 跳过对主窗口的更新
检查距离上次刷新的时间间隔是否超过0.5秒,小于则跳过刷新:
00439120 > |8B0D 2>mov ecx, dword ptr [4E3B2C]
00439126 . |81C1 E>add ecx, 3E8
0043912C . |3B4D C>cmp ecx, dword ptr [ebp-34]
0043912F . |73 44 jnb short 00439175
检查nNumber是否超过50,小于则跳过更新:
00439131 . |833D 3>cmp dword ptr [4E3B30], 32
00439138 . |7C 2B jl short 00439165
间隔时间*1000/edx (edx > 50),最后用计算的值更新:
0043913A . |8B45 C>mov eax, dword ptr [ebp-34]
0043913D . |8B15 3>mov edx, dword ptr [4E3B30]
00439143 . |2B05 2>sub eax, dword ptr [4E3B2C]
00439149 . |50 push eax ; /Divisor
0043914A . |68 E80>push 3E8 ; |Multiplier = 3E8 (1000.)
0043914F . |52 push edx ; |Multiplicand => 0
00439150 . |E8 FB5>call <jmp.&KERNEL32.MulDiv> ; \MulDiv
00439155 . |50 push eax ; /Arg2
00439156 . |8D8E 6>lea ecx, dword ptr [esi+3162] ; |
0043915C . |51 push ecx ; |Arg1
0043915D . |E8 CA8>call _Flash ; \_Flash
00439162 . |83C4 0>add esp, 8
00439165 > |33C0 xor eax, eax
00439167 . |8B55 C>mov edx, dword ptr [ebp-34]
0043916A . |A3 303>mov dword ptr [4E3B30], eax ; 将除数置零
0043916F . |8915 2>mov dword ptr [4E3B2C], edx ; 将系统时间记录
接受主线程的消息队列,并将消息信息放入MSG结构体中,并检查消息是否接受成功,若失败跳过下面对消息的处理:
00439175 > |6A 01 push 1 ; /RemoveMsg = PM_REMOVE
00439177 . |6A 00 push 0 ; |MsgFilterMax = WM_NULL
00439179 . |6A 00 push 0 ; |MsgFilterMin = WM_NULL
0043917B . |6A 00 push 0 ; |hWnd = NULL
0043917D . |8D8D 3>lea ecx, dword ptr [ebp-9C8] ; |
00439183 . |51 push ecx ; |pMsg
00439184 . |E8 A56>call <jmp.&USER32.PeekMessageA> ; \PeekMessageA
00439189 . |85C0 test eax, eax
0043918B . |0F84 C>je 00439454
转换子窗口消息中的快捷键消息:
00439191 . |8D85 3>lea eax, dword ptr [ebp-9C8]
00439197 . |50 push eax ; /pMsg
00439198 . |8B15 8>mov edx, dword ptr [4D3B80] ; |
0043919E . |52 push edx ; |hClient => 00020762 (class='MDIClient',parent=000607B6)
0043919F . |E8 3E6>call <jmp.&USER32.TranslateMDISysA>; \TranslateMDISysAccel
004391A4 . |85C0 test eax, eax
004391A6 . |0F85 9>jnz 00439442
若子窗口有操作消息,则跳过对主窗体的消息的判断,对子窗口消息进行处理;下面是对一般消息的判断,处理还是在下面,这里跳过:
……
……
这里开始为对所有消息的处理:
00439454 > |833D 8>cmp dword ptr [4D5780], 0
0043945B . |0F84 9>je 004395F9
00439461 . |833D 1>cmp dword ptr [4E3610], 0 ; (initial cpu selection)
00439468 . |0F84 8>je 004395F9
0043946E . |E8 F95>call 0045F36C ; 得到加载的dll模块信息
00439473 . |E8 9C7>call _Listmemory ; 检查内存链表
将模块信息给edi寄存器,下面对其进行检查,DLL以及EXE程序分开检查:
00439478 . |8B3D 1>mov edi, dword ptr [4D7218] ; edi : pModule
检查是否为DLL,若是则初始化参数,并跳转到条件判断处,用一个循环体进行判断:
0043947E . |833D A>cmp dword ptr [4D6EA0], 0
00439485 . |74 7E je short 00439505
00439487 . |85FF test edi, edi
00439489 . |74 7A je short 00439505
0043948B . |33DB xor ebx, ebx
0043948D . |EB 6E jmp short 004394FD
若是DLL则用一个循环体检查调试的模块是否是加载的模块,若是则在入口设置int3临时断点:
0043948F > |8D049B lea eax, dword ptr [ebx+ebx*4]
00439492 . |8D0480 lea eax, dword ptr [eax+eax*4]
00439495 . |8D0480 lea eax, dword ptr [eax+eax*4]
00439498 . |8D04C0 lea eax, dword ptr [eax+eax*8]
0043949B . |F64407>test byte ptr [edi+eax+8], 10 ; 检查模块是否需要记录
004394A0 . |75 5A jnz short 004394FC ; 不需要则遍历下个模块
004394A2 . |8D149B lea edx, dword ptr [ebx+ebx*4]
004394A5 . |8D1492 lea edx, dword ptr [edx+edx*4]
004394A8 . |8D1492 lea edx, dword ptr [edx+edx*4]
004394AB . |8D14D2 lea edx, dword ptr [edx+edx*8]
004394AE . |03D7 add edx, edi
004394B0 . |83C2 5>add edx, 50 ; 检查模块路径名与加载的DLL是否相同
004394B3 . |52 push edx ; /Arg2
004394B4 . |68 805>push 004D5B80 ; |
004394B9 . |E8 FEA>call 004A38BC ; \_stricmp
004394BE . |83C4 0>add esp, 8
004394C1 . |85C0 test eax, eax
004394C3 . |75 37 jnz short 004394FC ; 不同则遍历下个模块
004394C5 . |8D0C9B lea ecx, dword ptr [ebx+ebx*4]
004394C8 . |8D0C89 lea ecx, dword ptr [ecx+ecx*4]
004394CB . |8D0C89 lea ecx, dword ptr [ecx+ecx*4]
004394CE . |8D0CC9 lea ecx, dword ptr [ecx+ecx*8]
004394D1 . |837C0F>cmp dword ptr [edi+ecx+28], 0 ; 检查模块入口点是否存在
004394D6 . |74 2D je short 00439505 ; 不存在则跳出循环
004394D8 . |8D049B lea eax, dword ptr [ebx+ebx*4]
004394DB . |6A 00 push 0 ; /Arg4 = 00000000
004394DD . |6A 00 push 0 ; |Arg3 = 00000000
004394DF . |68 000>push 800 ; |Arg2 = 00000800 临时断点
004394E4 . |8D0480 lea eax, dword ptr [eax+eax*4] ; |
004394E7 . |8D0480 lea eax, dword ptr [eax+eax*4] ; |
004394EA . |8D04C0 lea eax, dword ptr [eax+eax*8] ; |
004394ED . |8B5407>mov edx, dword ptr [edi+eax+28] ; |
004394F1 . |52 push edx ; |Arg1
004394F2 . |E8 690>call _Setbreakpointext ; \_Setbreakpointext
004394F7 . |83C4 1>add esp, 10
004394FA . |EB 09 jmp short 00439505
004394FC > |43 inc ebx
这里为循环体判断处:
004394FD > |3B1D 0>cmp ebx, dword ptr [4D7200]
00439503 .^|7C 8A jl short 0043948F
下面为对EXE程序的检查,跳转到条件判断处,用一个循环体进行判断,设置工具栏:
00439505 > |833D 5>cmp dword ptr [4D7350], 0
0043950C . |0F84 B>je 004395CB
00439512 . |85FF test edi, edi ; 检查模块信息是否存在
00439514 . |0F84 B>je 004395CB
0043951A . |33DB xor ebx, ebx
0043951C . |EB 14 jmp short 00439532
0043951E > |8D049B lea eax, dword ptr [ebx+ebx*4]
00439521 . |8D0480 lea eax, dword ptr [eax+eax*4]
00439524 . |8D0480 lea eax, dword ptr [eax+eax*4]
00439527 . |8D04C0 lea eax, dword ptr [eax+eax*8]
0043952A . |F64407>test byte ptr [edi+eax+8], 10 ; 检查模块是否需要记录
0043952F . |74 09 je short 0043953A
00439531 . |43 inc ebx
00439532 > |3B1D 0>cmp ebx, dword ptr [4D7200]
00439538 .^|7C E4 jl short 0043951E
0043953A > |3B1D 0>cmp ebx, dword ptr [4D7200]
00439540 . |7D 6B jge short 004395AD
00439542 . |6A 00 push 0 ; /Arg1 = 00000000
00439544 . |E8 47A>call _Suspendprocess ; \_Suspendprocess 暂停线程
00439549 . |59 pop ecx
0043954A . |833D 2>cmp dword ptr [4DE924], 0
00439551 . |74 05 je short 00439558
00439553 . |E8 58E>call 00497BB0
00439558 > |833D 1>cmp dword ptr [4E2F10], 0
0043955F . |74 05 je short 00439566
00439561 . |E8 223>call 0049CD88
00439566 > |8D96 8>lea edx, dword ptr [esi+318E]
0043956C . |52 push edx ; /Arg2
0043956D . |6A 00 push 0 ; |Arg1 = 00000000
0043956F . |E8 BC8>call _Message ; \_Message
00439574 . |83C4 0>add esp, 8
00439577 . |33DB xor ebx, ebx
00439579 . |EB 25 jmp short 004395A0
0043957B > |8D049B lea eax, dword ptr [ebx+ebx*4]
0043957E . |8D0480 lea eax, dword ptr [eax+eax*4]
00439581 . |8D0480 lea eax, dword ptr [eax+eax*4]
00439584 . |8D04C0 lea eax, dword ptr [eax+eax*8]
00439587 . |F64407>test byte ptr [edi+eax+8], 10
0043958C . |74 11 je short 0043959F
0043958E . |8D149B lea edx, dword ptr [ebx+ebx*4]
00439591 . |8D1492 lea edx, dword ptr [edx+edx*4]
00439594 . |8D1492 lea edx, dword ptr [edx+edx*4]
00439597 . |8D14D2 lea edx, dword ptr [edx+edx*8]
0043959A . |836417>and dword ptr [edi+edx+8], FFFFFF>
0043959F > |43 inc ebx
004395A0 > |3B1D 0>cmp ebx, dword ptr [4D7200]
004395A6 .^|7C D3 jl short 0043957B
004395A8 . |E8 9F6>call 0046044C ; 设置工具栏
004395AD > |33DB xor ebx, ebx
004395AF . |EB 12 jmp short 004395C3
004395B1 > |8D049B lea eax, dword ptr [ebx+ebx*4]
004395B4 . |8D0480 lea eax, dword ptr [eax+eax*4]
004395B7 . |8D0480 lea eax, dword ptr [eax+eax*4]
004395BA . |8D04C0 lea eax, dword ptr [eax+eax*8]
004395BD . |834C07>or dword ptr [edi+eax+8], 10 ; 检查模块是否需要记录
004395C2 . |43 inc ebx
这里为循环体判断处:
004395C3 > |3B1D 0>cmp ebx, dword ptr [4D7200]
004395C9 .^|7C E6 jl short 004395B1
设置edi为线程信息,检查线程信息是否存在,循环遍历活动线程,将其激活:
004395CB > |8B3D B>mov edi, dword ptr [4D7DB0] ; pThreadInfo
004395D1 . |85FF test edi, edi
004395D3 . |74 1C je short 004395F1
004395D5 . |33DB xor ebx, ebx
004395D7 . |EB 10 jmp short 004395E9
004395D9 > |8B47 0>mov eax, dword ptr [edi+C]
004395DC . |50 push eax ; /hThread
004395DD . |E8 925>call <jmp.&KERNEL32.ResumeThread> ; \ResumeThread
004395E2 . |43 inc ebx
004395E3 . |81C7 6>add edi, 66C
循环体判断处,检查线程是否遍历完:
004395E9 > |3B1D 9>cmp ebx, dword ptr [4D7D98]
004395EF .^|7C E8 jl short 004395D9
根据线程信息设置标识:
004395F1 > |33D2 xor edx, edx
004395F3 . |8915 1>mov dword ptr [4E3610], edx
检查被调试程序是否运行状态,若不是则遍历所有插件,用其处理异常,然后跳过运行时的处理:
004395F9 > |833D 5>cmp dword ptr [4D5A5C], 3
00439600 . |74 14 je short 00439616
00439602 . |6A 00 push 0 ; /Arg1 = 00000000
00439604 . |E8 43D>call 00496B4C ; \Plugin_SetDebugEvent
00439609 . |59 pop ecx
0043960A . |6A 01 push 1 ; /Timeout = 1. ms
0043960C . |E8 AB5>call <jmp.&KERNEL32.Sleep> ; \Sleep
00439611 . |E9 D80>jmp 0043A2EE
使用WaitForDebugEvent接收异常,并检查是否接收到异常,若接收到异常则跳过下面的处理:
00439616 > |6A 00 push 0 ; /Timeout = 0. ms
00439618 . |68 145>push 004D5714 ; |pDebugEvent = Ollydbg.004D5714
0043961D . |E8 E85>call <jmp.&KERNEL32.WaitForDebugEv>; \WaitForDebugEvent
00439622 . |85C0 test eax, eax
00439624 . |75 44 jnz short 0043966A
检查标识以及系统时间,根据判断结构处理,发送WM_NULL消息,设置标识为0;或者跳过这个处理让插件处理异常:
00439626 . |833D 5>cmp dword ptr [4E3B54], 0
0043962D . |74 27 je short 00439656
0043962F . |8B0D 5>mov ecx, dword ptr [4E3B58]
00439635 . |83C1 6>add ecx, 64
00439638 . |3B4D C>cmp ecx, dword ptr [ebp-34]
0043963B . |73 19 jnb short 00439656
0043963D . |6A 00 push 0 ; /lParam = 0
0043963F . |6A 00 push 0 ; |wParam = 0
00439641 . |6A 00 push 0 ; |Message = WM_NULL
00439643 . |A1 5C3>mov eax, dword ptr [4E3B5C] ; |
00439648 . |50 push eax ; |ThreadId => 340
00439649 . |E8 F25>call <jmp.&USER32.PostThreadMessag>; \PostThreadMessageA
0043964E . |33D2 xor edx, edx
00439650 . |8915 5>mov dword ptr [4E3B54], edx
让插件处理0号异常然后跳过处理下面的处理:
00439656 > |6A 00 push 0 ; /Arg1 = 00000000
00439658 . |E8 EFD>call 00496B4C ; \ Plugin_SetDebugEvent
0043965D . |59 pop ecx
0043965E . |6A 00 push 0 ; /Timeout = 0. ms
00439660 . |E8 575>call <jmp.&KERNEL32.Sleep> ; \Sleep
00439665 . |E9 840>jmp 0043A2EE
将异常事件压栈,让插件处理异常:
0043966A > |68 145>push 004D5714 ; /Arg1
0043966F . |E8 D8D>call 00496B4C ; \Plugin_SetDebugEvent
00439674 . |59 pop ecx
检查是否是被调试程序所报的异常,若不是则设置相关的异常信息,检查异常事件是否为调试异常:
00439675 . |8B0D 1>mov ecx, dword ptr [4D5718] ; DebugEvent.dwProcessId
0043967B . |3B0D 7>cmp ecx, dword ptr [4D5A70]
00439681 . |74 56 je short 004396D9
00439683 . |A1 185>mov eax, dword ptr [4D5718]
00439688 . |50 push eax ; /Arg5 => 00000BA8
00439689 . |8B15 1>mov edx, dword ptr [4D5714] ; |
0043968F . |52 push edx ; |Arg4 => 00000001
00439690 . |8D8E 8>lea ecx, dword ptr [esi+D86] ; |
00439696 . |51 push ecx ; |Arg3
00439697 . |6A 00 push 0 ; |Arg2 = 00000000
00439699 . |6A 00 push 0 ; |Arg1 = 00000000
0043969B . |E8 6C0>call _Addtolist ; \_Addtolist
004396A0 . |83C4 1>add esp, 14
004396A3 . |833D 1>cmp dword ptr [4D5714], 1 ; EXCEPTION_DEBUG_EVENT
004396AA . |75 10 jnz short 004396BC ; 不是则跳转
004396AC . |833D 7>cmp dword ptr [4D5770], 0 ; STATUS_WAIT_0
004396B3 . |74 07 je short 004396BC ; 若是则跳转
设置ContinueStatus为DBG_EXCEPTION_NOT_HANDLED(不忽略异常):
004396B5 . |BB 010>mov ebx, 80010001
004396BA . |EB 05 jmp short 004396C1 ;
设置ContinueStatus为DBG_CONTINUE(忽略异常继续执行):
004396BC > |BB 020>mov ebx, 10002
调用ContinueDebugEvent让调试程序继续执行,跳转到最上面继续接收消息:
004396C1 > |53 push ebx ; /ContinueStatus
004396C2 . |A1 1C5>mov eax, dword ptr [4D571C] ; |
004396C7 . |50 push eax ; |ThreadId => 340
004396C8 . |8B15 1>mov edx, dword ptr [4D5718] ; |
004396CE . |52 push edx ; |ProcessId => BA8
004396CF . |E8 EA5>call <jmp.&KERNEL32.ContinueDebugE>; \ContinueDebugEvent
004396D4 .^ E9 9EF>jmp 00439077
将调试程序的状态赋值给一个局部变量,然后将其设置为STAT_EVENT,即进程暂停、被调试;并根据int3断点表设置断点:
004396D9 > |8B0D 5>mov ecx, dword ptr [4D5A5C]
004396DF . |C705 5>mov dword ptr [4D5A5C], 2 ; STAT_EVENT
004396E9 . |33C0 xor eax, eax
004396EB . |C705 F>mov dword ptr [4D56FC], 1
004396F5 . |A3 745>mov dword ptr [4D5774], eax
004396FA . |894D B>mov dword ptr [ebp-50], ecx
004396FD . |8B15 3>mov edx, dword ptr [4D8134]
00439703 . |8915 0>mov dword ptr [4D5700], edx
00439709 . |E8 961>call 0041B5A4
检查模块数量,若为0则跳过下面的取得模块信息部分:
0043970E . |833D 4>cmp dword ptr [4D7348], 0
00439715 . |74 11 je short 00439728
00439717 . |E8 505>call 0045F36C ; 得到模块信息
0043971C . |E8 F37>call _Listmemory ; 检查内存链表
00439721 . |E8 463>call 0042D56C ; 清空临时信息表
00439726 . |EB 3C jmp short 00439764
检查线程、异常相关信息,设置标识:
00439728 > |833D 2>cmp dword ptr [4D812C], 0
0043972F . |74 29 je short 0043975A
00439731 . |833D 9>cmp dword ptr [4D7D98], 1 ; 线程数量是否超过1
00439738 . |7F 20 jg short 0043975A
0043973A . |833D 3>cmp dword ptr [4D8130], 0 ; 异常码ExceptionCode是否存在
00439741 . |74 17 je short 0043975A
00439743 . |833D 1>cmp dword ptr [4D5714], 1 ;异常事件是否为EXCEPTION_DEBUG_EVENT
0043974A . |75 0E jnz short 0043975A
0043974C . |8B0D 2>mov ecx, dword ptr [4D572C]
00439752 . |3B0D 3>cmp ecx, dword ptr [4D8130] ; ecx :ExceptionCode
00439758 . |74 0A je short 00439764
0043975A > |C705 7>mov dword ptr [4D7C7C], 1
初始化几个变量,调用函数处理异常,即是在内存断点的处理中提到的那个函数:
00439764 > |33C0 xor eax, eax
00439766 . |33D2 xor edx, edx
00439768 . |A3 308>mov dword ptr [4D8130], eax ; ExceptionAddress
0043976D . |8D4D A>lea ecx, dword ptr [ebp-54] ; pReg
00439770 . |C605 2>mov byte ptr [4E3A20], 0 ; rtrace_Buf
00439777 . |8915 5>mov dword ptr [4E3B54], edx
0043977D . |51 push ecx ; /Arg1
0043977E . |E8 4D5>call 0042EBD0 ; \CheckDebugEvent
00439783 . |59 pop ecx
检查更新线程信息,若开了运行跟踪,则还要将运行跟踪的信息写入文件:
00439784 . |6A 00 push 0 ; /Arg2 = 00000000
00439786 . |8BD8 mov ebx, eax ; |
00439788 . |8B45 A>mov eax, dword ptr [ebp-54] ; |
0043978B . |50 push eax ; |Arg1
0043978C . |E8 871>call 0048AF18 ; \check_ThreadInfo
00439791 . |83C4 0>add esp, 8
00439794 . |803D 2>cmp byte ptr [4E3A20], 0
0043979B . |74 0B je short 004397A8
0043979D . |68 203>push 004E3A20
004397A2 . |E8 A51>call 0048AE4C set_file_rtrace
004397A7 . |59 pop ecx
将调试程序状态重新设置回去:
004397A8 > |33D2 xor edx, edx
004397AA . |8B4D B>mov ecx, dword ptr [ebp-50]
004397AD . |8915 5>mov dword ptr [4D8D5C], edx
004397B3 . |890D 5>mov dword ptr [4D5A5C], ecx
检查异常信息,并做相应的跳转:
004397B9 . |833D 1>cmp dword ptr [4D5714], 1 ; 异常事件是否为EXCEPTION_DEBUG_EVENT
004397C0 . |75 42 jnz short 00439804
004397C2 . |833D 7>cmp dword ptr [4D5770], 0 ; dwFirstChance == 0 ??
004397C9 . |75 39 jnz short 00439804
004397CB . |833D D>cmp dword ptr [4D36D8], 2 ; 程序的执行平台是否为windows
004397D2 . |74 0C je short 004397E0
004397D4 . |813D 2>cmp dword ptr [4D572C], 80000000 ; 异常地址是否大于80000000h
004397DE . |73 24 jnb short 00439804
这里为程序平台为windows的处理,弹出窗口,调整优先级:
004397E0 > |833D 8>cmp dword ptr [4D578C], 0
004397E7 . |75 11 jnz short 004397FA
004397E9 . |8D86 A>lea eax, dword ptr [esi+31A5]
004397EF . |50 push eax ; /Arg2
004397F0 . |6A 00 push 0 ; |Arg1 = 00000000
004397F2 . |E8 397>call _Message ; \_Message
004397F7 . |83C4 0>add esp, 8
004397FA > |6A 00 push 0 ; /Arg1 = 00000000
004397FC . |E8 D78>call _Animate ; \_Animate
00439801 . |59 pop ecx
00439802 . |33DB xor ebx, ebx
检查调试操作是否为Execute till RET(执行到返回),若不是则跳过该处理部分:
00439804 > |833D C>cmp dword ptr [4D57CC], 3 ; [4D57CC] == 3 Execute till RET
0043980B . |0F85 1>jnz 00439928
检查寄存器信息是否为空,若为空也跳过该处理部分:
00439811 . |837D A>cmp dword ptr [ebp-54], 0 ; [ebp-54] : pReg
00439815 . |0F84 0>je 00439928
将寄存器的EIP所在的机器码读取出来:
0043981B . |8D85 9>lea eax, dword ptr [ebp-468]
00439821 . |50 push eax ; /Arg2
00439822 . |8B55 A>mov edx, dword ptr [ebp-54] ; |
00439825 . |8B4A 2>mov ecx, dword ptr [edx+2C] ; |
00439828 . |51 push ecx ; |Arg1
00439829 . |E8 567>call _Readcommand ; \_Readcommand
0043982E . |83C4 0>add esp, 8
检查第一个机器码是否为C2(retn…)C3(retn)CA(retf…)CB(retf)或者CF(iretd),若不是则跳过对其处理的部分:
00439831 . |85C0 test eax, eax
00439833 . |0F86 E>jbe 00439928
00439839 . |33C0 xor eax, eax
0043983B . |8A85 9>mov al, byte ptr [ebp-468]
00439841 . |25 F60>and eax, 0F6
00439846 . |3D C20>cmp eax, 0C2
0043984B . |74 14 je short 00439861
0043984D . |33D2 xor edx, edx
0043984F . |8A95 9>mov dl, byte ptr [ebp-468]
00439855 . |81FA C>cmp edx, 0CF
0043985B . |0F85 C>jnz 00439928
检查线程信息:
00439861 > |833D C>cmp dword ptr [4D57C8], 0
00439868 . |0F84 A>je 0043991A
0043986E . |6A 00 push 0 ; /Arg2 = 00000000
00439870 . |8B4D A>mov ecx, dword ptr [ebp-54] ; |
00439873 . |51 push ecx ; |Arg1
00439874 . |E8 9F1>call 0048AF18 ; \check_ThreadInfo
00439879 . |83C4 0>add esp, 8
0043987C . |FF05 3>inc dword ptr [4E3B30]
调用OD导出函数_GO使调试程序继续执行,该函数里面一样对断点进行了设置以及处理,最后检查返回值是否为0,即标识执行成功,若执行失败则跳过:
00439882 . |6A 01 push 1 ; /Arg5 = 00000001
00439884 . |6A 00 push 0 ; |Arg4 = 00000000
00439886 . |6A 02 push 2 ; |Arg3 = 00000002
00439888 . |6A 00 push 0 ; |Arg2 = 00000000
0043988A . |6A 00 push 0 ; |Arg1 = 00000000
0043988C . |E8 83B>call _Go ; \_Go
00439891 . |83C4 1>add esp, 14
00439894 . |85C0 test eax, eax
00439896 . |74 75 je short 0043990D
调整优先级,对调试程序结构体信息、OD界面显示信息以及插件信息的更新:
00439898 . |6A 00 push 0 ; /Arg1 = 00000000
0043989A . |E8 398>call _Animate ; \_Animate
0043989F . |59 pop ecx
004398A0 . |E8 0B4>call 0042E2B0 ; update_ThreadRegInfo
004398A5 . |6A 01 push 1 ; /Arg1 = 00000001
004398A7 . |E8 CC8>call 00431978 ; \ update_subwin
004398AC . |59 pop ecx
004398AD . |68 145>push 004D5714 ; /Arg4 = 004D5714
004398B2 . |8B45 A>mov eax, dword ptr [ebp-54] ; |
004398B5 . |8B15 7>mov edx, dword ptr [4D5774] ; |
004398BB . |50 push eax ; |Arg3
004398BC . |83CA 0>or edx, 0 ; |
004398BF . |6A 00 push 0 ; |Arg2 = 00000000
004398C1 . |52 push edx ; |Arg1
004398C2 . |E8 BDD>call 00496B84 ; \update_Plugin
004398C7 . |83C4 1>add esp, 10
004398CA . |85C0 test eax, eax
004398CC .^ 0F85 A>jnz 00439077
004398D2 . |68 050>push 105 ; /Arg5 = 00000105
004398D7 . |6A 00 push 0 ; |Arg4 = 00000000
004398D9 . |6A 00 push 0 ; |Arg3 = 00000000
004398DB . |6A 00 push 0 ; |Arg2 = 00000000
004398DD . |8B0D 1>mov ecx, dword ptr [4D571C] ; |
004398E3 . |51 push ecx ; |Arg1 => 00000340
004398E4 . |E8 2F3>call _Setcpu ; \_Setcpu
004398E9 . |83C4 1>add esp, 14
发送广播到子窗体,要求更新窗口内容,设置窗口获得焦点,最后返回到消息循环开始处继续:
004398EC . |6A 00 push 0 ; /Arg3 = 00000000
004398EE . |6A 00 push 0 ; |Arg2 = 00000000
004398F0 . |68 740>push 474 ; |Arg1 = 00000474
004398F5 . |E8 7A0>call _Broadcast ; \_Broadcast
004398FA . |83C4 0>add esp, 0C
004398FD . |A1 7C3>mov eax, dword ptr [4D3B7C]
00439902 . |50 push eax ; /hWnd
00439903 . |E8 925>call <jmp.&USER32.SetForegroundWin>; \SetForegroundWindow
00439908 .^ E9 6AF>jmp 00439077
调整优先级,返回到消息循环开始处继续:
0043990D > |6A 04 push 4 ; /Arg1 = 00000004
0043990F . |E8 C48>call _Animate ; \_Animate
00439914 . |59 pop ecx
00439915 .^ E9 5DF>jmp 00439077
检查标识,调整优先级:
0043991A > |85DB test ebx, ebx
0043991C . |7E 02 jle short 00439920
0043991E . |33DB xor ebx, ebx
00439920 > |6A 00 push 0
00439922 . |E8 B18>call _Animate
00439927 . |59 pop ecx
检查调试操作是否为Execute till user code(执行到用户代码),若不是则跳过该处理部分:
00439928 > |833D C>cmp dword ptr [4D57CC], 5
0043992F . |75 30 jnz short 00439961
一样是检查EIP寄存器信息是否存在,若不存在跳过该处理部分:
00439931 . |837D A>cmp dword ptr [ebp-54], 0
00439935 . |74 2A je short 00439961
找到EIP所在的模块,检查是否是系统模块,若是则跳到该处理结束处:
00439937 . |8B45 A>mov eax, dword ptr [ebp-54]
0043993A . |8B50 2>mov edx, dword ptr [eax+2C]
0043993D . |52 push edx ; /Arg1
0043993E . |E8 D54>call _Findmodule ; \_Findmodule
00439943 . |59 pop ecx
00439944 . |8BF8 mov edi, eax
00439946 . |85C0 test eax, eax
00439948 . |74 09 je short 00439953
0043994A . |83BF F>cmp dword ptr [edi+3F5], 0 ; issystemdll == 0 ??
00439951 . |75 0E jnz short 00439961
根据ebx标识设置优先级,以及进行相应的跳转:
00439953 > |85DB test ebx, ebx
00439955 . |7E 02 jle short 00439959
00439957 . |33DB xor ebx, ebx
00439959 > |6A 00 push 0
0043995B . |E8 788>call _Animate
00439960 . |59 pop ecx
00439961 > |83FB 0>cmp ebx, 2
00439964 . |75 24 jnz short 0043998A
检查调试操作是否为Gracefully stop animation(停止自动跟踪),若不是则跳过该处理部分,该部分的处理就是调用OD的导出函数_GO,跳转到对调试信息处理的结束判断处,不再做其他检查:
00439966 . |833D C>cmp dword ptr [4D57CC], 8
0043996D . |74 1B je short 0043998A
0043996F . |6A 00 push 0 ; /Arg5 = 00000000
00439971 . |6A 00 push 0 ; |Arg4 = 00000000
00439973 . |6A 00 push 0 ; |Arg3 = 00000000
00439975 . |6A 00 push 0 ; |Arg2 = 00000000
00439977 . |A1 1C5>mov eax, dword ptr [4D571C] ; |
0043997C . |50 push eax ; |Arg1 => 00000340
0043997D . |E8 92B>call _Go ; \_Go
00439982 . |83C4 1>add esp, 14
00439985 . |E9 640>jmp 0043A2EE
根据ebx标识跳过下面的处理函数:
0043998A > |83FB 0>cmp ebx, 1
0043998D . |75 41 jnz short 004399D0
检查调试操作是否为No animation(无自动跟踪),若不是则跳过该处理部分:
0043998F . |833D C>cmp dword ptr [4D57CC], 0
00439996 . |74 09 je short 004399A1
下面对一些参数进行检查,根据检查结果设置ebx,以及是否设置所有的断点:
00439998 . |833D 1>cmp dword ptr [4D5714], 1 ;检查异常码是否为EXCEPTION_DEBUG_EVENT
0043999F . |74 2F je short 004399D0
004399A1 > |6A 00 push 0
004399A3 . |6A 00 push 0
004399A5 . |833D F>cmp dword ptr [4D56FC], 2
004399AC . |74 0D je short 004399BB
004399AE . |833D F>cmp dword ptr [4D56FC], 3
004399B5 . |74 04 je short 004399BB
004399B7 . |33D2 xor edx, edx
004399B9 . |EB 05 jmp short 004399C0
004399BB > |BA 010>mov edx, 1
004399C0 > |52 push edx ; |Arg2
004399C1 . |6A FF push -1 ; |Arg1 = FFFFFFFF
004399C3 . |E8 687>call 00431430 ; \Set_all_Bpoint
004399C8 . |83C4 1>add esp, 10
004399CB . |E9 1E0>jmp 0043A2EE ; 跳转到消息循环体最后
下面一块是对线程信息的检查,检查完毕后设置OD的界面,发送子窗体更新广播,更新插件:
004399D0 > |43 inc ebx
004399D1 . |0F85 9>jnz 00439A71
004399D7 . |E8 386>call _Listmemory
004399DC . |833D 2>cmp dword ptr [4DE924], 0
004399E3 . |74 05 je short 004399EA
004399E5 . |E8 C6E>call 00497BB0 ; update_topwin
004399EA > |833D 1>cmp dword ptr [4E2F10], 0
004399F1 . |74 05 je short 004399F8
004399F3 . |E8 903>call 0049CD88
004399F8 > |837D A>cmp dword ptr [ebp-54], 0
004399FC . |74 0E je short 00439A0C
004399FE . |6A 00 push 0 ; /Arg2 = 00000000
00439A00 . |8B4D A>mov ecx, dword ptr [ebp-54] ; |
00439A03 . |51 push ecx ; |Arg1
00439A04 . |E8 0F1>call 0048AF18 ; \ update _ThreadInfo
00439A09 . |83C4 0>add esp, 8
00439A0C > |68 010>push 101 ; /Arg5 = 00000101
00439A11 . |6A 00 push 0 ; |Arg4 = 00000000
00439A13 . |6A 00 push 0 ; |Arg3 = 00000000
00439A15 . |6A 00 push 0 ; |Arg2 = 00000000
00439A17 . |A1 1C5>mov eax, dword ptr [4D571C] ; |
00439A1C . |50 push eax ; |Arg1 => 00000340
00439A1D . |E8 F63>call _Setcpu ; \_Setcpu
00439A22 . |83C4 1>add esp, 14
00439A25 . |6A 00 push 0 ; /Arg3 = 00000000
00439A27 . |6A 00 push 0 ; |Arg2 = 00000000
00439A29 . |68 740>push 474 ; |Arg1 = 00000474
00439A2E . |E8 410>call _Broadcast ; \_Broadcast
00439A33 . |83C4 0>add esp, 0C
00439A36 . |8B15 7>mov edx, dword ptr [4D3B7C]
00439A3C . |52 push edx ; /hWnd
00439A3D . |E8 585>call <jmp.&USER32.SetForegroundWin>; \SetForegroundWindow
00439A42 . |33C9 xor ecx, ecx
00439A44 . |33C0 xor eax, eax
00439A46 . |890D 3>mov dword ptr [4E3B30], ecx
00439A4C . |A3 343>mov dword ptr [4E3B34], eax
00439A51 . |6A 04 push 4 ; /Arg1 = 00000004
00439A53 . |E8 207>call 00431978 ; \update_subwin
00439A58 . |59 pop ecx
00439A59 . |68 145>push 004D5714 ; /Arg4 = 004D5714
00439A5E . |6A 00 push 0 ; |Arg3 = 00000000
00439A60 . |6A 00 push 0 ; |Arg2 = 00000000
00439A62 . |6A 02 push 2 ; |Arg1 = 00000002
00439A64 . |E8 1BD>call 00496B84 ; \update_Plugin
00439A69 . |83C4 1>add esp, 10
00439A6C . |E9 7D0>jmp 0043A2EE
下面省略部分是一些对OD操作的处理,跟上面雷同,一般都是调用_GO函数执行代码;检查更新一些标识;用_Broadcast函数发送更新广播;检查是否有自动跟踪文件,对其进行更新等等:
……
……
两个对系统时间全局变量的判断以及设置,更新窗口,跳转到循环体开始处:
0043A2EE > |833D 1>cmp dword ptr [4E3A1C], 0
0043A2F5 . |74 0A je short 0043A301
0043A2F7 . |A1 1C3>mov eax, dword ptr [4E3A1C]
0043A2FC . |3B45 C>cmp eax, dword ptr [ebp-34]
0043A2FF . |72 1C jb short 0043A31D
0043A301 > |833D 1>cmp dword ptr [4E3814], 0
0043A308 .^ 0F84 6>je 00439077
0043A30E . |8B15 1>mov edx, dword ptr [4E3814]
0043A314 . |3B55 C>cmp edx, dword ptr [ebp-34]
0043A317 .^ 0F83 5>jnb 00439077
0043A31D > |E8 3E7>call 00431960
0043A322 .^\E9 50E>jmp 00439077
武汉科锐学员: angelqkm
2008-5-29
- 标 题:OllyDBG分析报告系列(6)---OD异常处理机制
- 作 者:angelqkm
- 时 间:2008-05-29 20:26
- 链 接:http://bbs.pediy.com/showthread.php?t=65686