1. 程式人生 > >IOLI crackme分析——從應用中學習使用radare2

IOLI crackme分析——從應用中學習使用radare2

Crackme0x00 - writeup

我現在開始看radare2book了,現在剛看1/3,有些無聊,因為之前也看過一些radare2的例項講解,所以現在先試著做一下里面的crackme練習。

先執行一下craceme0x00這個檔案,看來是要把密碼找出來。

[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x00
IOLI Crackme Level 0x00
Password: 1234
Invalid Password!

現在看一下string,使用iz命令。

[email protected]:~/IOLIcrackme/bin-linux# radare2 -A crackme0x00
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Type matching analysis for all functions (afta)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x08048360]> iz?
| iz|izj             Strings in data sections (in JSON/Base64)
| izz                Search for Strings in the whole binary
| izzz               Dump Strings from whole binary to r2 shell (for huge files)
| iz- [addr]         Purge string via bin.strpurge
[0x08048360]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x00000568 0x08048568  24  25 (.rodata) ascii IOLI Crackme Level 0x00\n
001 0x00000581 0x08048581  10  11 (.rodata) ascii Password: 
002 0x0000058f 0x0804858f   6   7 (.rodata) ascii 250382
003 0x00000596 0x08048596  18  19 (.rodata) ascii Invalid Password!\n
004 0x000005a9 0x080485a9  15  16 (.rodata) ascii Password OK :)\n

好吧,我覺得我找到密碼了,雖然不太肯定,但是可以試一下——250382.

[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x00
IOLI Crackme Level 0x00
Password: 250382
Password OK :)

成功。

看來真的只是一個簡單的熱身。


Crackme0x01 - writeup

還是先執行一下(其實我也不太確定是不是每次都要先執行,因為如果是真的maleware可能執行之後就直接game over了,不過這裡只是簡單的練習,所以就不考慮這個問題了。)

[email protected]
:~/IOLIcrackme/bin-linux# ./crackme0x01 IOLI Crackme Level 0x01 Password: 12345 Invalid Password!

好吧,仍舊是密碼。

那麼進入radare2,還是檢視string。

[0x08048330]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x00000528 0x08048528  24  25 (.rodata) ascii IOLI Crackme Level 0x01\n
001 0x00000541 0x08048541  10  11 (.rodata) ascii Password: 
002 0x0000054f 0x0804854f  18  19 (.rodata) ascii Invalid Password!\n
003 0x00000562 0x08048562  15  16 (.rodata) ascii Password OK :)\n

這次沒有直接顯示了,但是顯然“Password OK”是在告訴我們密碼驗證通過了,現在看一下這個字串在哪裡引用了

[0x08048330]> axt 0x08048562
main 0x8048442 [DATA] mov dword [esp], str.Password_OK_:

是在main函式裡面,直接跳到main函式,然後pdf一下

[0x08048330]> pdf?
Usage: pdf[bf]   disassemble function
| pdf   disassemble function
| pdfs  disassemble function summary
[0x08048330]> s main
[0x080483e4]> pdf
/ (fcn) main 113
|   main (int argc, char **argv, char **envp);
|           ; var unsigned int local_4h @ ebp-0x4
|           ; var int local_4h_2 @ esp+0x4
|           ; DATA XREF from entry0 (0x8048347)
|           0x080483e4      55             push ebp
|           0x080483e5      89e5           mov ebp, esp
|           0x080483e7      83ec18         sub esp, 0x18
|           0x080483ea      83e4f0         and esp, 0xfffffff0
|           0x080483ed      b800000000     mov eax, 0
|           0x080483f2      83c00f         add eax, 0xf
|           0x080483f5      83c00f         add eax, 0xf
|           0x080483f8      c1e804         shr eax, 4
|           0x080483fb      c1e004         shl eax, 4
|           0x080483fe      29c4           sub esp, eax
|           0x08048400      c70424288504.  mov dword [esp], str.IOLI_Crackme_Level_0x01 ; [0x8048528:4]=0x494c4f49 ; "IOLI Crackme Level 0x01\n" ; const char *format
|           0x08048407      e810ffffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804840c      c70424418504.  mov dword [esp], str.Password: ; [0x8048541:4]=0x73736150 ; "Password: " ; const char *format
|           0x08048413      e804ffffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048418      8d45fc         lea eax, dword [local_4h]
|           0x0804841b      89442404       mov dword [local_4h_2], eax
|           0x0804841f      c704244c8504.  mov dword [esp], 0x804854c  ; [0x804854c:4]=0x49006425 ; const char *format
|           0x08048426      e8e1feffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x0804842b      817dfc9a1400.  cmp dword [local_4h], 0x149a
|       ,=< 0x08048432      740e           je 0x8048442
|       |   0x08048434      c704244f8504.  mov dword [esp], str.Invalid_Password ; [0x804854f:4]=0x61766e49 ; "Invalid Password!\n" ; const char *format
|       |   0x0804843b      e8dcfeffff     call sym.imp.printf         ; int printf(const char *format)
|      ,==< 0x08048440      eb0c           jmp 0x804844e
|      ||   ; CODE XREF from main (0x8048432)
|      |`-> 0x08048442      c70424628504.  mov dword [esp], str.Password_OK_: ; [0x8048562:4]=0x73736150 ; "Password OK :)\n" ; const char *format
|      |    0x08048449      e8cefeffff     call sym.imp.printf         ; int printf(const char *format)
|      |    ; CODE XREF from main (0x8048440)
|      `--> 0x0804844e      b800000000     mov eax, 0
|           0x08048453      c9             leave
\           0x08048454      c3             ret

