Crackme0x00 - writeup

我现在开始看radare2book了,现在刚看1/3,有些无聊,因为之前也看过一些radare2的实例讲解,所以现在先试着做一下里面的crackme练习。

先执行一下craceme0x00这个文件,看来是要把密码找出来。

root@kali:~/IOLIcrackme/bin-linux# ./crackme0x00
IOLI Crackme Level 0x00
Password: 1234
Invalid Password!

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

root@kali:~/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.

root@kali:~/IOLIcrackme/bin-linux# ./crackme0x00
IOLI Crackme Level 0x00
Password: 250382
Password OK :)

成功。

看来真的只是一个简单的热身。


Crackme0x01 - writeup

还是先执行一下(其实我也不太确定是不是每次都要先执行,因为如果是真的maleware可能执行之后就直接game over了,不过这里只是简单的练习,所以就不考虑这个问题了。)

root@kali:~/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
root@kali:~/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,再执行一下程序测试一下

root@kali:~/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得知的那两个像是乱码一样的字符串做了一些变换,生成最后的提示信息。

root@kali:~/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之后并不会处理之后的字符,而是直接通过,让我们来试一下

root@kali:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 1234
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 555
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 555555555555555
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 5541
Password OK!
root@kali:~/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)

测试一下:

root@kali:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 4156
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 41561
Password Incorrect!  奇数
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 41562
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 2147483646
Password Incorrect!  前N位和不是15
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x05
IOLI Crackme Level 0x05
Password: 2147283646
Password OK!
root@kali:~/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)的栈结构:

