天天看點

[轉]OllyDBG 入門系列(三)-函數參考

标 題: 【原創】OllyDBG 入門系列(三)-函數參考

作 者: CCDebuger

時 間: 2006-02-14,23:34

鍊 接: http://bbs.pediy.com/showthread.php?t=21330

OllyDBG 入門系列(三)-函數參考

作者:CCDebuger

現在進入第三篇,這一篇我們重點講解怎樣使用 OllyDBG 中的函數參考(即名稱參考)功能。仍然選擇 crackmes.cjb.net 鏡像打包中的一個名稱為 CrackHead 的crackme。老規矩,先運作一下這個程式看看:

[轉]OllyDBG 入門系列(三)-函數參考

呵,竟然沒找到輸入注冊碼的地方!别急,我們點一下程式上的那個菜單“Shit”(真是 Shit 啊,呵呵),在下拉菜單中選“Try It”,會來到如下界面:

[轉]OllyDBG 入門系列(三)-函數參考

我們點一下那個“Check It”按鈕試一下,哦,竟然沒反應!我再輸個“78787878”試試,還是沒反應。再試試輸入字母或其它字元,輸不進去。由此判斷注冊碼應該都是數字,隻有輸入正确的注冊碼才有動靜。用 PEiD 檢測一下,結果為 MASM32 / TASM32,怪不得程式比較小。資訊收集的差不多了,現在關掉這個程式,我們用 OllyDBG 載入,按 F9 鍵直接讓它運作起來,依次點選上面圖中所說的菜單,使被調試程式顯示如上面的第二個圖。先不要點那個“Check It”按鈕,保留上圖的狀态。現在我們沒有什麼字串好參考了,我們就在 API 函數上下斷點,來讓被調試程式中斷在我們希望的地方。我們在 OllyDBG 的反彙編視窗中右擊滑鼠,在彈出菜單中選擇 查找->目前子產品中的名稱 (标簽),或者我們通過按 CTR+N 組合鍵也可以達到同樣的效果(注意在進行此操作時要在 OllyDBG 中保證是在目前被調試程式的領空,我在第一篇中已經介紹了領空的概念,如我這裡調試這個程式時 OllyDBG 的标題欄顯示的就是“[CPU - 主線程, 子產品 - CrackHea]”,這表明我們目前在被調試程式的領空)。通過上面的操作後會彈出一個對話框,如圖:

[轉]OllyDBG 入門系列(三)-函數參考

對于這樣的編輯框中輸注冊碼的程式我們要設斷點首選的 API 函數就是 GetDlgItemText 及 GetWindowText。每個函數都有兩個版本,一個是 ASCII 版,在函數後添加一個 A 表示,如 GetDlgItemTextA,另一個是 UNICODE 版,在函數後添加一個 W 表示。如 GetDlgItemTextW。對于編譯為 UNCODE 版的程式可能在 Win98 下不能運作,因為 Win98 并非是完全支援 UNICODE 的系統。而 NT 系統則從底層支援 UNICODE,它可以在作業系統内對字串進行轉換,同時支援 ASCII 和 UNICODE 版本函數的調用。一般我們打開的程式看到的調用都是 ASCII 類型的函數,以“A”結尾。又跑題了,呵呵。現在回到我們調試的程式上來,我們現在就是要找一下我們調試的程式有沒有調用 GetDlgItemTextA 或 GetWindowTextA 函數。還好,找到一個 GetWindowTextA。在這個函數上右擊,在彈出菜單上選擇“在每個參考上設定斷點”,我們會在 OllyDBG 視窗最下面的那個狀态欄裡看到“已設定 2 個斷點”。另一種方法就是那個 GetWindowTextA 函數上右擊,在彈出菜單上選擇“查找輸入函數參考”(或者按Enter鍵),将會出現下面的對話框:

[轉]OllyDBG 入門系列(三)-函數參考

看上圖,我們可以把兩條都設上斷點。這個程式隻需在第一條指令設斷點就可以了。好,我們現在按前面提到的第一條方法,就是“在每個參考上設定斷點”,這樣上圖中的兩條指令都會設上斷點。斷點設好後我們轉到我們調試的程式上來,現在我們在被我們調試的程式上點選那個“Check It”按鈕,被 OllyDBG 斷下:

00401323 |. E8 4C010000         CALL <JMP.&USER32.GetWindowTextA>           ; GetWindowTextA

00401328 |. E8 A5000000         CALL CrackHea.004013D2                      ; 關鍵,要按F7鍵跟進去

0040132D |. 3BC6                CMP EAX,ESI                                 ; 比較

0040132F |. 75 42               JNZ SHORT CrackHea.00401373                 ; 不等則完蛋

00401331 |. EB 2C               JMP SHORT CrackHea.0040135F

