Like Share Discussion Bookmark Smile

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

OllyDBG - 第五章 | 訊息斷點及RUN追蹤

教學文章提到,這個目的程式,使用OllyDBG - 第二章 | 字串參考OllyDBG - 第三章 | 函數參考,兩種方法都可以容易找到斷點。

註:我這邊是沒有去實際操作是否可以使用字串參考函數參考就可以找到,有時間再試試看。

這篇文章主要是要說訊息斷點RUN追蹤

檔案下載

目的程式:
cycle.7z

解壓縮密碼:

1
morosedog

使用PEid檢驗

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

開啟cycle.EXE使用並分析

  • 開啟 cycle.EXE
  • 輸入Nameserial
    • Morose12345678
  • 點擊「Check」按鈕
  • 沒有反應

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

前言

在設訊息斷點前,有兩個內容我們要簡單瞭解一下:

訊息是幹什麼的

  • Windows 的中文翻譯就是「視窗」,而 Windows 上面的套用程式也都是通過視窗來與使用者互動的。
  • 現在就有一個問題,套用程式是如何知道使用者做了什麼樣的操作的?這裡就要用到訊息了。
  • Windows 是個基於訊息的系統,它在套用程式開始執行後,為該程式建立一個「訊息佇列」,用來存放該程式可能建立的各種不同視窗的資訊。
  • 比如你建立視窗、點擊按鈕、搬移滑鼠等等,都是通過訊息來完成的。
  • 通俗的說,Windows 就像一個中間人,你要幹什麼事是先知會它,然後它才通過傳遞訊息的模式知會套用程式作出相應的操作。
  • 說到這,又有個問題了,在 Windows 下有多個程式都在執行,那我點了某個按鈕,或把某個視窗最大化,Windows 知道我是點的哪個嗎?
  • 這裡就要說到另一個內容:識別碼(handle)了。識別碼一般是個 32 位的數,表示一個對象。
  • Windows 通過使用識別碼來標識它代表的對象。比如你點擊某個按鈕,Windows 就是通過識別碼來判斷你是點擊了那一個按鈕,然後傳送相應的訊息知會程式。

RUN追蹤是幹什麼的:

簡單的說,RUN追蹤就是把被除錯程式執行過的指令儲存下來,讓你可以檢視被除錯程式執行期間干了哪些事。RUN追蹤會把位址、暫存器的內容、訊息以及已知的操作數記錄到RUN追蹤緩沖區中,你可以通過檢視RUN追蹤的記錄來瞭解程式執行了那些指令。在這還要注意一個緩沖區大小的問題,如果執行的指令太多,緩沖區滿了的話,就會自動丟棄前面老的記錄。

設定RUN追蹤,可以點選選項 -> 除錯選項 -> 追蹤開啟設定視窗

使用OllyDBG分析

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

  • F9運行程式
  • 輸入Nameserial
    • Morose12345678
  • 先不要點擊「Check」按鈕

  • 點擊選單上的檢視 -> 視窗 (或是點擊「W」圖的按鈕)

  • 會開啟Windwos視窗

  • 分析Windwos內容 (行號為我自己加入的,用於方便表示)
    • 1 表示父視窗
    • 2 看到類別為Button,標題為Check,所以這個代表Chekc的那顆按鈕
    • 3、4 看到類別為Edit,可以判定為兩個輸入框
    • 5、6 看到類別為Static,可以判斷為兩個標籤,分別為NameSerial
    • 7、8 看到類別為Button,標題為About;為什麼會有兩個,我的猜測是點擊About後裡面還有一個確定的按鈕
    • 9、10 這個我不做解釋,怕解釋錯了⋯這邊暫時不需要知道。

注意:控制碼(句柄),每次重開程式後都會不一樣。

