上一篇文章 中, 说了定时器的使用, 程序中用一个对话框来显示一个实时时钟, 本节主要来看看这个程序的反汇编代码。
返汇编代码和汇编代码一样, 也主要分成2个部分, 一个是程序入口, 利用对话框资源来显示一个对话框;另一个是对话框的窗口过程,处理了3个我们感兴趣的消息。
下面是反汇编代码:
00401000 /. 55 push ebp
00401001 |. 8BEC mov ebp, esp
00401003 |. 81C4 70FFFFFF add esp, -90
保存堆栈寄存器,并预留局部变量空间
00401009 |. 8B45 0C mov eax, dword ptr [ebp+C]
mov eax, uMsg
0040100C |. 3D 13010000 cmp eax, 113 ; Switch (cases 10..113)
cmp eax, WM_TIMER 看看是不是WM_TIMER消息
00401011 |. 75 51 jnz short 00401064
不是, 则转入下一个消息的比较
00401013 |. 8D45 F0 lea eax, dword ptr [ebp-10] ; Case 113 (WM_TIMER) of switch
lea eax, @stTime 获取结构@stTime地址
0040100C
00401016 |. 50 push eax ; /pLocaltime
00401017 |. E8 E2000000 call <jmp.&kernel32.GetLocalTime> ; /GetLocalTime
将结构@stTime地址作为参数, 调用GetLocalTime获取当前的时间
0040101C |. 0FB745 FC movzx eax, word ptr [ebp-4]
00401020 |. 50 push eax ; /<%02d>
00401021 |. 0FB745 FA movzx eax, word ptr [ebp-6] ; |
00401025 |. 50 push eax ; |<%02d>
00401026 |. 0FB745 F8 movzx eax, word ptr [ebp-8] ; |
0040102A |. 50 push eax ; |<%02d>
0040102B |. 0FB745 F6 movzx eax, word ptr [ebp-A] ; |
0040102F |. 50 push eax ; |<%02d>
00401030 |. 0FB745 F2 movzx eax, word ptr [ebp-E] ; |
00401034 |. 50 push eax ; |<%02d>
00401035 |. 0FB745 F0 movzx eax, word ptr [ebp-10] ; |
00401039 |. 50 push eax ; |<%04d>
将@stTime结构的其中6个成员的word类型数据转换为dword类型,并且按照
秒分时日月年的顺序入栈
0040103A |. 68 2C204000 push 0040202C ; |Format = "%04d-%02d-%02d %02d:%02d:%02d"
将结构化字符串的地址入栈
0040103F |. 8D85 70FFFFFF lea eax, dword ptr [ebp-90] ; |
获取局部变量@szBuf的地址
00401045 |. 50 push eax ; |s
将局部变量@szBuf地址入栈
00401046 |. E8 89000000 call <jmp.&user32.wsprintfA> ; /wsprintfA
调用wsprintf 这个唯一的采用__cdecl调用约定的win32 api, 格式化字符串, 保存在@szBuf中
0040104B |. 83C4 20 add esp, 20
因为wsprintf 采用__cdecl调用约定,故调用者必须自己清理栈空间, 维持栈的平衡
0040104E |. 8D85 70FFFFFF lea eax, dword ptr [ebp-90]
00401054 |. 50 push eax ; /Text
00401055 |. 68 E8030000 push 3E8 ; |ControlID = 3E8 (1000.)
0040105A |. FF75 08 push dword ptr [ebp+8] ; |hWnd
0040105D |. E8 8A000000 call <jmp.&user32.SetDlgItemTextA> ; /SetDlgItemTextA
调用SetDlgItemText函数
将格式化字符串也就是当前的时间 按照 yyyy-mm-dd HH:MM:SS 的形式显示在静态文本框中
00401062 |. EB 3E jmp short 004010A2
处理完毕, 退出过程
00401064 |> 3D 10010000 cmp eax, 110
cmp eax, WM_INITDIALOG 看看是不是WM_INITDIALOG消息
00401069 |. 75 13 jnz short 0040107E
不是, 则转入下一个消息判断
0040106B |. 6A 00 push 0 ; /Timerproc = NULL; Case 110 (WM_INITDIALOG) of switch 0040100C
0040106D |. 68 E8030000 push 3E8 ; |Timeout = 1000. ms
00401072 |. 6A 01 push 1 ; |TimerID = 1
00401074 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401077 |. E8 76000000 call <jmp.&user32.SetTimer> ; /SetTimer
在WM_INITDIALOG消息中, 用SetTimer设置定时器, 时间间隔为1秒
0040107C |. EB 24 jmp short 004010A2
WM_INITDIALOG处理完毕, 退出过程
0040107E |> 83F8 10 cmp eax, 10
cm eax, WM_CLOSE 看看是不是WM_CLOSE消息
00401081 |. 75 16 jnz short 00401099
不是, 则转入默认消息处理
00401083 |. 6A 01 push 1 ; /TimerID = 1; Case 10 (WM_CLOSE) of switch 0040100C
00401085 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401088 |. E8 59000000 call <jmp.&user32.KillTimer> ; /KillTimer
调用KillTimer销毁定时器
0040108D |. 6A 00 push 0 ; /Result = 0
0040108F |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401092 |. E8 49000000 call <jmp.&user32.EndDialog> ; /EndDialog
调用EndDialog结束对话框
00401097 |. EB 09 jmp short 004010A2
WM_CLOSE消息处理完毕,退出过程
00401099 |> B8 00000000 mov eax, 0 ; Default case of switch 0040100C
0040109E |. C9 leave
0040109F |. C2 1000 retn 10
默认消息处理
004010A2 |> B8 01000000 mov eax, 1
004010A7 |. C9 leave
004010A8 /. C2 1000 retn 10
处理完感兴趣的消息,窗口过程返回
以下是程序入口:
004010AB >/$ 6A 00 push 0 ; /pModule = NULL
004010AD |. E8 52000000 call <jmp.&kernel32.GetModuleHandleA> ; /GetModuleHandleA
004010B2 |. A3 00304000 mov dword ptr [403000], eax
004010B7 |. 6A 00 push 0 ; /lParam = NULL
004010B9 |. 68 00104000 push 00401000 ; |DlgProc = clock.00401000
004010BE |. 6A 00 push 0 ; |hOwner = NULL
004010C0 |. 6A 65 push 65 ; |pTemplate = 65
004010C2 |. FF35 00304000 push dword ptr [403000] ; |hInst = NULL
004010C8 |. E8 0D000000 call <jmp.&user32.DialogBoxParamA> ; /DialogBoxParamA
004010CD |. 6A 00 push 0 ; /ExitCode = 0
004010CF /. E8 24000000 call <jmp.&kernel32.ExitProcess> ; /ExitProcess
每个使用对话框资源的窗口的入口几乎都是一样, 所以, 在此就不再重复的去解释。
整个程序关键就在于四个api:
SetTimer
KillTimer
GetLocalTime
wsprintf
需要注意的有2点:
1. 在32位汇编中的函数参数数据类型以及返回值都是32位的, 如果不是32位的, 必须先用movzx或者movsx扩充为32位的
2. wsprintf采用的是__cdecl 调用约定, 需要调用者自己平衡堆栈
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>