00401333 |. 4E 6F 77 20 7>      ASCII "Now write a keyg"

00401343 |. 65 6E 20 61 6>      ASCII "en and tut and y"

00401353 |. 6F 75 27 72 6>      ASCII "ou're done.",0

0040135F |> 6A 00               PUSH 0                                      ; Style = MB_OK|MB_APPLMODAL

00401361 |. 68 0F304000         PUSH CrackHea.0040300F                      ; Title = "Crudd's Crack Head"

00401366 |. 68 33134000         PUSH CrackHea.00401333                      ; Text = "Now write a keygen and tut and you're done."

0040136B |. FF75 08             PUSH DWORD PTR SS:[EBP+8]                   ; hOwner

0040136E |. E8 19010000         CALL <JMP.&USER32.MessageBoxA>              ; MessageBoxA

從上面的代碼,我們很容易看出 00401328 位址處的 CALL CrackHea.004013D2 是關鍵,必須仔細跟蹤。而注冊成功則會顯示一個對話框,标題是“Crudd's Crack Head”,對話框顯示的内容是“Now write a keygen and tut and you're done.”現在我按一下 F8,準備步進到 00401328 位址處的那條 CALL CrackHea.004013D2 指令後再按 F7 鍵跟進去。等等,怎麼回事?怎麼按一下 F8 鍵跑到這來了:

00401474 $- FF25 2C204000      JMP DWORD PTR DS:[<&USER32.GetWindowText>    ; USER32.GetWindowTextA

0040147A $- FF25 30204000      JMP DWORD PTR DS:[<&USER32.LoadCursorA>]     ; USER32.LoadCursorA

00401480 $- FF25 1C204000      JMP DWORD PTR DS:[<&USER32.LoadIconA>]       ; USER32.LoadIconA

00401486 $- FF25 20204000      JMP DWORD PTR DS:[<&USER32.LoadMenuA>]       ; USER32.LoadMenuA

0040148C $- FF25 24204000      JMP DWORD PTR DS:[<&USER32.MessageBoxA>]     ; USER32.MessageBoxA

原來是跳到另一個斷點了。這個斷點我們不需要,按一下 F2 鍵删掉它吧。删掉 00401474 位址處的斷點後,我再按 F8 鍵,呵,完了,跑到 User32.dll 的領空了。看一下 OllyDBG 的标題欄:“[CPU - 主線程, 子產品 - USER32],跑到系統領空了,OllyDBG 反彙編視窗中顯示代碼是這樣:

77D3213C 6A 0C                 PUSH 0C

77D3213E 68 A021D377           PUSH USER32.77D321A0

77D32143 E8 7864FEFF           CALL USER32.77D185C0

怎麼辦?别急,我們按一下 ALT+F9 組合鍵,呵,回來了:

00401328 |. E8 A5000000        CALL CrackHea.004013D2                     ; 關鍵,要按F7鍵跟進去

0040132D |. 3BC6               CMP EAX,ESI                                ; 比較

0040132F |. 75 42              JNZ SHORT CrackHea.00401373                ; 不等則完蛋

光标停在 00401328 位址處的那條指令上。現在我們按 F7 鍵跟進:

004013D2 /$ 56                PUSH ESI                                    ; ESI入棧

004013D3 |. 33C0              XOR EAX,EAX                                 ; EAX清零

004013D5 |. 8D35 C4334000     LEA ESI,DWORD PTR DS:[4033C4]               ; 把注冊碼框中的數值送到ESI

004013DB |. 33C9              XOR ECX,ECX                                 ; ECX清零

004013DD |. 33D2              XOR EDX,EDX                                 ; EDX清零

004013DF |. 8A06              MOV AL,BYTE PTR DS:[ESI]                    ; 把注冊碼中的每個字元送到AL

004013E1 |. 46                INC ESI                                     ; 指針加1,指向下一個字元

004013E2 |. 3C 2D             CMP AL,2D                                   ; 把取得的字元與16進制值為2D的字元(即“-”)比較,這裡主要用于判斷輸入的是不是負數

004013E4 |. 75 08             JNZ SHORT CrackHea.004013EE                 ; 不等則跳

004013E6 |. BA FFFFFFFF       MOV EDX,-1                                  ; 如果輸入的是負數,則把-1送到EDX,即16進制FFFFFFFF

004013EB |. 8A06              MOV AL,BYTE PTR DS:[ESI]                    ; 取“-”号後的第一個字元

004013ED |. 46                INC ESI                                     ; 指針加1,指向再下一個字元

004013EE |> EB 0B             JMP SHORT CrackHea.004013FB

004013F0 |> 2C 30             SUB AL,30                                   ; 每位字元減16進制的30,因為這裡都是數字,如1的ASCII碼是“31H”,減30H後為1,即我們平時看到的數值

004013F2 |. 8D0C89            LEA ECX,DWORD PTR DS:[ECX+ECX*4]            ; 把前面運算後儲存在ECX中的結果乘5再送到ECX

004013F5 |. 8D0C48            LEA ECX,DWORD PTR DS:[EAX+ECX*2]            ; 每位字元運算後的值與2倍上一位字元運算後值相加後送ECX

004013F8 |. 8A06              MOV AL,BYTE PTR DS:[ESI]                    ; 取下一個字元

004013FA |. 46                INC ESI                                     ; 指針加1,指向再下一個字元

004013FB |> 0AC0              OR AL,AL

004013FD |.^ 75 F1            JNZ SHORT CrackHea.004013F0                 ; 上面一條和這一條指令主要是用來判斷是否已把使用者輸入的注冊碼計算完

004013FF |. 8D040A            LEA EAX,DWORD PTR DS:[EDX+ECX]              ; 把EDX中的值與經過上面運算後的ECX中值相加送到EAX

00401402 |. 33C2              XOR EAX,EDX                                 ; 把EAX與EDX異或。如果我們輸入的是負數,則此處功能就是把EAX中的值取反

00401404 |. 5E                POP ESI                                     ; ESI出棧。看到這條和下一條指令,我們要考慮一下這個ESI的值是哪裡運算得出的呢?

00401405 |. 81F6 53757A79     XOR ESI,797A7553                            ; 把ESI中的值與797A7553H異或

0040140B \. C3                RETN

這裡留下了一個問題:那個 ESI 寄存器中的值是從哪運算出來的?先不管這裡,我們接着按 F8 鍵往下走,來到 0040140B 位址處的那條 RETN 指令(這裡可以通過在調試選項的“指令”标簽中勾選“使用 RET 代替 RETN”來更改傳回指令的顯示方式),再按一下 F8,我們就走出 00401328 位址處的那個 CALL 了。現在我們回到了這裡:

0040132D |. 3BC6             CMP EAX,ESI                                  ; 比較

0040132F |. 75 42            JNZ SHORT CrackHea.00401373                  ; 不等則完蛋

光标停在了 0040132D 位址處的那條指令上。根據前面的分析,我們知道 EAX 中存放的是我們輸入的注冊碼經過計算後的值。我們來看一下資訊視窗:

ESI=E6B5F2F9

EAX=FF439EBE

左鍵選擇資訊視窗中的 ESI=E6B5F2F9,再按右鍵,在彈出菜單上選“修改寄存器”,我們會看到這樣一個視窗:

[轉]OllyDBG 入門系列(三)-函數參考

可能你的顯示跟我不一樣,因為這個 crackme 中已經說了每個機器的序列号不一樣。關掉上面的視窗,再對資訊視窗中的 EAX=FF439EBE 做同樣操作:

[轉]OllyDBG 入門系列(三)-函數參考

由上圖我們知道了原來前面分析的對我們輸入的注冊碼進行處理後的結果就是把字元格式轉為數字格式。我們原來輸入的是字串“12345666”,現在轉換為了數字 12345666。這下就很清楚了,随便在上面那個修改 ESI 圖中顯示的有符号或無符号編輯框中複制一個,粘貼到我們調試的程式中的編輯框中試一下:

[轉]OllyDBG 入門系列(三)-函數參考

呵呵,成功了。且慢高興,這個 crackme 是要求寫出注冊機的。我們先不要求寫注冊機,但注冊的算法我們要搞清楚。還記得我在前面說到的那個 ESI 寄存器值的問題嗎?現在看看我們上面的分析,其實對做注冊機來說是沒有多少幫助的。要搞清注冊算法,必須知道上面那個 ESI 寄存器值是如何産生的,這弄清楚後才能真正清楚這個 crackme 算法。今天就先說到這裡,關于如何追出 ESI 寄存器的值我就留到下一篇 OllyDBG 入門系列(四)-記憶體斷點 中再講吧。

--------------------------------------------------------------------------------

【版權聲明】 本文純屬技術交流, 轉載請注明作者并保持文章的完整, 謝謝! 

上傳的附件

[轉]OllyDBG 入門系列(三)-函數參考

<b>作者贊賞</b>

<a href="http://union.dangdang.com/transfer.php?from=P-262177&amp;ad_type=10&amp;sys_id=1&amp;backurl=http%3A%2F%2Fbook.dangdang.com%2F">當當計算書籍 5-8折</a>

本文轉自Sam Lin部落格部落格園部落格,原文連結:http://www.cnblogs.com/samlin/archive/2009/04/01/1427601.html,如需轉載請自行聯系原作者

繼續閱讀