注意高亮程式碼,前面是讀入使用者輸入的密碼,高亮部分進行了判斷,然後跳轉輸出密碼是否正確的提示。

所以使用者輸入的程式碼是和0x149a進行了比較,so,密碼是

[0x080483e4]> ? 0x149a
hex     0x149a
octal   012232
unit    5.2K
segment 0000:049a
int32   5274
string  "\x9a\x14"
binary  0b0001010010011010
fvalue: 5274.0
float:  0.000000f
double: 0.000000
trits   0t21020100
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x01
IOLI Crackme Level 0x01
Password: 5274
Password OK :)

5274


Crackme0x02 – writeup

這裡就不再單獨說程式執行的結果了,我之前也沒了解過,IOLIcrackme都是查驗密碼的,直接進入radare2裡面了。

前面的步驟都是一樣的,這裡直接貼pdf 的結果了。

[0x080483e4]> pdf
/ (fcn) main 144
|   main (int argc, char **argv, char **envp);
|           ; var unsigned int local_ch @ ebp-0xc
|           ; var signed int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; var int local_4h_2 @ esp+0x4
|           ; DATA XREF from entry0 (0x8048347)
|           0x080483e4      55             push ebp
|           0x080483e5      89e5           mov ebp, esp
|           0x080483e7      83ec18         sub esp, 0x18
|           0x080483ea      83e4f0         and esp, 0xfffffff0
|           0x080483ed      b800000000     mov eax, 0
|           0x080483f2      83c00f         add eax, 0xf
|           0x080483f5      83c00f         add eax, 0xf
|           0x080483f8      c1e804         shr eax, 4
|           0x080483fb      c1e004         shl eax, 4
|           0x080483fe      29c4           sub esp, eax
|           0x08048400      c70424488504.  mov dword [esp], str.IOLI_Crackme_Level_0x02 ; [0x8048548:4]=0x494c4f49 ; "IOLI Crackme Level 0x02\n" ; const char *format
|           0x08048407      e810ffffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804840c      c70424618504.  mov dword [esp], str.Password: ; [0x8048561:4]=0x73736150 ; "Password: " ; const char *format
|           0x08048413      e804ffffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048418      8d45fc         lea eax, dword [local_4h]
|           0x0804841b      89442404       mov dword [local_4h_2], eax
|           0x0804841f      c704246c8504.  mov dword [esp], 0x804856c  ; [0x804856c:4]=0x50006425 ; const char *format
|           0x08048426      e8e1feffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x0804842b      c745f85a0000.  mov dword [local_8h], 0x5a  ; 'Z' ; 90
|           0x08048432      c745f4ec0100.  mov dword [local_ch], 0x1ec ; 492
|           0x08048439      8b55f4         mov edx, dword [local_ch]
|           0x0804843c      8d45f8         lea eax, dword [local_8h]
|           0x0804843f      0110           add dword [eax], edx
|           0x08048441      8b45f8         mov eax, dword [local_8h]
|           0x08048444      0faf45f8       imul eax, dword [local_8h]
|           0x08048448      8945f4         mov dword [local_ch], eax
|           0x0804844b      8b45fc         mov eax, dword [local_4h]
|           0x0804844e      3b45f4         cmp eax, dword [local_ch]
|       ,=< 0x08048451      750e           jne 0x8048461
|       |   0x08048453      c704246f8504.  mov dword [esp], str.Password_OK_: ; [0x804856f:4]=0x73736150 ; "Password OK :)\n" ; const char *format
|       |   0x0804845a      e8bdfeffff     call sym.imp.printf         ; int printf(const char *format)
|      ,==< 0x0804845f      eb0c           jmp 0x804846d
|      ||   ; CODE XREF from main (0x8048451)
|      |`-> 0x08048461      c704247f8504.  mov dword [esp], str.Invalid_Password ; [0x804857f:4]=0x61766e49 ; "Invalid Password!\n" ; const char *format
|      |    0x08048468      e8affeffff     call sym.imp.printf         ; int printf(const char *format)
|      |    ; CODE XREF from main (0x804845f)
|      `--> 0x0804846d      b800000000     mov eax, 0
|           0x08048472      c9             leave
\           0x08048473      c3             ret

仍舊關注高亮部分。這裡進行了一些數學運算,然後用結果與使用者輸入的密碼進行比較,所以關鍵就是運算結果。

其實這個運算挺簡單的,看程式碼就可以很快看出來:(90+492)*(90+492)=338724.

可以在除錯模式下驗證一下(這裡的教程我還沒看,完全是根據help自己找的方法,所以可能比較笨一點。)

在除錯模式(r2 -Ad crackme0x02)下,把斷點設定在0x0804844b(db 0x0804844b),然後執行(dc),這時檢視暫存器的值(dr)

[0xf7f270b0]> db 0x0804844b
[0xf7f270b0]> dc
IOLI Crackme Level 0x02
Password: 1234
hit breakpoint at: 804844b
[0x0804844b]> dr
eax = 0x00052b24
ebx = 0x00000000
ecx = 0xff844340
edx = 0x000001ec
esi = 0xf7ef9000
edi = 0xf7ef9000
esp = 0xff8447a0
ebp = 0xff8447c8
eip = 0x0804844b
eflags = 0x00000206
oeax = 0xffffffff
[0x0804844b]> ? 0x00052b24
hex     0x52b24
octal   01225444
unit    330.8K
segment 5000:0b24
int32   338724
string  "$+\x05"
binary  0b000001010010101100100100
fvalue: 338724.0
float:  0.000000f
double: 0.000000
trits   0t122012122100