行號 控制碼 標題 父視窗 WinProc ID 樣式 擴充樣式 緒程 ClsProc 類別
1 00120274 CycleCrackme Topmost 94C80A44 00010100 76AFAF83 #32770
2 00070268 Check 00120274 00000066 50010000 00000004 76B058F1 Button
3 00090234 00120274 000003E8 50010080 00000204 76B0167B Edit
4 000B0230 00120274 000003E9 50010080 00000204 76B0167B Edit
5 000B0244 Name 00120274 0000FFFF 50020000 00000004 76B05164 Static
6 000B025C Serial 00120274 0000FFFF 50020000 00000004 76B05164 Static
7 000B0260 About 00120274 00000067 50010000 00000004 76B058F1 Button
8 000B0278 About 00120274 00000003 50010000 00000004 76B058F1 Button
9 000D027C Default IME 00120274 8C000000 76B04411 IME
10 000F01C8 MSCTFIME UI 000D027C 8C000000 FFFF01DB MSCTFIME UI
  • 針對Chekc按鈕的控制碼(句柄)右鍵
  • 選擇在ClassProc 上設訊息斷點
  • 會開啟在ClassProc 上設訊息斷點視窗

  • 點擊訊息下拉選單
  • 會看到訊息真不少

    註:這麼多訊息我們選哪個呢?註冊是個按鈕,我們就在按下按鈕再鬆開時讓程式中斷。
    查一下 MSDN,我們知道這個訊息應該是WM_LBUTTON_UP,看字面意思也可以知道是左鍵鬆開時的訊息

  • 選擇202 WM_LBUTTON_UP
  • 按下確定,消息斷點就設定完畢

  • 這邊會發現三個按鈕都被下了斷點

  • 回到OllyDBG
  • 點擊選單上的除錯 -> 開啟或清除RUN追蹤

    註:第一次點這個選單是開啟RUN追蹤,在開啟的情況下點擊就是清除RUN追蹤的記錄,對RUN追蹤熟悉時還可以設定條件

  • 保證目前在我們除錯的程式領空
  • 在反匯編視窗中點擊右鍵選擇RUN追蹤 -> 加入所有函數過程的入口

  • 可以看到OllyDBG把識別出的函數過程都在前面加了灰色條

  • 回到cycle程式,點擊「Check」按鈕
  • 斷點到76B058F1 > 8BFF mov edi,edi

  • 此時要打開記憶體映射視窗
  • 點擊選單上的檢視 -> 記憶體 (或是點擊「M」圖的按鈕)

  • 會開啟Memory map視窗

  • 選中00401000,並設定斷點

為什麼在00401000上設定斷點?

看完上面的圖我們應該瞭解為什麼在00401000處的代碼段下斷點了

  • 按下F9
  • 跳到中斷點,而且可以發現回到程式領空

    註:當然在 401000 處所在的段不是絕對的,我們主要是要看程式的代碼段在什麼位置,其實在上面圖中OllyDBG記憶體視窗的「包括」欄中我們就可以看得很清楚了

  • 按下F9
  • 選單檢視->RUN 追蹤
  • 點擊選單上的檢視 -> RUN 追蹤 (或是點擊「…」圖的按鈕)

  • 會開啟Run trace視窗

  • 選中隨便一條cycle
  • 點擊右鍵選擇統計模組

  • 會開啟統計用於 cycle視窗
  • 關心計數為1的地址
  • 隨便對其中一點左鍵兩下就會跳到反匯編對應的相關代碼處
  • 這邊就點00401082

  • 跳轉到00401082 |> \837D 10 67 cmp dword ptr ss:[ebp+10],67
  • 看不懂組合語言的情況下,看不到什麼關鍵的

  • 繼續點另外一個計數為1的地址
  • 這邊點0040111F
  • 跳轉到0040111F |> \C3 retn
  • 首先retn我猜是返回指令,所以假設為最後一個動作
  • 我們往上看匯編可以看到一些熟悉的東西
  • 所以我將斷點設定在004010BE |. 6A 11 push 11 ; /Count = 11 (17.)

  • 按下Alt + B開啟Breakpoints視窗
  • 將其它的斷點停用76B058F1 USER32 Log "<WinProc>" mov edi,edi停用

  • 點擊選單上的除錯 -> 關閉 RUN 追蹤

  • 回到cycle程式,點擊「Check」按鈕
  • 斷點到004010BE |. 6A 11 push 11 ; /Count = 11 (17.)
  • 我們就可以開始分析了

分段分析

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

  • 分析過程中,會發現斷點到cmp ecx,11jnz short 0040111F就跳到ret了。