所以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               ; 检查前三个字符是否为LOL
|      |`=< 0x08048503      75bc           jne 0x80484c1
|      |    0x08048505      c745f8010000.  mov dword [local_8h], 1
|      |,=< 0x0804850c      eb07           jmp 0x8048515
|      ||   ; CODE XREF from sym.dummy (0x80484d2)
|      `--> 0x0804850e      c745f8000000.  mov dword [local_8h], 0
|       |   ; CODE XREF from sym.dummy (0x804850c)
|       `-> 0x08048515      8b45f8         mov eax, dword [local_8h]
|           0x08048518      c9             leave
\           0x08048519      c3             ret

注意高亮部分,dummy函数使用了第二个参数,即环境变量,代码中检查了每个环境变量,看是否存在某个环境变量的开始三个字符为“LOL”。

所以想要通过该程序测试,

  1. 密码满足crackme0x05的要求
  2. 存在一个环境变量,前三个字符为"LOL"

测试一下:

root@kali:~/IOLIcrackme/bin-linux# export LOLTTT=test
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x06
IOLI Crackme Level 0x06
Password: 4156
Password OK!

Crackme0x07 – writeup

这个程序我是在visual mode下查看的,因为的函数调用比较多,也有一些修改。

查看main函数的代码,发现有一个sub.strlen_5b9函数,查看该函数,和crackme0x06中的check函数结构很像,但是代码更长一些,为了和crackme0x06保持一致,首先将该函数重命名(dr)为check,然后在原本parell函数的位置,有一个函数call 0x8048542

[0x080485e3 160 /root/IOLIcrackme/bin-linux/crackme0x07]> diq;?0;f t.. @ entry0+483 # 0x80485e3                                                 
none at 0x00000000
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xff89b020  0100 0000 68b5 89ff 0000 0000 76b5 89ff  ....h.......v...
0xff89b030  58bb 89ff 6fbb 89ff 80bb 89ff 95bb 89ff  X...o...........
0xff89b040  a0bb 89ff b4bb 89ff c2bb 89ff cdbb 89ff  ................
0xff89b050  f3bb 89ff 04bc 89ff 0ebc 89ff 24bc 89ff  ............$...
 eax 0x00000000      ebx 0x00000000      ecx 0x00000000      edx 0x00000000
 esi 0x00000000      edi 0x00000000      esp 0xff89b020      ebp 0x00000000
 eip 0xf7eed0b0      eflags I           oeax 0x0000000b
|       :   0x080485e3      0fb600         movzx eax, byte [eax]
|       :   0x080485e6      8845f3         mov byte [local_dh], al
|       :   0x080485e9      8d45fc         lea eax, dword [local_4h_2]
|       :   0x080485ec      89442408       mov dword [local_8h_2], eax
|       :   0x080485f0      c7442404c287.  mov dword [local_4h], 0x80487c2       ; [0x80487c2:4]=0x50006425                                     
|       :   0x080485f8      8d45f3         lea eax, dword [local_dh]
|       :   0x080485fb      890424         mov dword [esp], eax
|       :   0x080485fe      e8c5fdffff     call sym.imp.sscanf         ;[1]   ; int sscanf(const char *s, const char *format,   ...)            
|       :   0x08048603      8b55fc         mov edx, dword [local_4h_2]
|       :   0x08048606      8d45f8         lea eax, dword [local_8h]
|       :   0x08048609      0110           add dword [eax], edx
|       :   0x0804860b      837df810       cmp dword [local_8h], 0x10
|      ,==< 0x0804860f      7512           jne 0x8048623               ;[2]                                                                     
|      |:   0x08048611      8b450c         mov eax, dword [arg_ch]       ; [0xc:4]=-1 ; 12                                                      
|      |:   0x08048614      89442404       mov dword [local_4h], eax
|      |:   0x08048618      8b4508         mov eax, dword [arg_8h]       ; [0x8:4]=-1 ; 8                                                       
|      |:   0x0804861b      890424         mov dword [esp], eax
|      |:   0x0804861e      e81fffffff     call 0x8048542              ;[3]                                                                     
|      `--> 0x08048623      8d45f4         lea eax, dword [local_ch]
|       :   0x08048626      ff00           inc dword [eax]
|       `=< 0x08048628      eba3           jmp 0x80485cd               ;[4]                                                                     
|           0x0804862a      e8f5feffff     call 0x8048524              ;[5]    

点击数字键3,进入该函数

[0x08048543 160 /root/IOLIcrackme/bin-linux/crackme0x07]> diq;?0;f t.. @ entry0+323 # 0x8048543                                                 
none at 0x00000000
- offset -   0 1  2 3  4 5  6 7  8 9  A B  0123456789AB
0xff89b020  0100 0000 68b5 89ff 0000 0000  ....h.......
0xff89b02c  76b5 89ff 58bb 89ff 6fbb 89ff  v...X...o...
0xff89b038  80bb 89ff 95bb 89ff a0bb 89ff  ............
0xff89b044  b4bb 89ff c2bb 89ff cdbb 89ff  ............
0xff89b050  f3bb 89ff 04bc 89ff 0ebc 89ff  ............
0xff89b05c  24bc 89ff                      $...
 eax 0x00000000      ebx 0x00000000      ecx 0x00000000
 edx 0x00000000      esi 0x00000000      edi 0x00000000
 esp 0xff89b020      ebp 0x00000000      eip 0xf7eed0b0
 eflags I           oeax 0x0000000b
|           0x08048543      mov ebp, esp
|           0x08048545      sub esp, 0x18
|           0x08048548      lea eax, dword [local_4h]
|           0x0804854b      mov dword [local_8h], eax
|           0x0804854f      mov dword [local_4h_2], 0x80487c2          ; [0x80487c2:4]=0x50006425                                               
|           0x08048557      mov eax, dword [arg_8h]                    ; [0x8:4]=-1 ; 8                                                         
|           0x0804855a      mov dword [esp], eax
|           0x0804855d      call sym.imp.sscanf                        ;[1]   ; int sscanf(const char *s, const char *format,   ...)            
|           0x08048562      mov eax, dword [arg_ch]                    ; [0xc:4]=-1 ; 12                                                        
|           0x08048565      mov dword [local_4h_2], eax
|           0x08048569      mov eax, dword [local_4h]
|           0x0804856c      mov dword [esp], eax
|           0x0804856f      call fcn.080484b4                          ;[2]                                                                     
|           0x08048574      test eax, eax
|       ,=< 0x08048576      je 0x80485b7                               ;[3]                                                                     
|       |   0x08048578      mov dword [local_8h_2], 0
|       |   ; CODE XREF from parell (0x80485b5)                                                                                                 
|      .--> 0x0804857f      cmp dword [local_8h_2], 9
|     ,===< 0x08048583      jg 0x80485b7                               ;[3]

发现该函数和parell函数的结构类似,fcn.080484b4应该是函数dummy,所以把0x8048542定义为一个函数,并重命名为parell,fcn.080484b4重命名为dummy,进入dummy函数,可以验证其确实是crackme0x06中的dummy函数。

现在重新回到parell函数,函数验证正确的消息就是从这里输出的,看一下调用dummy函数后,函数进行了哪些运算

[0x0804867d]> pdf @parell
/ (fcn) parell 119
|   parell (int arg_8h, int arg_ch);
|           ; var int local_8h_2 @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg int arg_8h @ ebp+0x8
|           ; arg int arg_ch @ ebp+0xc
|           ; var int local_4h_2 @ esp+0x4
|           ; var int local_8h @ esp+0x8
|           ; CALL XREF from check (0x804861e)
|           0x08048542 b    push ebp
|           0x08048543      mov ebp, esp
|           0x08048545      sub esp, 0x18
|           0x08048548      lea eax, dword [local_4h]
|           0x0804854b      mov dword [local_8h], eax
|           0x0804854f      mov dword [local_4h_2], 0x80487c2          ; [0x80487c2:4]=0x50006425
|           0x08048557      mov eax, dword [arg_8h]                    ; [0x8:4]=-1 ; 8
|           0x0804855a      mov dword [esp], eax
|           0x0804855d      call sym.imp.sscanf                        ; int sscanf(const char *s, const char *format,   ...)
|           0x08048562      mov eax, dword [arg_ch]                    ; [0xc:4]=-1 ; 12
|           0x08048565      mov dword [local_4h_2], eax
|           0x08048569      mov eax, dword [local_4h]
|           0x0804856c      mov dword [esp], eax
|           0x0804856f      call dummy
|           0x08048574      test eax, eax                  ; 检查dummy返回值,为1时继续判断
|       ,=< 0x08048576      je 0x80485b7
|       |   0x08048578      mov dword [local_8h_2], 0
|       |   ; CODE XREF from parell (0x80485b5)
|      .--> 0x0804857f      cmp dword [local_8h_2], 9
|     ,===< 0x08048583      jg 0x80485b7
|     |:|   0x08048585      mov eax, dword [local_4h]
|     |:|   0x08048588      and eax, 1
|     |:|   0x0804858b      test eax, eax
|    ,====< 0x0804858d      jne 0x80485b0
|    ||:|   0x0804858f      cmp dword [0x804a02c], 1                   ; [0x804a02c:4]=0
|   ,=====< 0x08048596      jne 0x80485a4
|   |||:|   0x08048598      mov dword [esp], str.Password_OK           ; [0x80487c5:4]=0x73736150 ; "Password OK!\n"
|   |||:|   0x0804859f      call sym.imp.printf                        ; int printf(const char *format)
|   `-----> 0x080485a4      mov dword [esp], 0
|    ||:|   0x080485ab      call sym.imp.exit                          ; void exit(int status)
|    `----> 0x080485b0      lea eax, dword [local_8h_2]
|     |:|   0x080485b3      inc dword [eax]
|     |`==< 0x080485b5      jmp 0x804857f
|     `-`-> 0x080485b7      leave
\           0x080485b8      ret