確實是338724,再執行一下程式測試一下

[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x02
IOLI Crackme Level 0x02
Password: 338724
Password OK :)

成功!


Crackme0x03 – writeup

進入radare2之後,iz的結果出現了變化

[0x08048360]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x000005ec 0x080485ec  17  18 (.rodata) ascii Lqydolg#Sdvvzrug$
001 0x000005fe 0x080485fe  17  18 (.rodata) ascii Sdvvzrug#RN$$$#=,
002 0x00000610 0x08048610  24  25 (.rodata) ascii IOLI Crackme Level 0x03\n
003 0x00000629 0x08048629  10  11 (.rodata) ascii Password: 

先看一下main函式吧

            ;-- main:
/ (fcn) sym.main 128
|   sym.main (int argc, char **argv, char **envp);
|           ; var int local_ch @ ebp-0xc
|           ; var signed int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; var int local_4h_2 @ esp+0x4
|           ; DATA XREF from entry0 (0x8048377)
|           0x08048498      55             push ebp
|           0x08048499      89e5           mov ebp, esp
|           0x0804849b      83ec18         sub esp, 0x18
|           0x0804849e      83e4f0         and esp, 0xfffffff0
|           0x080484a1      b800000000     mov eax, 0
|           0x080484a6      83c00f         add eax, 0xf
|           0x080484a9      83c00f         add eax, 0xf
|           0x080484ac      c1e804         shr eax, 4
|           0x080484af      c1e004         shl eax, 4
|           0x080484b2      29c4           sub esp, eax
|           0x080484b4      c70424108604.  mov dword [esp], str.IOLI_Crackme_Level_0x03 ; [0x8048610:4]=0x494c4f49 ; "IOLI Crackme Level 0x03\n" ; const char *format
|           0x080484bb      e890feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x080484c0      c70424298604.  mov dword [esp], str.Password: ; [0x8048629:4]=0x73736150 ; "Password: " ; const char *format
|           0x080484c7      e884feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x080484cc      8d45fc         lea eax, dword [local_4h]
|           0x080484cf      89442404       mov dword [local_4h_2], eax
|           0x080484d3      c70424348604.  mov dword [esp], 0x8048634  ; [0x8048634:4]=0x6425 ; const char *format
|           0x080484da      e851feffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x080484df      c745f85a0000.  mov dword [local_8h], 0x5a  ; 'Z' ; 90
|           0x080484e6      c745f4ec0100.  mov dword [local_ch], 0x1ec ; 492
|           0x080484ed      8b55f4         mov edx, dword [local_ch]
|           0x080484f0      8d45f8         lea eax, dword [local_8h]
|           0x080484f3      0110           add dword [eax], edx
|           0x080484f5      8b45f8         mov eax, dword [local_8h]
|           0x080484f8      0faf45f8       imul eax, dword [local_8h]
|           0x080484fc      8945f4         mov dword [local_ch], eax
|           0x080484ff      8b45f4         mov eax, dword [local_ch]
|           0x08048502      89442404       mov dword [local_4h_2], eax
|           0x08048506      8b45fc         mov eax, dword [local_4h]
|           0x08048509      890424         mov dword [esp], eax
|           0x0804850c      e85dffffff     call sym.test
|           0x08048511      b800000000     mov eax, 0
|           0x08048516      c9             leave
\           0x08048517      c3             ret

發現最後呼叫了sym.test函式,在呼叫test函式之前還進行了一些數學運算,和crackme0x02中的數學運算時一樣的,結果(338724)與使用者輸入密碼一起作為sym.test的引數,我們來看一下test函式:

[0x08048498]> s sym.test
[0x0804846e]> pdf
/ (fcn) sym.test 42
|   sym.test (int arg_8h, unsigned int arg_ch);
|           ; arg int arg_8h @ ebp+0x8
|           ; arg unsigned int arg_ch @ ebp+0xc
|           ; CALL XREF from sym.main (0x804850c)
|           0x0804846e      55             push ebp
|           0x0804846f      89e5           mov ebp, esp
|           0x08048471      83ec08         sub esp, 8
|           0x08048474      8b4508         mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8
|           0x08048477      3b450c         cmp eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12
|       ,=< 0x0804847a      740e           je 0x804848a
|       |   0x0804847c      c70424ec8504.  mov dword [esp], str.Lqydolg_Sdvvzrug ; [0x80485ec:4]=0x6479714c ; "Lqydolg#Sdvvzrug$"
|       |   0x08048483      e88cffffff     call sym.shift
|      ,==< 0x08048488      eb0c           jmp 0x8048496
|      ||   ; CODE XREF from sym.test (0x804847a)
|      |`-> 0x0804848a      c70424fe8504.  mov dword [esp], str.Sdvvzrug_RN ; [0x80485fe:4]=0x76766453 ; "Sdvvzrug#RN$$$#=,"
|      |    0x08048491      e87effffff     call sym.shift
|      |    ; CODE XREF from sym.test (0x8048488)
|      `--> 0x08048496      c9             leave
\           0x08048497      c3             ret

嗯…338724與使用者輸入進行了比較,之後呼叫了sym.shift函式,根據比較結果不同,shift函式的引數也不同(所以shift函式只有一個引數,我有了一個猜測…),下面看一下shift函式:

[0x0804846e]> pdf @sym.shift
/ (fcn) sym.shift 90
|   sym.shift (char *s);
|           ; var unsigned int local_7ch @ ebp-0x7c
|           ; var int local_78h @ ebp-0x78
|           ; arg char *s @ ebp+0x8
|           ; var int local_4h @ esp+0x4
|           ; CALL XREFS from sym.test (0x8048483, 0x8048491)
|           0x08048414      55             push ebp
|           0x08048415      89e5           mov ebp, esp
|           0x08048417      81ec98000000   sub esp, 0x98
|           0x0804841d      c74584000000.  mov dword [local_7ch], 0
|           ; CODE XREF from sym.shift (0x804844e)
|       .-> 0x08048424      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|       :   0x08048427      890424         mov dword [esp], eax        ; const char *s
|       :   0x0804842a      e811ffffff     call sym.imp.strlen         ; size_t strlen(const char *s)
|       :   0x0804842f      394584         cmp dword [local_7ch], eax
|      ,==< 0x08048432      731c           jae 0x8048450
|      |:   0x08048434      8d4588         lea eax, dword [local_78h]
|      |:   0x08048437      89c2           mov edx, eax
|      |:   0x08048439      035584         add edx, dword [local_7ch]
|      |:   0x0804843c      8b4584         mov eax, dword [local_7ch]
|      |:   0x0804843f      034508         add eax, dword [s]
|      |:   0x08048442      0fb600         movzx eax, byte [eax]
|      |:   0x08048445      2c03           sub al, 3
|      |:   0x08048447      8802           mov byte [edx], al
|      |:   0x08048449      8d4584         lea eax, dword [local_7ch]
|      |:   0x0804844c      ff00           inc dword [eax]
|      |`=< 0x0804844e      ebd4           jmp 0x8048424
|      |    ; CODE XREF from sym.shift (0x8048432)
|      `--> 0x08048450      8d4588         lea eax, dword [local_78h]
|           0x08048453      034584         add eax, dword [local_7ch]
|           0x08048456      c60000         mov byte [eax], 0
|           0x08048459      8d4588         lea eax, dword [local_78h]
|           0x0804845c      89442404       mov dword [local_4h], eax
|           0x08048460      c70424e88504.  mov dword [esp], 0x80485e8  ; [0x80485e8:4]=0xa7325 ; const char *format
|           0x08048467      e8e4feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804846c      c9             leave
\           0x0804846d      c3             ret

確實,shift只有一個引數,主體進行了一些數學運算,然後呼叫了printf函式。

回憶一下程式的功能,輸出提示資訊 -> 使用者輸入密碼 -> 程式判斷密碼正確性 -> 輸出提示資訊

而這裡shift函式只是根據338724與使用者輸入的比較結果,輸出不同的資訊,所以,我們完全不需要知道shift這裡到底做了什麼,密碼應該就是338724。而且應該也能猜到,shift這裡就是把一開始我們通過iz得知的那兩個像是亂碼一樣的字串做了一些變換,生成最後的提示資訊。

[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x03
IOLI Crackme Level 0x03
Password: 338724
Password OK!!! :)

Bingo!


Crackme0x04 – writeup

這個程式的iz結果是正常的

[0x080483d0]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x0000063b 0x0804863b  13  14 (.rodata) ascii Password OK!\n
001 0x00000649 0x08048649  20  21 (.rodata) ascii Password Incorrect!\n
002 0x0000065e 0x0804865e  24  25 (.rodata) ascii IOLI Crackme Level 0x04\n
003 0x00000677 0x08048677  10  11 (.rodata) ascii Password: 

main函式中,使用者輸入密碼後,程式直接呼叫了check函式。

            ;-- main:
/ (fcn) sym.main 92
|   sym.main (int argc, char **argv, char **envp);
|           ; var int local_78h @ ebp-0x78
|           ; var int local_4h @ esp+0x4
|           ; DATA XREF from entry0 (0x80483e7)
|           0x08048509      55             push ebp
|           0x0804850a      89e5           mov ebp, esp
|           0x0804850c      81ec88000000   sub esp, 0x88
|           0x08048512      83e4f0         and esp, 0xfffffff0
|           0x08048515      b800000000     mov eax, 0
|           0x0804851a      83c00f         add eax, 0xf
|           0x0804851d      83c00f         add eax, 0xf
|           0x08048520      c1e804         shr eax, 4
|           0x08048523      c1e004         shl eax, 4
|           0x08048526      29c4           sub esp, eax
|           0x08048528      c704245e8604.  mov dword [esp], str.IOLI_Crackme_Level_0x04 ; [0x804865e:4]=0x494c4f49 ; "IOLI Crackme Level 0x04\n" ; const char *format
|           0x0804852f      e860feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048534      c70424778604.  mov dword [esp], str.Password: ; [0x8048677:4]=0x73736150 ; "Password: " ; const char *format
|           0x0804853b      e854feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048540      8d4588         lea eax, dword [local_78h]
|           0x08048543      89442404       mov dword [local_4h], eax
|           0x08048547      c70424828604.  mov dword [esp], 0x8048682  ; [0x8048682:4]=0x7325 ; const char *format
|           0x0804854e      e821feffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x08048553      8d4588         lea eax, dword [local_78h]
|           0x08048556      890424         mov dword [esp], eax
|           0x08048559      e826ffffff     call sym.check
|           0x0804855e      b800000000     mov eax, 0
|           0x08048563      c9             leave
\           0x08048564      c3             ret

下面看check函式幹了些什麼

