天天看點

Crackmes.de上borismilner的一個crackme

borismilner提供的幾個​​crackme​​歸檔在"1-very_easy_for_newbies"下:

Crackmes.de上borismilner的一個crackme

level0-level3還算簡單,是名副其實的"crackme for newbies",但是相比起來level-4.exe可能超過"newbie"的能力,因為作者在CRM中用了簡單的SMC(self-modify code)。且不論它是不是真的"very easy",看我在此獻個醜把這題解了。

    先查殼,這個exe倒是不帶殼(不要以為這個級别的CRM不帶殼,那真是太小看作者了!),運作程式,随便輸入一串字元,程式對此傳回"NOT A GOOD JOB":

Crackmes.de上borismilner的一個crackme

以此為切入口,在IDA中"Strings"視窗中搜尋該字元串,然而,并沒有任何收獲:

Crackmes.de上borismilner的一個crackme

那就換"Password"試試運氣,輸入密碼的地方應該離列印"Password"不遠:

Crackmes.de上borismilner的一個crackme
Crackmes.de上borismilner的一個crackme

對Password引用的代碼片的附近又調用了scanf,之後又對輸入的字元串與字元串"Mario"進行了逐一比對。嗯,第一反應覺得"Mario"就是應該就是password,重新運作一次程式,輸入"Mario"試試,依舊提示失敗:

Crackmes.de上borismilner的一個crackme

看來需要好好分析一下程式,先調試一下程式,看看輸入"Mario"後,是什麼原因導緻程式輸出"NOT A GOOD JOB"。

.text:0040140C cmp     ds:inputStr, 'M'
.text:00401413 jz      short loc_40148B
.text:00401415 cmp     ds:inputStr+1, 'a'
.text:0040141C jz      short loc_40148B
.text:0040141E cmp     ds:inputStr+2, 'r'
.text:00401425 jz      short loc_40148B
.text:00401427 cmp     ds:inputStr+3, 'i'
.text:0040142E jz      short loc_40148B
.text:00401430 cmp     ds:inputStr+4, 'o'
.text:00401437 jz      short loc_40148B      

在0x40140C處下斷點,即程式輸入字元串後首次進行比較的位置。輸入"Mario"後,程式會跳轉到0x0040148B處,這段代碼片好像會往螢幕上輸出一段火星文,先運作看下結果:

.text:0040148B mov     eax, offset aIhsF@hhcMhe        ; "\rIHS'F'@HHC'MHE'&\r"
.text:00401490 mov     ecx, 10h
.text:00401495
.text:00401495 loc_401495:                             ; CODE XREF: sub_4013E0+B9j
.text:00401495                                         ; sub_4013E0+CCj
.text:00401495 xor     byte ptr [eax+ecx], 7
.text:00401499 loop    loc_401495
.text:0040149B xor     byte ptr [eax+ecx], 7
.text:0040149F push    eax                             ; Format
.text:004014A0 call    printf
.text:004014A5 add     esp, 4      

當程式執行完printf,螢幕上輸出的并不是什麼火星文,取而代之的依然是熟悉而又不和諧的"NOT A GOOD JOB"字樣!再回過去看下IDA中0x0040148B處引用的火星文,已經變成了"NOT A GOOD JOB",如圖:

Crackmes.de上borismilner的一個crackme

這種變化源自0x00401495-0x00401499之間的xor循環,這段xor循環将資料段的字元串進行異或解碼,解碼前:

.data:0040902A 00000013 C \rIHS'F'@HHC'MHE'&\r      

解碼後:

.data:0040902A aIhsF@hhcMhe db 0Dh,'NOT A GOOD JOB !',0Dh,0 ; DATA XREF: sub_4013E0+A4o
.data:0040902A                                         ; sub_4013E0:loc_40148Bo      

嗯,看來密碼不能是"Mario",作者誤導我呐!換一個密碼,避開所有坑,繼續往下調試必然會進入這個代碼片:

.text:0040144C loc_40144C:                             ; CODE XREF: sub_4013E0+6Aj
.text:0040144C mov     eax, offset loc_409000          ; smc
.text:00401451 mov     ebx, offset loc_40145B
.text:00401456 jmp     loc_409000      

jmp指令使程式跳轉到資料段執行(請注意下面代碼片的段字首.data,我調試了很久才注意到這是資料段):

.data:00409000 xor     byte ptr [eax+4], 0F7h
.data:00409004 jl      short near ptr Format+15h ;<-注意這條指令,執行完後面的xor 
                                             ;byte ptr [eax+4],7後會變為mov ecx,[esp+arg_0]
.data:00409006 and     al, 4
.data:00409008 xor     byte ptr [eax+4], 7
.data:0040900C or      ecx, 0F0F0h
.data:00409012 jmp     ebx      

此時,eax指向0x00409000,是以指令xor byte ptr [eax+4],0F7h其實就是修改0x409004處的資料,同時這處還是一條指令,最終0x00409004被修改為一條mov指令,如下圖:

Crackmes.de上borismilner的一個crackme

至于[esp+arg_0]中,按照調試的結果,存儲的是程式的參數數量,即int argc。

int main(int argc,char* argv[]);      

最後,程式指令jmp ebx跳轉到代碼段0x0040145B:

Crackmes.de上borismilner的一個crackme

這段代碼相對比較容易,它将ecx中存儲的參數的數量通過循環移位,移動到eax的最高位,然後使eax同0x10000000相加。如果相加的結果造成符号位溢出,則會使0x401474處的jno指令不發生跳轉,然後再做一次SMC解碼,得到正确的顯示結果:"GOOD JOB"

Crackmes.de上borismilner的一個crackme
.text:00401460 mov     byte ptr loc_409000, 10h
...
.text:00401476 sub     byte ptr loc_409000, 7