注意到这里多了一个判断(高亮部分),看一下哪里引用了0x804a02c这个地址

[0x0804867d]> axt 0x804a02c
dummy 0x8048505 [DATA] mov dword [0x804a02c], 1
parell 0x804858f [DATA] cmp dword [0x804a02c], 1

在dummy函数中

[0x0804867d]> pdf @dummy
/ (fcn) dummy 112
|   dummy (int arg_ch);
|           ; var int local_8h @ ebp-0x8
|           ; var int local_4h @ ebp-0x4
|           ; arg int arg_ch @ ebp+0xc
|           ; var int local_4h_2 @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from parell (0x804856f)
|           ; CALL XREF from check (0x804863c)
|           0x080484b4 b    push ebp
|           0x080484b5      mov ebp, esp
|           0x080484b7      sub esp, 0x18
|           0x080484ba      mov dword [local_4h], 0
|       .-> 0x080484c1      mov eax, dword [local_4h]
|       :   0x080484c4      lea edx, dword [eax*4]
|       :   0x080484cb      mov eax, dword [arg_ch]                    ; [0xc:4]=-1 ; 12
|       :   0x080484ce      cmp dword [edx + eax], 0
|      ,==< 0x080484d2      je 0x8048518
|      |:   0x080484d4      mov eax, dword [local_4h]
|      |:   0x080484d7      lea ecx, dword [eax*4]
|      |:   0x080484de      mov edx, dword [arg_ch]                    ; [0xc:4]=-1 ; 12
|      |:   0x080484e1      lea eax, dword [local_4h]
|      |:   0x080484e4      inc dword [eax]
|      |:   0x080484e6      mov dword [local_8h_2], 3
|      |:   0x080484ee      mov dword [local_4h_2], str.LOLO           ; [0x80487a8:4]=0x4f4c4f4c ; "LOLO"
|      |:   0x080484f6      mov eax, dword [ecx + edx]
|      |:   0x080484f9      mov dword [esp], eax
|      |:   0x080484fc      call sym.imp.strncmp                       ; int strncmp(const char *s1, const char *s2, size_t n)
|      |:   0x08048501      test eax, eax
|      |`=< 0x08048503      jne 0x80484c1
|      |    0x08048505      mov dword [0x804a02c], 1                   ; [0x804a02c:4]=0
|      |    0x0804850f      mov dword [local_8h], 1
|      |,=< 0x08048516      jmp 0x804851f
|      `--> 0x08048518      mov dword [local_8h], 0
|       |   ; CODE XREF from dummy (0x8048516)
|       `-> 0x0804851f      mov eax, dword [local_8h]
|           0x08048522      leave
\           0x08048523      ret

注意高亮部分,可以得知,只要dummy函数中strncmp函数返回0,0x804a02c就会被赋值为1,所以prarell函数中的这个比较在dummy函数通过后是必然成立的。

在main -> check -> parell -> dummy这个函数调用流程中,我们已经检查了parell和dummy函数和crackme0x06中的区别,发现对最终密码的检查并没有区别,下面看check函数:

[0x0804867d]> pdf @check
/ (fcn) check 196
|   check (int arg_8h, int arg_ch);
|           ; var int local_dh @ ebp-0xd
|           ; var int local_ch @ ebp-0xc
|           ; var int local_8h @ ebp-0x8
|           ; var int local_4h_2 @ ebp-0x4
|           ; arg int arg_8h @ ebp+0x8
|           ; arg int arg_ch @ ebp+0xc
|           ; var int local_4h @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from main (0x80486d4)
|           0x080485b9      push ebp
|           0x080485ba      mov ebp, esp
|           0x080485bc      sub esp, 0x28                              ; '('
|           0x080485bf      mov dword [local_8h], 0
|           0x080485c6      mov dword [local_ch], 0
|           ; CODE XREF from check (0x8048628)
|       .-> 0x080485cd      mov eax, dword [arg_8h]                    ; [0x8:4]=-1 ; 8
|       :   0x080485d0      mov dword [esp], eax
|       :   0x080485d3      call sym.imp.strlen                        ; size_t strlen(const char *s)
|       :   0x080485d8      cmp dword [local_ch], eax
|      ,==< 0x080485db      jae 0x804862a
|      |:   0x080485dd      mov eax, dword [local_ch]
|      |:   0x080485e0      add eax, dword [arg_8h]
|      |:   0x080485e3      movzx eax, byte [eax]
|      |:   0x080485e6      mov byte [local_dh], al
|      |:   0x080485e9      lea eax, dword [local_4h_2]
|      |:   0x080485ec      mov dword [local_8h_2], eax
|      |:   0x080485f0      mov dword [local_4h], 0x80487c2            ; [0x80487c2:4]=0x50006425
|      |:   0x080485f8      lea eax, dword [local_dh]
|      |:   0x080485fb      mov dword [esp], eax
|      |:   0x080485fe      call sym.imp.sscanf                        ; int sscanf(const char *s, const char *format,   ...)
|      |:   0x08048603      mov edx, dword [local_4h_2]
|      |:   0x08048606      lea eax, dword [local_8h]
|      |:   0x08048609      add dword [eax], edx
|      |:   0x0804860b      cmp dword [local_8h], 0x10
|     ,===< 0x0804860f      jne 0x8048623
|     ||:   0x08048611      mov eax, dword [arg_ch]                    ; [0xc:4]=-1 ; 12
|     ||:   0x08048614      mov dword [local_4h], eax
|     ||:   0x08048618      mov eax, dword [arg_8h]                    ; [0x8:4]=-1 ; 8
|     ||:   0x0804861b      mov dword [esp], eax
|     ||:   0x0804861e      call parell
|     `---> 0x08048623      lea eax, dword [local_ch]
|      |:   0x08048626      inc dword [eax]
|      |`=< 0x08048628      jmp 0x80485cd
|      `--> 0x0804862a      call 0x8048524
|           0x0804862f      mov eax, dword [arg_ch]                    ; [0xc:4]=-1 ; 12
|           0x08048632      mov dword [local_4h], eax
|           0x08048636      mov eax, dword [local_4h_2]
|           0x08048639      mov dword [esp], eax
|           0x0804863c      call dummy
|           0x08048641      test eax, eax
|       ,=< 0x08048643      je 0x804867b
|       |   0x08048645      mov dword [local_ch], 0
|       |   ; CODE XREF from check (0x8048679)
|      .--> 0x0804864c      cmp dword [local_ch], 9
|     ,===< 0x08048650      jg 0x804867b
|     |:|   0x08048652      mov eax, dword [local_4h_2]
|     |:|   0x08048655      and eax, 1
|     |:|   0x08048658      test eax, eax
|    ,====< 0x0804865a      jne 0x8048674
|    ||:|   0x0804865c      mov dword [esp], str.wtf                   ; [0x80487d3:4]=0x3f667477 ; "wtf?\n"
|    ||:|   0x08048663      call sym.imp.printf                        ; int printf(const char *format)
|    ||:|   0x08048668      mov dword [esp], 0
|    ||:|   0x0804866f      call sym.imp.exit                          ; void exit(int status)
|    `----> 0x08048674      lea eax, dword [local_ch]
|     |:|   0x08048677      inc dword [eax]
|     |`==< 0x08048679      jmp 0x804864c
|     `-`-> 0x0804867b      leave
\           0x0804867c      ret