[0x08048509]> pdf @sym.check
/ (fcn) sym.check 133
|   sym.check (char *s);
|           ; var char *local_dh @ ebp-0xd
|           ; var unsigned int local_ch @ ebp-0xc
|           ; var unsigned int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg char *s @ ebp+0x8
|           ; var char *format @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from sym.main (0x8048559)
|           0x08048484      55             push ebp
|           0x08048485      89e5           mov ebp, esp
|           0x08048487      83ec28         sub esp, 0x28               ; '('
|           0x0804848a      c745f8000000.  mov dword [local_8h], 0
|           0x08048491      c745f4000000.  mov dword [local_ch], 0
|           ; CODE XREF from sym.check (0x80484f9)
|       .-> 0x08048498      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|       :   0x0804849b      890424         mov dword [esp], eax        ; const char *s
|       :   0x0804849e      e8e1feffff     call sym.imp.strlen         ; size_t strlen(const char *s)
|       :   0x080484a3      3945f4         cmp dword [local_ch], eax   ; local_ch: 當前檢查第幾個字元
|      ,==< 0x080484a6      7353           jae 0x80484fb
|      |:   0x080484a8      8b45f4         mov eax, dword [local_ch]
|      |:   0x080484ab      034508         add eax, dword [s]
|      |:   0x080484ae      0fb600         movzx eax, byte [eax]
|      |:   0x080484b1      8845f3         mov byte [local_dh], al    ; local_dh: 當前檢查的字元
|      |:   0x080484b4      8d45fc         lea eax, dword [local_4h]
|      |:   0x080484b7      89442408       mov dword [local_8h_2], eax ; local_4h/local_8h_2: sscanf的第三個引數,儲存格式化結果
|      |:   0x080484bb      c74424043886.  mov dword [format], 0x8048638 ; format: sscanf的第二個引數,格式
|      |:   0x080484c3      8d45f3         lea eax, dword [local_dh]
|      |:   0x080484c6      890424         mov dword [esp], eax        ; const char *s
|      |:   0x080484c9      e8d6feffff     call sym.imp.sscanf         ; sscanf對當前檢查字元進行了格式化,由字元轉化為整型數字
|      |:   0x080484ce      8b55fc         mov edx, dword [local_4h]
|      |:   0x080484d1      8d45f8         lea eax, dword [local_8h]
|      |:   0x080484d4      0110           add dword [eax], edx
|      |:   0x080484d6      837df80f       cmp dword [local_8h], 0xf   ; local_8h: 對格式化結果求和,並與0xf比較,若相等,則通過
|     ,===< 0x080484da      7518           jne 0x80484f4
|     ||:   0x080484dc      c704243b8604.  mov dword [esp], str.Password_OK ; [0x804863b:4]=0x73736150 ; "Password OK!\n" ; const char *format
|     ||:   0x080484e3      e8acfeffff     call sym.imp.printf         ; int printf(const char *format)
|     ||:   0x080484e8      c70424000000.  mov dword [esp], 0          ; int status
|     ||:   0x080484ef      e8c0feffff     call sym.imp.exit           ; void exit(int status)
|     ||:   ; CODE XREF from sym.check (0x80484da)
|     `---> 0x080484f4      8d45f4         lea eax, dword [local_ch]
|      |:   0x080484f7      ff00           inc dword [eax]
|      |`=< 0x080484f9      eb9d           jmp 0x8048498
|      |    ; CODE XREF from sym.check (0x80484a6)
|      `--> 0x080484fb      c70424498604.  mov dword [esp], str.Password_Incorrect ; [0x8048649:4]=0x73736150 ; "Password Incorrect!\n" ; const char *format
|           0x08048502      e88dfeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048507      c9             leave
\           0x08048508      c3             ret

我在程式碼上註解了各區域性變數的意義,注意check函式中呼叫了sscanf函式,其中第二個引數是0x8048638,儲存的是轉換格式,我們看一下是什麼

[0x08048509]> ps 15 @0x8048638
%d\x00Password OK!

所以sscanf就是把數字字元轉換成了數字,以方便之後的求和運算。

總結一下,check函式逐個檢查使用者輸入的字串,首先將字元轉換為數字,逐步求和直到和為15,則密碼通過;如果直到檢查到字串結束都不能得到和為15的結果,則密碼錯誤。

所以這個程式的正確密碼應該有無限個,因為它在和為15之後並不會處理之後的字元,而是直接通過,讓我們來試一下

[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 1234
Password Incorrect!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 555
Password OK!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 555555555555555
Password OK!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 5541
Password OK!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 55414365474
Password OK!

可以看到,前N個數字和為15的密碼均可以通過測試。


Crackme0x05 - writeup

這個程式和cackme0x04很像,也是在main函式中呼叫check函式,我們直接看check函式

/ (fcn) sym.check 120
|   sym.check (char *s);
|           ; var char *local_dh @ ebp-0xd
|           ; var unsigned int local_ch @ ebp-0xc
|           ; var unsigned int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg char *s @ ebp+0x8
|           ; var char *format @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from sym.main (0x8048590)
|           0x080484c8      55             push ebp
|           0x080484c9      89e5           mov ebp, esp
|           0x080484cb      83ec28         sub esp, 0x28               ; '('
|           0x080484ce      c745f8000000.  mov dword [local_8h], 0
|           0x080484d5      c745f4000000.  mov dword [local_ch], 0
|           ; CODE XREF from sym.check (0x8048530)
|       .-> 0x080484dc      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|       :   0x080484df      890424         mov dword [esp], eax        ; const char *s
|       :   0x080484e2      e89dfeffff     call sym.imp.strlen         ; size_t strlen(const char *s)
|       :   0x080484e7      3945f4         cmp dword [local_ch], eax
|      ,==< 0x080484ea      7346           jae 0x8048532
|      |:   0x080484ec      8b45f4         mov eax, dword [local_ch]
|      |:   0x080484ef      034508         add eax, dword [s]
|      |:   0x080484f2      0fb600         movzx eax, byte [eax]
|      |:   0x080484f5      8845f3         mov byte [local_dh], al
|      |:   0x080484f8      8d45fc         lea eax, dword [local_4h]
|      |:   0x080484fb      89442408       mov dword [local_8h_2], eax ;   ...
|      |:   0x080484ff      c74424046886.  mov dword [format], 0x8048668 ; [0x8048668:4]=0x50006425 ; const char *format
|      |:   0x08048507      8d45f3         lea eax, dword [local_dh]
|      |:   0x0804850a      890424         mov dword [esp], eax        ; const char *s
|      |:   0x0804850d      e892feffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
|      |:   0x08048512      8b55fc         mov edx, dword [local_4h]
|      |:   0x08048515      8d45f8         lea eax, dword [local_8h]
|      |:   0x08048518      0110           add dword [eax], edx
|      |:   0x0804851a      837df810       cmp dword [local_8h], 0x10
|     ,===< 0x0804851e      750b           jne 0x804852b
|     ||:   0x08048520      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|     ||:   0x08048523      890424         mov dword [esp], eax
|     ||:   0x08048526      e859ffffff     call sym.parell
|     ||:   ; CODE XREF from sym.check (0x804851e)
|     `---> 0x0804852b      8d45f4         lea eax, dword [local_ch]
|      |:   0x0804852e      ff00           inc dword [eax]
|      |`=< 0x08048530      ebaa           jmp 0x80484dc
|      |    ; CODE XREF from sym.check (0x80484ea)
|      `--> 0x08048532      c70424798604.  mov dword [esp], str.Password_Incorrect ; [0x8048679:4]=0x73736150 ; "Password Incorrect!\n" ; const char *format
|           0x08048539      e856feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804853e      c9             leave
\           0x0804853f      c3             ret

