OllyDBG - 第十章 | 反匯編練習 (二) 下
這篇將延續OllyDBG - 第九章 | 反匯編練習 (二) 中,在上一篇中找到了計算序號的區域,這邊試著去了解計算過。
這篇會稍微有點枯燥乏味,因為在不懂組合語言的情況下,一定會有很多疑問;所以過程中會需要很多其他的資料來做協助。
而且這邊的過程將會盡量一行一行去做了解及解釋,然後試著融會貫通了解其算法。
檔案下載
相關的目的程式和使用的工具,請直接至OllyDBG - 第八章 | 反匯編練習 (二) 上,這邊下載
分析
延續上一篇的分析過程
1 | 004011DB . 53 push ebx ; 輸入的用戶名長度 |
我們有發現計算序號的Call
位置在00401340
,以下提供代碼
1 | 00401340 /$ 55 push ebp |
使用OllyDBG分析
- 啟動
OllyDBG
- 按下快捷鍵
F3
- 選擇
TraceMe.exe
- 根據分析我們需要在
00401340
的地址下斷點註:記得先移除或是停用之前設定的斷點
- 在反匯編視窗按下
Ctrl + G
- 輸入
00401340
- 然後在
00401340 /$ 55 push ebp
上面設定斷點
- 按下
F9
執行程式 - 輸入用戶名
abcdefg
、序列號12345678
- 按下
Check
按鈕 - 會斷點在
00401340 /$ 55 push ebp
F8
一步一步分析
** 注意:以下的過程會牽涉到大量的組合語言以及CUP暫存器的一些相關知識 **
補充知識
在開始之前,我先補充一些我們可能會需要先了解的知識,這樣在下方的解釋過程中,可以加速大家理解
- 暫存器(寄存器)
可以比喻為CPU身上的口袋,可以在過程中對這些暫存器進行存/取的動作。
當然每個暫存器的用途特性都不盡相同。
每個暫存器的的大小都是32位元,也就是四個字元(Byte);可容納的資料範圍為0 ~ FFFFFFFF (無符號數)。註:更詳細的可以參考x86組合語言 - 第二章 | x86架構及暫存器解釋
EIP 暫存器?
EIP暫存器包含下一條將要執行的指令的位址。
EIP只能在一個call
指令後從堆疊讀出。註:白話文就是,把下一次要執行的指令位置,先放在這個暫存器立面。
Call xxxxxxxx
是什麼?
等同於push eip
然後jmp xxxxxxxxx
C/C++資料長度
BYTE = 8 BIT
CHAR = 1 BYTE
INT = 4 BYTE
DOUBLE = 8 BYTE
LONG = 4 BYTE
SHORT = 2 BYTE
WORD = 2 BYTE
DWORD = 4 BYTE = 32BIT有號數運算(比較)後使用的條件跳越指令
指令 | 意義 | 示意 | 條件 |
---|---|---|---|
jl jngl |
若低於則跳越 若不大於或等於則跳越 |
x < y | SF≠OF |
jle jng |
若低於或等於則跳越 若不大於則跳越 |
x≦y | ZF=1或SF≠OF |
- 暫存器
指令分析
(以下為不負責任分析,在沒有非常了解暫存器和組合語言的情況下,可能有部分的解釋上沒這麼正確或是錯誤)
1 | 00401341 |. 8B6C24 0C mov ebp,dword ptr ss:[esp+0xC] |
當前esp = 0018F678
0018F678 + 0xC = 0x18F684
0x18F684 的值 = 61 62 63 64 65 66 67 00 3B 00 00 00 08 00 00 00 abcdefg.;......
註:在資料視窗那邊按下
Ctrl + G
輸入0x18F684
就可以找到值了
mov ebp, dword ptr ss:[0x18F684]
解釋:將0x18F684
位置放在ss
的堆疊指標暫存器,然後dword ptr,表示取得4 BYTE(四個位元組)的資料,也就是0018F6D8 61 62 63 64 65 66 67 00 abcdefg.
然後mov ebp,也就是將這個存到ebp裡面
所以這行指令執行完後,ebp = 0018F6D8 ; ASCII “abcdefg”
1 | 00401347 |. 8B7C24 18 mov edi,dword ptr ss:[esp+0x18] |
當前esp = 0018F670
0018F670 + 0x18 = 0x18F688
0x18F688 的值 = 07 00 00 00 01 00 00 00 ......
註:在資料視窗那邊按下
Ctrl + G
輸入0x18F688
就可以找到值了
mov edi,dword ptr ss:[esp+0x18]
解釋:將0x18F688
位置放在ss
的堆疊指標暫存器,然後dword ptr,表示取得4 BYTE(四個位元組)的資料,也就是0018F688 07 00 00 00 01 00 00 00 ......
然後mov edi,也就是將這個存到edi裡面
所以這行指令執行完後,edi = 00000007
1 | 0040134B |. B9 03000000 mov ecx,0x3 |
單純把ecx存成00000003
1 | 00401350 |. 33F6 xor esi,esi |
將esi 和 eax的暫存器,變成00000000
1 | 00401356 |. /7E 21 jle short TraceMe.00401379 |
當前edi = 00000007
當前ecx = 00000003
cmp edi,ecx 表示比較edi和ecx是否相同
jle short TraceMe.00401379 表示edi如果等於或是小於eax就跳轉實現,就會跳到00401379的位置
所以這邊是沒有跳轉,會繼續執行下面的的指令...
註:這邊表示你輸入的用戶名長度必須要大於3個字元 (程式有要求須要大於四個字元以上,這是在之前就有被判斷了)
** 以下將會是這個序號的計算的核心過程,會比較複雜一點,我盡量透過文字將其解釋清楚 **
1 | 00401359 |> /83F8 07 /cmp eax,0x7 |
首先這段指令算是一個迴圈,透過用戶名來做計算序號,從哪看出是個迴圈?
最後兩行cmp ecx,edi
和jl short TraceMe.00401359
注意到jl short TraceMe.00401359
是向上跳轉的,而cmp ecx,edi
是在比較,目前用戶名的個字元,是否都已經都被計算過了,就是Count的概念。
好的以下開始講解每一行指令
1 | 00401359 |> /83F8 07 /cmp eax,0x7 |
當前eax = 00000000
0x7 = 00000007
cmp eax,0x7 表示比較edi和ecx是否相同
jle short TraceMe.00401360 表示eax如果等於或是小於00000007就跳轉實現,就會跳到00401360的位置
也就是說跳過xor eax,eax 不做eax的暫存器,變成00000000的動作
註:為什麼要做eax變為00000000的動作,是因為他的序號計算規則的限制,這個實際在分析過程,我相信你會了解他的用意
xor eax,eax
xor edx,edx
將eax 和 edx的暫存器,變成00000000
注:eax的動作是有條件的,如上面剛剛分析的。
1 | 00401364 |. |8A1429 |mov dl,byte ptr ds:[ecx+ebp] |
第一次進來的時候
ecx = 00000003
ebp = 0018F6D8
00000003 + 0018F6D8 = 0x18F6DB
0x18F6DB 的值 = 64 65 66 67 00 3B 00 00 defg.;..
註:在資料視窗那邊按下
Ctrl + G
輸入0x18F6DB
就可以找到值了
mov dl,byte ptr ds:[ecx+ebp]
解釋:將0x18F6DB
位置放在ds
的資料區段暫存器,然後byte ptr,表示取得一個字元的資料,也就是0018F6DB 64 d
然後mov dl,也就是將這個存到dl裡面
dl 是 8-bit的,所以這時候edx會變成 00000064
註:為什麼我是mov 到 dl 卻是 edx 變動?這時候請參考補充知識的最後一張圖,有清楚的表示dl和edx的關係
第一次進來的時候
eax = 00000000
eax + 00405030 = 00405030
00405030 的值 = 00405030 0C 0A 13 09 0C 0B 0A 08 .....
註:在資料視窗那邊按下
Ctrl + G
輸入00405030
就可以找到值了
mov bl,byte ptr ds:[eax+0x405030]
解釋:將00405030
位置放在ds
的資料區段暫存器,然後byte ptr,表示取得一個字元的資料,也就是00405030 0C .
然後mov bl,也就是將這個存到bl裡面
bl 是 8-bit的,所以這時候ebx會變成 0000000C
註:為什麼我是mov 到 bl 卻是 ebx 變動?這時候請參考補充知識的最後一張圖,有清楚的表示bl和ebx的關係
經過第一次的指令過程中
edx = 00000064
ebx = 0000000C
imul edx,ebx 表示作為相乘,並放到edx裡,00000064 * 0000000C = 0x4B0
edx = 0x4B0
註:0x4B0 = 十進制的 1200
esi = 00000000
edx = 000004B0
add esi,edx 表示為相加,並放到esi裡,00000000 + 000004B0 = 0x4B0
esi = 0x4B0
inc ecx 表示將ecx + 1
inc eax 表示將eax + 1
註:這就像是程式裡面for迴圈的i++的概念
當前
ecx = 00000004
edi = 00000007
cmp ecx,edi 表示比較ecx和是否edi相同
jl short TraceMe.00401359 表示ecx如果小於edi就跳轉實現,就會跳到00401359的位置
也就是說還沒有把用戶名都計算完的意思
註:這邊如同一開始所說,是個迴圈,建議大家在OD裡面試著動手做分析,一定可以很清楚知道計算過程,我這邊只是把每一行在做什麼做個說明。
1 | 00401378 |. 5B pop ebx |
這邊就不一行一行去說明了,簡單來說就是上面把序號計算完畢後,把計算過後的序號放在ebp裡面
然後把你輸入的序號放在eax裡面。
最後用lstrcmpA來比對這兩個值是否相同,然後這個call回傳結果回去
總結
分析組合語言的過程,在你看不懂也沒有經驗的情況下,會非常痛苦!但是當你試著去了解每個指令和CPU暫存器的一些用途特性後,會發現其實沒這麼難,只是過程中會很花時間,但是當你分析成功後,絕對會獲得很大的成就感和很大進步!
這邊沒有根據計算方式去做出一個註冊機,是因為我對於組合語言或是C語言都還不熟,也還沒有去用網路上提供的註冊機產生器,所以這邊就沒有去實作這塊,這在未來有碰到,就會特別在寫文章。
註:我這邊提供一些可以參考的文章 (以上的練習我沒事先參考)
CSDN billvsme的专栏 的 OllyDbg 使用笔记 (二)