check函数中,调用了parell函数之后,又多了一大段代码,调用parell函数下面的call 0x8048524是字符数字累加循环的判断条件为false时的跳转目标。

0x08048524      push ebp
0x08048525      mov ebp, esp
0x08048527      sub esp, 8
0x0804852a      mov dword [esp], str.Password_Incorrect       ; [0x80487ad:4]=0x73736150 ; "Password Incorrect!\n"                  
0x08048531      call sym.imp.printf                        ;[4]   ; int printf(const char *format)                                  
0x08048536      mov dword [esp], 0
0x0804853d      call sym.imp.exit                          ;[5]   ; void exit(int status)     

可以看到该函数会直接退出程序。而除此之外,check函数中没有其他代码可以跳转到0x804862f下面的代码,即下方代码并不会执行。

综上,该程序和crackme0x06的密码验证条件应该是相同的。

测试一下:

root@kali:~/IOLIcrackme/bin-linux# export LOLTTT=test
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x07
IOLI Crackme Level 0x07
Password: 4156
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x07
IOLI Crackme Level 0x07
Password: 41561
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x07
IOLI Crackme Level 0x07
Password: 41562
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x07
IOLI Crackme Level 0x07
Password: 2147483646
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x07
IOLI Crackme Level 0x07
Password: 2147283646
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x07
IOLI Crackme Level 0x07
Password: 2237283646
Password Incorrect!

Crackme0x08 – writeup

进入radare2,先在visual mode下走一遍这个程序的流程:

Main -> check -> parell -> dummy