1
2
3
4
5
6
7
8
9
10
00401100  |> \83F9 11       cmp ecx,11                               ;  比較注冊碼的長度是否為十進制的17
00401103 |. 75 1A jnz short 0040111F ; 不等於則跳轉到0040111F
00401105 |. |E8 E7000000 call 004011F1
0040110A |. |B9 01FF0000 mov ecx,0FF01
0040110F |. |51 push ecx
00401110 |. |E8 7B000000 call 00401190
00401115 |. |83F9 01 cmp ecx,1
00401118 |. |74 06 je short 00401120
0040111A |> |E8 47000000 call 00401166
0040111F |> \C3 ret

  • 下斷點在00401100 |> \83F9 11 cmp ecx,11 ; 比較注冊碼的長度是否為十進制的17
  • 在執行下次分析之前我們先把註冊碼輸入為17位
  • 回到cycle程式,輸入12345678901234567點擊「Check」按鈕
  • 這次可以發現成功過了cmp ecx,11這段
  • 繼續向下分析 (看到call那些都還先不要進去)
  • 當執行到0040111A |> /E8 47000000 call 00401166發現無法再按F8往下
  • 回到cycle程式,會發現跳出Please enter a valid serial for your name!

(分析到這邊我就沒再繼續分析了,只是大概有個感覺,在對組合語言更熟後我會再重新分析過一次)

  • 以下是參考教學文章中所註解的 (因為我還不熟組合語言)

