Like Share Discussion Bookmark Smile

J.J. Huang   2019-07-07   OllyDBG   瀏覽次數:

OllyDBG - 第三章 | 函數參考

這篇的重點講解怎樣使用 OllyDBG 中的函數參考(即名稱參考)功能。

檔案下載

目的程式:
crackhead.7z

解壓縮密碼:

1
morosedog

使用PEid檢驗

檢驗顯示是使用MASM32/TASM32編寫。

開啟CrackHead.exe使用並分析

  • 開啟 CrackHead.exe

  • 乍看一下好像沒什麼東西,左上角有一個shit
  • 點擊shit
  • 可以看到有AboutGreetsTry It

  • 我們都點點看,點到Try It,發現會跳出類似輸入註冊碼的地方
  • 內容已經填了12345666
  • 點擊「Check It」按鈕
  • 沒反應
  • 在輸入框內改輸入其他字符,會發現僅可輸入數字
  • 輸入987654321
  • 點擊「Check It」按鈕
  • 依舊沒反應

分析:看來要輸入正確的數字,然後按下「Check It」才會有反應

使用OllyDBG分析

  • 啟動OllyDBG
  • 按下快捷鍵F3
  • 選擇CrackHead.exe
  • 在入口點地址後面加上註解 (良好的註解有助於分析)

  • 這邊我先稍微拖拉一下捲軸看下反匯編視窗內是否可有跡可循
  • 可以發現下方好像調用了很多USER32的東西 (API函數)

  • 在反匯編窗口按下右鍵選擇 尋找 -> 目前模組中的名稱(標籤) (快捷鍵Ctrl + N)

    注:在進行此操作時要在OllyDBG中保證是在目前被除錯程式的領空,我在第一篇中已經介紹了領空的概念,如我這裡除錯這個程式時OllyDBG的標題欄顯示的就是「[CPU - 主執行緒, 模組 - CrackHea

  • 打開了名稱位於 CrackHea的視窗
  • 我們可以在點擊視窗標題
  • 然後直接輸入關鍵字,他會自動對照開頭去高亮
    • 例如輸入getw

註:GetWindowTextWindows API宏,在WinUser.h中根據是否已定義Unicode被分別定義為GetWindowTextW和GetWindowTextA。在函數後加入一個W為UNICODE 版,在函數後加入一個A為ASCII 版。
該函數將指定窗口的標題條文本(如果存在)拷貝到一個緩存區內。如果指定的窗口是一個控件,則拷貝控件的文本。但是,GetWindowText可能無法獲取外部應用程序中控件的文本,獲取自繪的控件或者是外部的密碼編輯框很有可能會失敗。

  • 找到GetWindowTextA按下右鍵
  • 選擇尋找輸入函數參考

  • 會開啟參考位於 CrackHea:.test 到 USER32.GetWindwoTextA視窗

  • 00401323 call <jmp.&USER32.GetWindowTextA>按下F2設定斷點

    註:如果想要全部設定斷點,右鍵選擇在每個指令上設定斷點(S),但是這邊僅需要對第一條設定斷點即可。

註:為什麼是下在這,其實這需要一點程式經驗,因為這個函數定義的名稱為GetWindowTextA,我大概解釋為取得視窗中text裡面的值。所以我判斷來取值的位置應該有判斷的邏輯可以去追。

  • 斷點下好後,按下F9執行程式,會看到CrackHead.exe程式,被執行了
  • 我們先點開Try It,然後點擊Check It

  • 這次我們的程序會停在剛剛設定的斷點

反匯編視窗內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
00401323  |.  E8 4C010000   call <jmp.&USER32.GetWindowTextA>                       ; \GetWindowTextA
00401328 |. E8 A5000000 call 004013D2
0040132D |. 3BC6 cmp eax,esi
0040132F |. 75 42 jnz short 00401373
00401331 |. EB 2C jmp short 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 0040300F ; |Title = "Crudd's Crack Head"
00401366 |. 68 33134000 push 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

分段分析

(以下在本人對組合語言不懂的情況下分析,所以以下為不負責任分析,如有錯誤請鞭小力一點)

  • 稍微分析上面的Code,我假設00401328 |. E8 A5000000 call 004013D2這邊是關鍵
  • cmp eax,esi是比較
  • 0040132F |. 75 42 jnz short 00401373是表示不等於
  • 而且他是跳轉到00401373 |> \EB 15 jmp short 0040138A,已經跳過了Crudd's Crack Hea,所以我假設這邊就是失敗跳轉

    參考:x86組合語言 - 第三章 | 基本指令集,要開始慢慢開始記這些語法了唷。

註:以上的假設是需要重複的去追程式執行過程,再慢慢進行假設。

繼續Debug

  • 根據假設,我們按F8運行到00401328 |. E8 A5000000 call 004013D2後按下F7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
004013D2  /$  56            push esi
004013D3 |. 33C0 xor eax,eax
004013D5 |. 8D35 C4334000 lea esi,dword ptr ds:[4033C4]
004013DB |. 33C9 xor ecx,ecx
004013DD |. 33D2 xor edx,edx
004013DF |. 8A06 mov al,byte ptr ds:[esi]
004013E1 |. 46 inc esi
004013E2 |. 3C 2D cmp al,2D
004013E4 |. 75 08 jnz short 004013EE
004013E6 |. BA FFFFFFFF mov edx,-1
004013EB |. 8A06 mov al,byte ptr ds:[esi]
004013ED |. 46 inc esi
004013EE |> EB 0B jmp short 004013FB
004013F0 |> 2C 30 /sub al,30
004013F2 |. 8D0C89 |lea ecx,dword ptr ds:[ecx+ecx*4]
004013F5 |. 8D0C48 |lea ecx,dword ptr ds:[eax+ecx*2]
004013F8 |. 8A06 |mov al,byte ptr ds:[esi]
004013FA |. 46 |inc esi
004013FB |> 0AC0 or al,al
004013FD |.^ 75 F1 \jnz short 004013F0
004013FF |. 8D040A lea eax,dword ptr ds:[edx+ecx]
00401402 |. 33C2 xor eax,edx
00401404 |. 5E pop esi
00401405 |. 81F6 53757A79 xor esi,797A7553
0040140B \. C3 retn
  • 這時候因為我對於組合語言不熟,繼續F8,並且觀察資訊視窗

  • 發現滿多的都還是看不懂的⋯所以參考文章
  • 以下是參考教學文章中所註解的 (因為我還不熟組合語言)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 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 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 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的值是哪裡運算得出的呢?
00401405 |. 81F6 53757A79 xor esi,797A7553 ; 把ESI中的值與797A7553H異或
0040140B \. C3 retn
  • 這邊看來就是將輸入的值做一些運算處理,然後將直往回丟
  • 我們一路F8會回到0040132D |. 3BC6 cmp eax,esi (這行表示比較這兩個值)
    • jnz 為不等於,剛剛假設這邊就是失敗跳轉
  • 可以看到資訊視窗的內容 (可能與我的不一樣)
1
2
esi=797A7553
eax=00BC6142

  • 對資訊視窗的esi=797A7553點選右鍵,選擇修改暫存器
  • 會到如下圖。

  • 對資訊視窗的esi=797A7553點選右鍵,選擇修改暫存器
  • 會到如下圖。

  • 此時可以發現很眼熟的12345666出現了

  • 所以表示他會將我輸入的1234566透過運算轉為16進位的00BC6142然後去和797A7553做比較

  • 答案呼之欲出,也就是我只要取得797A7553的有號或是無號的數值,去當註冊碼,這樣比對結果就會是一致

  • 複製2038068563

  • 貼到程式中,並點擊Check It

  • 成功了。

註:這個 crackme 是要求寫出註冊機的。我們先不要求寫註冊機,但註冊的算法我們要搞清楚。在前面說到的那個 ESI 暫存器值,看看我們上面的分析,其實對做註冊機來說是沒有多少說明的。要搞清註冊算法,必須知道上面那個 ESI 暫存器值是如何產生的,這弄清楚後才能真正清楚這個 crackme 算法。

總結

學習到這邊後,發現組合語言非常重要,在不懂組合語言的情況下,真的沒辦法分析太多;不過這邊的出發點本來就不是組合語言,在邊學邊看的過程中,慢慢學習,主要是先了解OllyDBG的使用。


註:以上參考了
看雪論壇OllyDBG 入门系列(三)-函数参考