发现主要的流程仍然不变,粗看下来计算过程也差不多,用radiff2比较一下两个程序

              fcn.08048360  23 0x8048360 |   MATCH  (1.000000) | 0x8048360   23 sym._init
 sym.imp.__libc_start_main   6 0x8048388 |   MATCH  (1.000000) | 0x8048388    6 sym.imp.__libc_start_main
             sym.imp.scanf   6 0x8048398 |   MATCH  (1.000000) | 0x8048398    6 sym.imp.scanf
            sym.imp.strlen   6 0x80483a8 |   MATCH  (1.000000) | 0x80483a8    6 sym.imp.strlen
            sym.imp.printf   6 0x80483b8 |   MATCH  (1.000000) | 0x80483b8    6 sym.imp.printf
            sym.imp.sscanf   6 0x80483c8 |   MATCH  (1.000000) | 0x80483c8    6 sym.imp.sscanf
           sym.imp.strncmp   6 0x80483d8 |   MATCH  (1.000000) | 0x80483d8    6 sym.imp.strncmp
              sym.imp.exit   6 0x80483e8 |   MATCH  (1.000000) | 0x80483e8    6 sym.imp.exit
                    entry0  33 0x8048400 |   MATCH  (1.000000) | 0x8048400   33 entry0
              fcn.08048424  33 0x8048424 |   MATCH  (1.000000) | 0x8048424   33 fcn.08048424
              fcn.08048450  47 0x8048450 |   MATCH  (1.000000) | 0x8048450   47 sym.__do_global_dtors_aux
              fcn.08048480  50 0x8048480 |   MATCH  (1.000000) | 0x8048480   50 sym.frame_dummy
              sub.LOLO_4b4 112 0x80484b4 |   MATCH  (1.000000) | 0x80484b4  112 sym.dummy
sub.Password_Incorrect_524  30 0x8048524 |   MATCH  (1.000000) | 0x8048524   30 sym.che
            sub.sscanf_542 119 0x8048542 |   MATCH  (1.000000) | 0x8048542  119 sym.parell
            sub.strlen_5b9 196 0x80485b9 |   MATCH  (1.000000) | 0x80485b9  196 sym.check
                      main  99 0x804867d |   MATCH  (1.000000) | 0x804867d   99 sym.main
              fcn.08048755   4 0x8048755 |   MATCH  (1.000000) | 0x8048755    4 sym.__i686.get_pc_thunk.bx
              fcn.08048760  35 0x8048760 |   MATCH  (1.000000) | 0x8048760   35 sym.__do_global_ctors_aux
              fcn.0804878d  17 0x804878d |     NEW  (0.000000)
       sym.__libc_csu_init  99 0x80486e0 |     NEW  (0.000000)
       sym.__libc_csu_fini   5 0x8048750 |     NEW  (0.000000)
                 sym._fini  26 0x8048784 |     NEW  (0.000000)

发现流程中的主要函数都相同,我们可以猜测此程序和crackme0x07程序对于密码的检测要求是先相同的,测试一下:

root@kali:~/IOLIcrackme/bin-linux# export LOLTTT=test
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x08
IOLI Crackme Level 0x08
Password: 4156
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x08
IOLI Crackme Level 0x08
Password: 41561
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x08
IOLI Crackme Level 0x08
Password: 41562
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x08
IOLI Crackme Level 0x08
Password: 2147483646
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x08
IOLI Crackme Level 0x08
Password: 2147283646
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x08
IOLI Crackme Level 0x08
Password: 2237283646
Password Incorrect!

Crackme0x09 – writeup

这个程序比前面几个看起来复杂一些,如果之前没有看到过前9个程序,直接看这个程序,可能要花费更大的力气才能看懂,但是由于之前已经看了9个类似的程序了,只靠猜测也能猜出crackme0x09这个程序中每个函数的意思。

看一下main函数

[0xf7fb50b0]> pdf @main
/ (fcn) main 120
|   main (int argc, char **argv, char **envp);
|           ; var int local_78h @ ebp-0x78
|           ; var int local_4h_2 @ ebp-0x4
|           ; arg int arg_10h @ ebp+0x10
|           ; var int local_4h @ esp+0x4
|           ; DATA XREF from entry0 (0x8048437)
|           0x080486ee      55             push ebp
|           0x080486ef      89e5           mov ebp, esp
|           0x080486f1      53             push ebx
|           0x080486f2      81ec84000000   sub esp, 0x84
|           0x080486f8      e869000000     call fcn.08048766
|           0x080486fd      81c3f7180000   add ebx, 0x18f7
|           0x08048703      83e4f0         and esp, 0xfffffff0
|           0x08048706      b800000000     mov eax, 0
|           0x0804870b      83c00f         add eax, 0xf
|           0x0804870e      83c00f         add eax, 0xf
|           0x08048711      c1e804         shr eax, 4
|           0x08048714      c1e004         shl eax, 4
|           0x08048717      29c4           sub esp, eax
|           0x08048719      8d8375e8ffff   lea eax, dword [ebx - 0x178b]
|           0x0804871f      890424         mov dword [esp], eax
|           0x08048722      e8b9fcffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048727      8d838ee8ffff   lea eax, dword [ebx - 0x1772]
|           0x0804872d      890424         mov dword [esp], eax
|           0x08048730      e8abfcffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048735      8d4588         lea eax, dword [local_78h]
|           0x08048738      89442404       mov dword [local_4h], eax
|           0x0804873c      8d8399e8ffff   lea eax, dword [ebx - 0x1767]
|           0x08048742      890424         mov dword [esp], eax
|           0x08048745      e876fcffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x0804874a      8b4510         mov eax, dword [arg_10h]    ; [0x10:4]=-1 ; 16
|           0x0804874d      89442404       mov dword [local_4h], eax
|           0x08048751      8d4588         lea eax, dword [local_78h]
|           0x08048754      890424         mov dword [esp], eax
|           0x08048757      e8bafeffff     call sub.strlen_616
|           0x0804875c      b800000000     mov eax, 0
|           0x08048761      8b5dfc         mov ebx, dword [local_4h_2]
|           0x08048764      c9             leave
\           0x08048765      c3             ret