(這邊以下看不懂也沒關係,不要氣餒,目前先盡量了解OD工具的功能和使用…因為我也很多問號)

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
004010BC  |. /74 61         je short 0040111F
004010BE |. |6A 11 push 11 ; /Count = 11 (17.)
004010C0 |. |68 60214000 push 00402160 ; |Buffer = cycle.00402160
004010C5 |. |68 E8030000 push 3E8 ; |ControlID = 3E8 (1000.)
004010CA |. |FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004010CD |. |E8 F7010000 call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004010D2 |. |0BC0 or eax,eax
004010D4 |. |74 49 je short 0040111F
004010D6 |. |B9 10000000 mov ecx,10
004010DB |. |2BC8 sub ecx,eax
004010DD |. |BE 60214000 mov esi,00402160 ; ASCII "MoroseMoroseMoro"
004010E2 |. |8BFE mov edi,esi ; 使用者名送 EDI
004010E4 |. |03F8 add edi,eax
004010E6 |. |FC cld
004010E7 |. |F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[>
004010E9 |. |33C9 xor ecx,ecx ; 清零,設迴圈計數器
004010EB |. |BE 71214000 mov esi,00402171 ; 註冊碼送ESI
004010F0 |> |41 /inc ecx
004010F1 |. |AC |lods byte ptr ds:[esi] ; 取註冊碼的每個字元
004010F2 |. |0AC0 |or al,al ; 判斷是否為空
004010F4 |. |74 0A |je short 00401100 ; 沒有則跳走
004010F6 |. |3C 7E |cmp al,7E ; 判斷字元是否為非ASCII字元
004010F8 |. |7F 06 |jg short 00401100 ; 非ASCII字元跳走
004010FA |. |3C 30 |cmp al,30 ; 看是否小於30H,主要是判斷是不是數字或字母等
004010FC |. |72 02 |jb short 00401100 ; 小於跳走
004010FE |.^|EB F0 \jmp short 004010F0
00401100 |> |83F9 11 cmp ecx,11 ; 比較注冊碼的長度是否為十進制的17
00401103 |. |75 1A jnz short 0040111F ; 不等於則跳轉到0040111F
00401105 |. |E8 E7000000 call 004011F1 ; 關鍵,F7跟進去
0040110A |. |B9 01FF0000 mov ecx,0FF01
0040110F |. |51 push ecx
00401110 |. |E8 7B000000 call 00401190 ; 關鍵,跟進去
00401115 |. |83F9 01 cmp ecx,1
00401118 |. |74 06 je short 00401120
0040111A |> |E8 47000000 call 00401166 ; 跳出註冊失敗訊息
0040111F |> \C3 ret
00401120 |> A1 68214000 mov eax,dword ptr ds:[402168]
00401125 |. 8B1D 6C214000 mov ebx,dword ptr ds:[40216C]
0040112B |. 33C3 xor eax,ebx
0040112D |. 3305 82214000 xor eax,dword ptr ds:[402182]
00401133 |. 0D 40404040 or eax,40404040
00401138 |. 25 77777777 and eax,77777777
0040113D |. 3305 79214000 xor eax,dword ptr ds:[402179]
00401143 |. 3305 7D214000 xor eax,dword ptr ds:[40217D]
00401149 |.^ 75 CF jnz short 0040111A ; 這裡跳走就失敗
0040114B E8 2B000000 call 0040117B ; 註冊成功對話框
00401150 \. C3 ret
00401151 /$ 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401153 |. 68 00204000 push 00402000 ; |Title = "About this crackme"
00401158 |. 68 13204000 push 00402013 ; |Text = "CycleCrackme by cW_",LF,"Try to get a serial for your name and write a keygen... no patching!",LF,"This should be not too hard..., if you understand how it works ;-)",LF,"Please send your solution to cW_6556@yahoo.com"
0040115D |. FF75 08 push dword ptr ss:[ebp+8] ; |hOwner
00401160 |. E8 58010000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
00401165 \. C3 ret
00401166 /$ 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401168 |. 68 DE204000 push 004020DE ; |Title = "Ahm. No!"
0040116D |. 68 E7204000 push 004020E7 ; |Text = "Please enter a valid serial for your name!"
00401172 |. FF75 08 push dword ptr ss:[ebp+8] ; |hOwner
00401175 |. E8 43010000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
0040117A \. C3 ret
0040117B /$ 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
0040117D |. 68 12214000 push 00402112 ; |Title = "Wow!"
00401182 |. 68 17214000 push 00402117 ; |Text = "Congratulations!",LF,"Write a tutorial/keygen an send it to cW_6556@yahoo.com"
00401187 |. FF75 08 push dword ptr ss:[ebp+8] ; |hOwner
0040118A |. E8 2E010000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
0040118F \. C3 ret

這邊是教學說的內容:

1
2
3
4
寫到這準備追蹤算法時,才發現這個 crackme 還是挺複雜的,具體算法我就不寫了,實在沒那麼多時間詳細追蹤。有興趣的可以跟一下,註冊碼是17位,使用者名採用複製的模式延伸到 16 位,如我輸入「CCDebuger」,延伸後就是「CCDebugerCCDebug」。大致是先取延伸後使用者名的前 8 位和註冊碼的前 8 位,把使用者名的前四位和後四位分別與註冊碼的前四位和後四位進行運算,算完後再把延伸後使用者名的後 8 位和註冊碼的後 8 位分兩部分,再與前面使用者名和註冊碼的前 8 位計算後的值進行異或計算,最後結果等於 0 就成功。註冊碼的第 17 位我尚沒有找到有何用處。對於新手來說,可能這個 crackme 的難度大了一點。沒關係,我們主要是學習 OllyDBG 的使用,方法掌握就可以了。
最後說明一下:
1、這個程式在設定了訊息斷點後可以省略在代碼段上設訪問斷點那一步,直接開啟 RUN 追蹤,訊息斷點斷下後按 CTR+F12 群組合鍵讓程式執行,RUN 追蹤記錄中就可以找到關鍵地方。
2、對於這個程式,你可以不設訊息斷點,在輸入使用者名和註冊碼後先不按那個「Check」按鈕,直接開啟 RUN 追蹤,加入「所有函數過程的入口」後再回到程式中點「Check」按鈕,這時在 OllyDBG 中開啟 RUN 追蹤記錄同樣可以找到關鍵位置。

總結

這個CrackMe真的有難度,我有嘗試著追蹤進去關鍵的Call,發現非常的複雜⋯一堆組合語言就殺死我了。看來在寫個一兩張了解OllyDBG後,要認真把組合語言學習過一遍。不過在這邊實作過程慢慢看得懂一些組合語言了。


註:以上參考了
看雪論壇OllyDBG 入门系列(五)-消息断点及 RUN 跟踪