可以看到check函式仍舊是呼叫了sscanf函式,然後求和,只不過這次是和0x10比較,如果相等,函式再一次呼叫了parell函式,引數為使用者輸入的密碼,看一下parell函式:

[0x08048540]> pdf @sym.parell
/ (fcn) sym.parell 68
|   sym.parell (char *s);
|           ; var int local_4h @ ebp-0x4
|           ; arg char *s @ ebp+0x8
|           ; var char *format @ esp+0x4
|           ; var int local_8h @ esp+0x8
|           ; CALL XREF from sym.check (0x8048526)
|           0x08048484      55             push ebp
|           0x08048485      89e5           mov ebp, esp
|           0x08048487      83ec18         sub esp, 0x18
|           0x0804848a      8d45fc         lea eax, dword [local_4h]
|           0x0804848d      89442408       mov dword [local_8h], eax   ;   ...
|           0x08048491      c74424046886.  mov dword [format], 0x8048668 ; [0x8048668:4]=0x50006425 ; const char *format
|           0x08048499      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|           0x0804849c      890424         mov dword [esp], eax        ; const char *s
|           0x0804849f      e800ffffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
|           0x080484a4      8b45fc         mov eax, dword [local_4h]
|           0x080484a7      83e001         and eax, 1
|           0x080484aa      85c0           test eax, eax
|       ,=< 0x080484ac      7518           jne 0x80484c6
|       |   0x080484ae      c704246b8604.  mov dword [esp], str.Password_OK ; [0x804866b:4]=0x73736150 ; "Password OK!\n" ; const char *format
|       |   0x080484b5      e8dafeffff     call sym.imp.printf         ; int printf(const char *format)
|       |   0x080484ba      c70424000000.  mov dword [esp], 0          ; int status
|       |   0x080484c1      e8eefeffff     call sym.imp.exit           ; void exit(int status)
|       |   ; CODE XREF from sym.parell (0x80484ac)
|       `-> 0x080484c6      c9             leave
\           0x080484c7      c3             ret

parell函式裡,對使用者輸入的密碼進行了格式轉換,轉換為了整型數字。注意高亮部分,通過這裡的運算,判斷使用者輸入密碼是否為偶數,若為偶數,則密碼通過。

總結一下密碼的要求:

  1. 偶數
  2. 前N個數字和為16
  3. 最大不超過int的範圍(-2147483648~2147483647)

測試一下:

[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 4156
Password OK!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 41561
Password Incorrect!  奇數
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 41562
Password OK!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 2147483646
Password Incorrect!  前N位和不是15
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 2147283646
Password OK!
[email protected]:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 2237283646
Password Incorrect!  超過int範圍

 


Crackme0x06 – writeup

這個程式裡面的函式呼叫流程和crackme0x05差不多,但是還是有一些不同,看一下main函式和check函式

            ;-- main:
/ (fcn) sym.main 99
|   sym.main (int argc, char **argv, char **envp);
|           ; var int local_78h @ ebp-0x78
|           ; arg int arg_10h @ ebp+0x10
|           ; var int local_4h @ esp+0x4
|           ; DATA XREF from entry0 (0x8048417)
|           0x08048607      55             push ebp
|           0x08048608      89e5           mov ebp, esp
|           0x0804860a      81ec88000000   sub esp, 0x88
|           0x08048610      83e4f0         and esp, 0xfffffff0
|           0x08048613      b800000000     mov eax, 0
|           0x08048618      83c00f         add eax, 0xf
|           0x0804861b      83c00f         add eax, 0xf
|           0x0804861e      c1e804         shr eax, 4
|           0x08048621      c1e004         shl eax, 4
|           0x08048624      29c4           sub esp, eax
|           0x08048626      c70424638704.  mov dword [esp], str.IOLI_Crackme_Level_0x06 ; [0x8048763:4]=0x494c4f49 ; "IOLI Crackme Level 0x06\n" ; const char *format
|           0x0804862d      e886fdffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048632      c704247c8704.  mov dword [esp], str.Password: ; [0x804877c:4]=0x73736150 ; "Password: " ; const char *format
|           0x08048639      e87afdffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804863e      8d4588         lea eax, dword [local_78h]
|           0x08048641      89442404       mov dword [local_4h], eax
|           0x08048645      c70424878704.  mov dword [esp], 0x8048787  ; [0x8048787:4]=0x7325 ; const char *format
|           0x0804864c      e847fdffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x08048651      8b4510         mov eax, dword [arg_10h]    ; [0x10:4]=-1 ; 16
|           0x08048654      89442404       mov dword [local_4h], eax
|           0x08048658      8d4588         lea eax, dword [local_78h]
|           0x0804865b      890424         mov dword [esp], eax
|           0x0804865e      e825ffffff     call sym.check
|           0x08048663      b800000000     mov eax, 0
|           0x08048668      c9             leave
\           0x08048669      c3             ret
[0x08048400]> pdf @sym.check
/ (fcn) sym.check 127
|   sym.check (char *s, int arg_ch);
|           ; var char *local_dh @ ebp-0xd
|           ; var unsigned int local_ch @ ebp-0xc
|           ; var unsigned int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg char *s @ ebp+0x8
|           ; arg int arg_ch @ ebp+0xc
|           ; var char *format @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from sym.main (0x804865e)
|           0x08048588      55             push ebp
|           0x08048589      89e5           mov ebp, esp
|           0x0804858b      83ec28         sub esp, 0x28               ; '('
|           0x0804858e      c745f8000000.  mov dword [local_8h], 0
|           0x08048595      c745f4000000.  mov dword [local_ch], 0
|           ; CODE XREF from sym.check (0x80485f7)
|       .-> 0x0804859c      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|       :   0x0804859f      890424         mov dword [esp], eax        ; const char *s
|       :   0x080485a2      e801feffff     call sym.imp.strlen         ; size_t strlen(const char *s)
|       :   0x080485a7      3945f4         cmp dword [local_ch], eax
|      ,==< 0x080485aa      734d           jae 0x80485f9
|      |:   0x080485ac      8b45f4         mov eax, dword [local_ch]
|      |:   0x080485af      034508         add eax, dword [s]
|      |:   0x080485b2      0fb600         movzx eax, byte [eax]
|      |:   0x080485b5      8845f3         mov byte [local_dh], al
|      |:   0x080485b8      8d45fc         lea eax, dword [local_4h]
|      |:   0x080485bb      89442408       mov dword [local_8h_2], eax ;   ...
|      |:   0x080485bf      c74424043d87.  mov dword [format], 0x804873d ; [0x804873d:4]=0x50006425 ; const char *format
|      |:   0x080485c7      8d45f3         lea eax, dword [local_dh]
|      |:   0x080485ca      890424         mov dword [esp], eax        ; const char *s
|      |:   0x080485cd      e8f6fdffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
|      |:   0x080485d2      8b55fc         mov edx, dword [local_4h]
|      |:   0x080485d5      8d45f8         lea eax, dword [local_8h]
|      |:   0x080485d8      0110           add dword [eax], edx
|      |:   0x080485da      837df810       cmp dword [local_8h], 0x10
|     ,===< 0x080485de      7512           jne 0x80485f2
|     ||:   0x080485e0      8b450c         mov eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12
|     ||:   0x080485e3      89442404       mov dword [format], eax
|     ||:   0x080485e7      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|     ||:   0x080485ea      890424         mov dword [esp], eax
|     ||:   0x080485ed      e828ffffff     call sym.parell
|     ||:   ; CODE XREF from sym.check (0x80485de)
|     `---> 0x080485f2      8d45f4         lea eax, dword [local_ch]
|      |:   0x080485f5      ff00           inc dword [eax]
|      |`=< 0x080485f7      eba3           jmp 0x804859c
|      |    ; CODE XREF from sym.check (0x80485aa)
|      `--> 0x080485f9      c704244e8704.  mov dword [esp], str.Password_Incorrect ; [0x804874e:4]=0x73736150 ; "Password Incorrect!\n" ; const char *format
|           0x08048600      e8b3fdffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048605      c9             leave
\           0x08048606      c3             ret