程序开头就调用了一个函数fcn.08048766,看一下这个函数干了什么

 

/ (fcn) fcn.08048766 4
|   fcn.08048766 ();
|           ; CALL XREF from sub.strlen_616 (0x804861d)
|           ; CALL XREF from main (0x80486f8)
|           0x08048766      8b1c24         mov ebx, dword [esp]
\           0x08048769      c3             ret

ebx中传入了一个值。

 

再回到main函数,它依次调用了printf -> printf -> scanf -> strlen_616,根据前面九个程序的经验,我们已经可以猜到这几个函数都干了什么,可以验证一下,比如说第一个printf,它输出的应该是“IOLI Crackme Level 0x09”我们看到它的参数是eax寄存器中的值,而不是像前面的程序一样直接传入字符串的地址,所以可以看一下eax寄存器中的值是不是该字符串的地址:

[0xf7fb50b0]> db 0x08048722
[0xf7fb50b0]> dc
hit breakpoint at: 8048722
[0x08048722]> dr
eax = 0x08048869
ebx = 0x08049ff4
ecx = 0xae65ce84
edx = 0xffa19754
esi = 0xf7f87000
edi = 0xf7f87000
esp = 0xffa19690
ebp = 0xffa19728
eip = 0x08048722
eflags = 0x00000286
oeax = 0xffffffff
[0x08048722]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x00000838 0x08048838   4   5 (.rodata) ascii LOLO
001 0x0000083d 0x0804883d  20  21 (.rodata) ascii Password Incorrect!\n
002 0x00000855 0x08048855  13  14 (.rodata) ascii Password OK!\n
003 0x00000863 0x08048863   5   6 (.rodata) ascii wtf?\n
004 0x00000869 0x08048869  24  25 (.rodata) ascii IOLI Crackme Level 0x09\n
005 0x00000882 0x08048882  10  11 (.rodata) ascii Password:

可以看到当程序执行到第一个printf语句时,eax寄存器中的值和“IOLO Crackme Level 0x09”字符串的地址是一样的。所以main函数和之前的程序没有什么不同,只是传参的地址取值方式变了,下面继续,看一下strlen_616函数,该函数和之前的check函数类似,后面仍旧有一小段代码是访问不到的,只看上面会访问到的代码:

 

