【文章作者】: icefisher
【作者郵箱】: 181712814@qq.com
【軟件下載】:
【軟件名稱】: echap518.exe(只是供學習的crackme)
【加殼方式】: 里面直接有個unpacked.ExE,是脫過殼的見【原創】我也來談談消息斷點二
【保護方式】:
【使用工具】: OllyICE
【文章日期】: 20080813
-----------------------------------------------------------------------
第一篇原創希望大家支持下,首先我想和我一樣菜的好多新人,一定都看過了論壇上的那個ollydbg教程,其中的消息斷點真是讓人難以理解,痛苦不堪啊,那么今天我就講講我自己的體會吧,希望望大家共勉。
首先,我覺得我們應該先了解消息,什么是消息,而談到消息,就不能不說到windows程序設計的結構(大家放心。。。我大概講下,不是什么重點)。
如下,這是一個典型windows程序結構,其中很多細節地方我都略去了,
1 /*------------------------------------------------------------------------ 2 HELLOWIN.C -- Displays "Hello, Windows 98!" in client area 3 4 (c) Charles Petzold, 1998 5 6 -----------------------------------------------------------------------*/ 7 8 #include <windows.h> 9 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 10 11 12 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) 13 14 { 15 16 static TCHAR szAppName[] = TEXT ("HelloWin") ; 17 18 HWND hwnd ; 19 20 MSG msg ; 21 //前面都是變量定義,不關注。 22 23 WNDCLASwndclass ; 24 25 26 wndclass.style = CS_HREDRAW | CS_VREDRAW ; 27 28 wndclass.lpfnWndProc = WndProc ; //關鍵句,指出消息的處理函數所在位置 29 30 wndclass.cbClsExtra = 0 ; 31 32 wndclass.cbWndExtra = 0 ; 33 34 wndclass.hInstance = hInstance ; 35 36 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; 37 38 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; 39 40 wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ; 41 42 wndclass.lpszMenuNam = NULL ; 43 44 wndclass.lpszClassName= szAppName ; 45 46 //這一連串的wndclass是在定義一類窗口的屬性,形象地說就是在定義一個模子,然后后面可以用這個模子去建立更多的窗口,其中這個模子中我們需要注意的就是紅字的 wndclass.lpfnWndProc = WndProc ;,他指出了這個模子的消息處理函數是哪個?也就是說這個窗口內發生的一切事件,比如說點擊,雙擊,鼠標移動,按鍵,按下按鈕這一切都會生消息,而系統會自動調用WndProc去處理這些問題。*/ 47 if (!RegisterClass (&wndclass)) 48 49 { 50 51 MessageBox ( NULL, TEXT ("This program requires Windows NT!"), 52 53 szAppName, MB_ICONERROR) ; 54 55 return 0 ; 56 57 } 58 //上面的是很簡單的一個注冊模子,你開始定義了這個模子wndclass,那你得讓系統知道啊,這部分就是這個功能,不須太關注。 59 hwnd = CreateWindow( szAppName, // window class name 60 61 TEXT ("The Hello Program"), // window caption 62 63 WS_OVERLAPPEDWINDOW, // window style 64 65 CW_USEDEFAULT,// initial x position 66 67 CW_USEDEFAULT,// initial y position 68 69 CW_USEDEFAULT,// initial x size 70 71 CW_USEDEFAULT,// initial y size 72 73 NULL, // parent window handle 74 75 NULL, // window menu handle 76 77 hInstance, // program instance handle 78 79 NULL) ; // creation parameters 80 81 //注意上面便是利用這個模子,再加上一些自己獨有的特點建立了一個窗口,大家不需關注 82 83 ShowWindow (hwnd, iCmdShow) ; 84 85 UpdateWindow (hwnd) ; 86 87 88 while (GetMessage (&msg, NULL, 0, 0)) 89 90 { 91 92 TranslateMessage (&msg) ; 93 94 DispatchMessage (&msg) ; 95 96 } 97 //上面這個需要關注,前面提到過,如果窗口產生了消息我們需要調用WndProc來處理,那么之前我們需要上面的哪個while循環先處理下,這個現在我們也不需要了解。 98 return msg.wParam ; 99 100 } 101 102 103 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 104 { 105 106 HDC hdc ; 107 108 PAINTSTRUCT ps ; 109 110 RECT rect ; 111 112 //重點來了,大家注意當我們到了WndProc這個消息處理函數中時,我們會不自然地想到:那么多種的消息,系統如何分辨是那個呢,這是我們就要注意到UINT message,他說明了消息的種類,而WPARAM,LPARAM都是消息具體的參數,大家這時看下面,因為message表示的消息不同,系統使用了switch來選擇對應的處理程序,這里要注意WM_CREATE:像這些WM_開頭的都是消息,而實質上都是數字,不過是頭文件定義了一些等價字符串而已。 113 switch (message) 114 115 { 116 117 case WM_CREATE: 118 119 PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ; 120 121 return 0 ; 122 123 case WM_PAINT: 124 125 hdc = BeginPaint (hwnd, &ps) ; 126 127 GetClientRect (hwnd, &rect) ; 128 129 DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect, 130 131 DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; 132 133 EndPaint (hwnd, &ps) ; 134 135 return 0 ; 136 137 case WM_DESTROY: 138 139 PostQuitMessage (0) ; 140 141 return 0 ; 142 } 143 return DefWindowProc (hwnd, message, wParam, lParam) ; 144 }
Ok,大家是不是覺得很抽象,大家只要把我的注釋看下,大概了解這是干嘛就可以了,其實為了理解消息,只需要注意我紅字標出的部分,wndclass.lpfnWndProc = WndProc ;這個就是我們的重點,他指出了這類windows窗口的消息處理交給了那個函數。然后重點就是CALLBACK,里面根據不同的消息轉到不同的處理地方。
而大家在crackme中廣為使用的則是對話框,比窗口實現方便些,他調用
1 DialogBox ( hInstance, 2 TEXT("AboutBox"), 3 hwnd, 4 AboutDlgProc 5 )
來實現,其中最后一個參數即為消息處理函數,等同於上面所說的WndProc
===========================================================================
好了當我們大概理解消息處理函數后我們就可以來看看這個例子了,首先ollyice載入unpacked.exe。F8單步運行。
如圖地方F7,進入后F8,
如圖地方F7,進入后可以看到F8幾步就可以看到
回想起上篇我提到過的DialogBox (
hInstance,
TEXT("AboutBox"),
hwnd,
AboutDlgProc
)
因為DialogBoxParamA比DialogBox 最后多一個參數用來做記錄,不影響我們理解,
(當然我們也可以用Ctrl+N查找函數DialogBoxParamA來直接定位到這里
)
我們看到DLGProc=004015A1,那么我們明顯可以理解4015A1處是消息處理函數所在。我們Ctrl+G跟過去看,
我們會看到cmp eax,110 或者cmp eax,111之類的比較,
回想起上篇,這就等同於
switch (message)
{
case WM_CREATE:
case WM_PAINT:
case WM_DESTROY:
}
而其中我們需要知道的是WM_COMMAD =111(用來定位按鈕的消息)
WM_LBUTONUP =202(鼠標左鍵放開)
而且我們還要注意一個地方回想上次我提到的
WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
當我們產生一個消息時,必然是調用這個函數,那么根據windows函數調用進棧法則,我們不難得到在消息處理函數開頭如果下斷點,棧結果必然如此
{
返回地址
hwnd
message
wParam
lParam
}
好了,基礎講完,就再看程序(因為前面push ebp,所以后面在取message時,為ebp+C,大家應該能明白吧),好了我們先F9運行次程序,在查看->窗口明顯我們可以看到Check的id值為1。
現在我們來仔細看看這個消息處理函數,他首先從ebp+C中取出了message,存入了eax中,然后開始switch的過程,他不斷比較這些以確定是哪個消息,
好了,我們知道按下按鈕會產生WM_COMMAD(111) 消息,那么當我們看到cmp eax,111,je跳走時,我們離目標已經很近了,跟蹤je到目的地,我們會奇怪,怎么還比較啊,這是因為按鈕有很多個,每一個按下都會產生WM_COMMAD(111),那么這時我們需要再次判斷是哪個按鈕?
我們利用的就是wParam的低四位,他記錄了按下按鈕的ID值,所以就有了movz eax,
[ebp+10],好了我們已經知道了Check的ID值為1,那么我們看到
cmp eax,1
je xxxxxxxx
跟過去下斷點,這時我們就完成了對check的斷點,只要一按check必然會來到此處,后續的也會從這里開始,一切就會光明無比
方法二
當我們面對一個很龐雜的系統時我們又該如何來找那關鍵的消息處理函數呢?茫茫多的程序中又如何去發現
cmp eax,111 與cmp eax,1呢
回到前題,我覺得消息處理函數無論他在那里,當他被調用時,棧中的內容必然是
{
返回地址
hwnd
message
wParam
lParam
}只要我們能抓住他時,我覺得我們就能抓住一切。
首先當我們無法找到消息處理函數時,也許代碼太多我們遺漏了,也許有別的技巧可以隱藏。但是我們可以先F9運行起來,在查看->窗口中,我們選擇他們的共有父窗口,點擊跟隨ClassProc
然后我們進入了系統領空,然后在第一句上下條件斷點:
[ESP+8]==WM_COMMAND&&[ESP+c]==1
然后我們在對話框中輸入數據,點擊check,我們發現我們會中斷在系統領空,並且注意觀察棧

大家都明白了吧,然后在查看->內存,代碼段下內存訪問斷點,F9運行下,直接到達消息處理函數
這時我們這需要靜靜的F8一路跟蹤就可以了
個人總結,記住[ESP+8]==WM_COMMAND&&[ESP+c]==按鈕ID