注意高亮部分,check函式有兩個引數,第一個引數仍舊是使用者輸入的密碼,第二個字串是main函式的引數ebp+0x10,那麼這是哪個引數呢?

注意main(int argc, char **argv, char **envp)的棧結構:

ebp+0x10

envp

ebp+0xc

argv

ebp+0x8

argc

ebp+0x4

Return address

ebp

Previous ebp

所以check函式多了一個引數——程式執行的環境變數,除此之外,main -> check -> parell的函式呼叫流程,以及函式內部的操作沒有變化,下面看parell函式

[0x08048400]> pdf @sym.parell
/ (fcn) sym.parell 110
|   sym.parell (char *s, char *arg_ch);
|           ; var signed int local_8h_2 @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg char *s @ ebp+0x8
|           ; arg char *arg_ch @ ebp+0xc
|           ; var char *format @ esp+0x4
|           ; var int local_8h @ esp+0x8
|           ; CALL XREF from sym.check (0x80485ed)
|           0x0804851a      55             push ebp
|           0x0804851b      89e5           mov ebp, esp
|           0x0804851d      83ec18         sub esp, 0x18
|           0x08048520      8d45fc         lea eax, dword [local_4h]
|           0x08048523      89442408       mov dword [local_8h], eax   ;   ...
|           0x08048527      c74424043d87.  mov dword [format], 0x804873d ; [0x804873d:4]=0x50006425 ; const char *format
|           0x0804852f      8b4508         mov eax, dword [s]          ; [0x8:4]=-1 ; 8
|           0x08048532      890424         mov dword [esp], eax        ; const char *s
|           0x08048535      e88efeffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
|           0x0804853a      8b450c         mov eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12
|           0x0804853d      89442404       mov dword [format], eax
|           0x08048541      8b45fc         mov eax, dword [local_4h]
|           0x08048544      890424         mov dword [esp], eax
|           0x08048547      e868ffffff     call sym.dummy
|           0x0804854c      85c0           test eax, eax
|       ,=< 0x0804854e      7436           je 0x8048586
|       |   0x08048550      c745f8000000.  mov dword [local_8h_2], 0
|       |   ; CODE XREF from sym.parell (0x8048584)
|      .--> 0x08048557      837df809       cmp dword [local_8h_2], 9
|     ,===< 0x0804855b      7f29           jg 0x8048586
|     |:|   0x0804855d      8b45fc         mov eax, dword [local_4h]
|     |:|   0x08048560      83e001         and eax, 1
|     |:|   0x08048563      85c0           test eax, eax               ; 檢查使用者輸入密碼是否為偶數
|    ,====< 0x08048565      7518           jne 0x804857f
|    ||:|   0x08048567      c70424408704.  mov dword [esp], str.Password_OK ; [0x8048740:4]=0x73736150 ; "Password OK!\n" ; const char *format
|    ||:|   0x0804856e      e845feffff     call sym.imp.printf         ; int printf(const char *format)
|    ||:|   0x08048573      c70424000000.  mov dword [esp], 0          ; int status
|    ||:|   0x0804857a      e869feffff     call sym.imp.exit           ; void exit(int status)
|    ||:|   ; CODE XREF from sym.parell (0x8048565)
|    `----> 0x0804857f      8d45f8         lea eax, dword [local_8h_2]
|     |:|   0x08048582      ff00           inc dword [eax]
|     |`==< 0x08048584      ebd1           jmp 0x8048557
|     | |   ; CODE XREFS from sym.parell (0x804854e, 0x804855b)
|     `-`-> 0x08048586      c9             leave
\           0x08048587      c3             ret