[0x08048722]> pdf @sub.strlen_616
/ (fcn) sub.strlen_616 216
|   sub.strlen_616 (int arg_8h, int arg_ch);
|           ; var int local_11h @ ebp-0x11
|           ; var int local_10h @ ebp-0x10
|           ; var int local_ch @ ebp-0xc
|           ; var int local_8h @ ebp-0x8
|           ; arg int arg_8h @ ebp+0x8
|           ; arg int arg_ch @ ebp+0xc
|           ; var int local_4h @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from main (0x8048757)
|           0x08048616      55             push ebp
|           0x08048617      89e5           mov ebp, esp
|           0x08048619      53             push ebx
|           0x0804861a      83ec24         sub esp, 0x24               ; '$'
|           0x0804861d      e844010000     call fcn.08048766
|           0x08048622      81c3d2190000   add ebx, 0x19d2
|           0x08048628      c745f4000000.  mov dword [local_ch], 0
|           0x0804862f      c745f0000000.  mov dword [local_10h], 0
|           ; CODE XREF from sub.strlen_616 (0x8048693)
|       .-> 0x08048636      8b4508         mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8
|       :   0x08048639      890424         mov dword [esp], eax
|       :   0x0804863c      e88ffdffff     call sym.imp.strlen         ; size_t strlen(const char *s)
|       :   0x08048641      3945f0         cmp dword [local_10h], eax
|      ,==< 0x08048644      734f           jae 0x8048695
|      |:   0x08048646      8b45f0         mov eax, dword [local_10h]
|      |:   0x08048649      034508         add eax, dword [arg_8h]
|      |:   0x0804864c      0fb600         movzx eax, byte [eax]
|      |:   0x0804864f      8845ef         mov byte [local_11h], al
|      |:   0x08048652      8d45f8         lea eax, dword [local_8h]
|      |:   0x08048655      89442408       mov dword [local_8h_2], eax
|      |:   0x08048659      8d835ee8ffff   lea eax, dword [ebx - 0x17a2]
|      |:   0x0804865f      89442404       mov dword [local_4h], eax
|      |:   0x08048663      8d45ef         lea eax, dword [local_11h]
|      |:   0x08048666      890424         mov dword [esp], eax
|      |:   0x08048669      e882fdffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
|      |:   0x0804866e      8b55f8         mov edx, dword [local_8h]
|      |:   0x08048671      8d45f4         lea eax, dword [local_ch]
|      |:   0x08048674      0110           add dword [eax], edx
|      |:   0x08048676      837df410       cmp dword [local_ch], 0x10
|     ,===< 0x0804867a      7512           jne 0x804868e
|     ||:   0x0804867c      8b450c         mov eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12
|     ||:   0x0804867f      89442404       mov dword [local_4h], eax
|     ||:   0x08048683      8b4508         mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8
|     ||:   0x08048686      890424         mov dword [esp], eax
|     ||:   0x08048689      e8fbfeffff     call 0x8048589
|     `---> 0x0804868e      8d45f0         lea eax, dword [local_10h]
|      |:   0x08048691      ff00           inc dword [eax]
|      |`=< 0x08048693      eba1           jmp 0x8048636
|      `--> 0x08048695      e8c3feffff     call 0x804855d
|           0x0804869a      8b450c         mov eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12
|           0x0804869d      89442404       mov dword [local_4h], eax
|           0x080486a1      8b45f8         mov eax, dword [local_8h]
|           0x080486a4      890424         mov dword [esp], eax
|           0x080486a7      e828feffff     call 0x80484d4
|           0x080486ac      85c0           test eax, eax
|       ,=< 0x080486ae      7438           je 0x80486e8
|       |   0x080486b0      c745f0000000.  mov dword [local_10h], 0
|       |   ; CODE XREF from sub.strlen_616 (0x80486e6)
|      .--> 0x080486b7      837df009       cmp dword [local_10h], 9
|     ,===< 0x080486bb      7f2b           jg 0x80486e8
|     |:|   0x080486bd      8b45f8         mov eax, dword [local_8h]
|     |:|   0x080486c0      83e001         and eax, 1
|     |:|   0x080486c3      85c0           test eax, eax
|    ,====< 0x080486c5      751a           jne 0x80486e1
|    ||:|   0x080486c7      8d836fe8ffff   lea eax, dword [ebx - 0x1791]
|    ||:|   0x080486cd      890424         mov dword [esp], eax
|    ||:|   0x080486d0      e80bfdffff     call sym.imp.printf         ; int printf(const char *format)
|    ||:|   0x080486d5      c70424000000.  mov dword [esp], 0
|    ||:|   0x080486dc      e82ffdffff     call sym.imp.exit           ; void exit(int status)
|    `----> 0x080486e1      8d45f0         lea eax, dword [local_10h]
|     |:|   0x080486e4      ff00           inc dword [eax]
|     |`==< 0x080486e6      ebcf           jmp 0x80486b7
|     `-`-> 0x080486e8      83c424         add esp, 0x24               ; '$'
|           0x080486eb      5b             pop ebx
|           0x080486ec      5d             pop ebp
\           0x080486ed      c3             ret

主要涉及两个函数0x80485890x804855d,其中0x8048589应该就是parell函数,0x804855d应该是输出密码错误信息的函数,先看一下0x804855d:

 

            0x0804855d      55             push ebp
            0x0804855e      89e5           mov ebp, esp
            0x08048560      53             push ebx
            0x08048561      83ec04         sub esp, 4
            0x08048564      e8fd010000     call fcn.08048766           ;[1]                                                                     
            0x08048569      81c38b1a0000   add ebx, 0x1a8b
            0x0804856f      8d8349e8ffff   lea eax, dword [ebx - 0x17b7]
            0x08048575      890424         mov dword [esp], eax
            0x08048578      e863feffff     call sym.imp.printf         ;[2]   ; int printf(const char *format)                                  
            0x0804857d      c70424000000.  mov dword [esp], 0
            0x08048584      e887feffff     call sym.imp.exit           ;[3]   ; void exit(int status) 

可以看一下输出的字符串是什么:

[0x0804855d]> db 0x08048578
[0x0804855d]> dc
IOLI Crackme Level 0x09
Password: 1234
hit breakpoint at: 8048578
[0x0804855d]> dr
eax = 0x0804883d
ebx = 0x08049ff4
ecx = 0x00000030
edx = 0x00000004
esi = 0xf7f87000
edi = 0xf7f87000
esp = 0xffa19650
ebp = 0xffa19658
eip = 0x08048578
eflags = 0x00000212
oeax = 0xffffffff
[0x0804855d]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x00000838 0x08048838   4   5 (.rodata) ascii LOLO
001 0x0000083d 0x0804883d  20  21 (.rodata) ascii Password Incorrect!\n
002 0x00000855 0x08048855  13  14 (.rodata) ascii Password OK!\n
003 0x00000863 0x08048863   5   6 (.rodata) ascii wtf?\n
004 0x00000869 0x08048869  24  25 (.rodata) ascii IOLI Crackme Level 0x09\n
005 0x00000882 0x08048882  10  11 (.rodata) ascii Password:

果然,输出的是密码错误的提示信息。

还是回到类似check函数的strlen_616函数,看一下我们认为是parell函数的0x8048589

[0x080486ee]> pdf @fcn.08048589
/ (fcn) fcn.08048589 141
|   fcn.08048589 (int arg_8h, int arg_ch);
|           ; var int local_ch @ ebp-0xc
|           ; var int local_8h @ ebp-0x8
|           ; arg int arg_8h @ ebp+0x8
|           ; arg int arg_ch @ ebp+0xc
|           ; var int local_4h @ esp+0x4
|           ; var int local_8h_2 @ esp+0x8
|           ; CALL XREF from sub.strlen_616 (0x8048689)
|           0x08048589      55             push ebp
|           0x0804858a      89e5           mov ebp, esp
|           0x0804858c      53             push ebx
|           0x0804858d      83ec14         sub esp, 0x14
|           0x08048590      e8d1010000     call fcn.08048766
|           0x08048595      81c35f1a0000   add ebx, 0x1a5f
|           0x0804859b      8d45f8         lea eax, dword [local_8h]
|           0x0804859e      89442408       mov dword [local_8h_2], eax
|           0x080485a2      8d835ee8ffff   lea eax, dword [ebx - 0x17a2]
|           0x080485a8      89442404       mov dword [local_4h], eax
|           0x080485ac      8b4508         mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8
|           0x080485af      890424         mov dword [esp], eax
|           0x080485b2      e839feffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
|           0x080485b7      8b450c         mov eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12
|           0x080485ba      89442404       mov dword [local_4h], eax
|           0x080485be      8b45f8         mov eax, dword [local_8h]
|           0x080485c1 b    890424         mov dword [esp], eax
|           0x080485c4      e80bffffff     call fcn.080484d4
|           0x080485c9      85c0           test eax, eax
|       ,=< 0x080485cb      7443           je 0x8048610
|       |   0x080485cd      c745f4000000.  mov dword [local_ch], 0
|       |   ; CODE XREF from fcn.08048589 (0x804860e)
|      .--> 0x080485d4      837df409       cmp dword [local_ch], 9
|     ,===< 0x080485d8      7f36           jg 0x8048610
|     |:|   0x080485da      8b45f8         mov eax, dword [local_8h]
|     |:|   0x080485dd      83e001         and eax, 1
|     |:|   0x080485e0      85c0           test eax, eax
|    ,====< 0x080485e2      7525           jne 0x8048609
|    ||:|   0x080485e4      8b83fcffffff   mov eax, dword [ebx - 4]
|    ||:|   0x080485ea      833801         cmp dword [eax], 1
|   ,=====< 0x080485ed      750e           jne 0x80485fd
|   |||:|   0x080485ef      8d8361e8ffff   lea eax, dword [ebx - 0x179f]
|   |||:|   0x080485f5      890424         mov dword [esp], eax
|   |||:|   0x080485f8      e8e3fdffff     call sym.imp.printf         ; int printf(const char *format)
|   `-----> 0x080485fd      c70424000000.  mov dword [esp], 0
|    ||:|   0x08048604      e807feffff     call sym.imp.exit           ; void exit(int status)
|    `----> 0x08048609      8d45f4         lea eax, dword [local_ch]
|     |:|   0x0804860c      ff00           inc dword [eax]
|     |`==< 0x0804860e      ebc4           jmp 0x80485d4
|     `-`-> 0x08048610      83c414         add esp, 0x14
|           0x08048613      5b             pop ebx
|           0x08048614      5d             pop ebp
\           0x08048615      c3             ret

发现和parell的操作确实相同,其中的0x80484d4应该就是dummy函数。

 

…..好吧,其实后面步骤都差不多,看一下0x80484d4dummy函数是不是一样,查看输出的字符串内容。

其中根据iz命令显示的字符串,我们已经差不多可以肯定这个程序对密码的检测和前面几个程序同样是相同的,直接测试一下

root@kali:~/IOLIcrackme/bin-linux# export LOLTTT=test
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x09
IOLI Crackme Level 0x09
Password: 4156
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x09
IOLI Crackme Level 0x09
Password: 41561
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x09
IOLI Crackme Level 0x09
Password: 41562
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x09
IOLI Crackme Level 0x09
Password: 2147483646
Password Incorrect!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x09
IOLI Crackme Level 0x09
Password: 2147283646
Password OK!
root@kali:~/IOLIcrackme/bin-linux# ./crackme0x09
IOLI Crackme Level 0x09
Password: 2237283646
Password Incorrect!

总结

  这十个程序程序的功能是一样的,只是对密码的检查计算过程不同。
  一开始只是简单的和正确的密码进行比较,其中正确密码的引用方式又有不同;
  之后是需要密码满足一系列的要求,不断引入不同的要求,并加入最终不起作用或者不用被执行的混淆代码。
  在对程序进行破解的过程中,我主要是采用静态分析,使用了s、pdf、iz、Vpp等命令,有时为了验证,查看相关寄存器的值,也引入了动态分析,在debug模式下,使用db、dc、dr等命令进行操作。
  由于我对radare2还并不是特别熟悉,虽然最终都得到了答案,但是可能过程并不是特别简洁。而且由于这十个程序的功能过于一致,所以只能用来对radare2进行初步的熟悉和了解,进一步的使用还需要接触更多的实例应用。

01-01 00:04