parell函式在檢查使用者輸入是否為偶像之前,呼叫了dummy函式,從高亮部分看,棧中壓入了兩個引數,一個是轉換為整型數字的使用者輸入,一個是環境變數。注意檢查使用者輸入是否為偶數的程式碼外面套了一個迴圈,我沒發現這個迴圈有什麼用處。下面看dummy函式幹了什麼

[0x08048400]> pdf @sym.dummy
/ (fcn) sym.dummy 102
|   sym.dummy (char **s1);
|           ; var int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg char **s1 @ ebp+0xc
|           ; var char *s2 @ esp+0x4
|           ; var size_t *n @ esp+0x8
|           ; CALL XREF from sym.parell (0x8048547)
|           0x080484b4      55             push ebp
|           0x080484b5      89e5           mov ebp, esp
|           0x080484b7      83ec18         sub esp, 0x18
|           0x080484ba      c745fc000000.  mov dword [local_4h], 0
|       .-> 0x080484c1      8b45fc         mov eax, dword [local_4h]
|       :   0x080484c4      8d1485000000.  lea edx, dword [eax*4]
|       :   0x080484cb      8b450c         mov eax, dword [s1]         ; [0xc:4]=-1 ; 12
|       :   0x080484ce      833c0200       cmp dword [edx + eax], 0    ; 檢查是否到字串結尾
|      ,==< 0x080484d2      743a           je 0x804850e
|      |:   0x080484d4      8b45fc         mov eax, dword [local_4h]
|      |:   0x080484d7      8d0c85000000.  lea ecx, dword [eax*4]
|      |:   0x080484de      8b550c         mov edx, dword [s1]         ; [0xc:4]=-1 ; 12
|      |:   0x080484e1      8d45fc         lea eax, dword [local_4h]
|      |:   0x080484e4      ff00           inc dword [eax]
|      |:   0x080484e6      c74424080300.  mov dword [n], 3            ; size_t n
|      |:   0x080484ee      c74424043887.  mov dword [s2], str.LOLO    ; [0x8048738:4]=0x4f4c4f4c ; "LOLO" ; const char *s2
|      |:   0x080484f6      8b0411         mov eax, dword [ecx + edx]
|      |:   0x080484f9      890424         mov dword [esp], eax        ; const char *s1
|      |:   0x080484fc      e8d7feffff     call sym.imp.strncmp        ; int strncmp(const char *s1, const char *s2, size_t n)
|      |:   0x08048501      85c0           test eax, eax               ;