Hello,
This is another IOLI crackme challenge solution.
root@kali:~/IOLI-crackme/bin-linux# r2 crackme0x04
[0x080483d0]> aaa
[ ] 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.* unctions (aan)
[x] Type matching analysis for all functions (afta)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x080483d0]> afl
0x0804833c 1 23 sym._init
0x08048364 1 6 sym.imp.__libc_start_main
0x08048374 1 6 sym.imp.scanf
0x08048384 1 6 sym.imp.strlen
0x08048394 1 6 sym.imp.printf
0x080483a4 1 6 sym.imp.sscanf
0x080483b4 1 6 sym.imp.exit
0x080483d0 1 33 entry0
0x080483f4 3 33 fcn.080483f4
0x08048420 6 47 sym.__do_global_dtors_au
0x08048450 4 50 sym.frame_dummy
0x08048484 6 133 sym.check
0x08048509 1 92 sym.main
0x08048570 4 99 sym.__libc_csu_init
0x080485e0 1 5 sym.__libc_csu_fini
0x080485e5 1 4 sym.__i686.get_pc_thunk.bx
0x080485f0 4 35 sym.__do_global_ctors_aux
0x08048614 1 26 sym._fini
[0x080483d0]> pdf @main
-- 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 ; push word, doubleword or quadword onto the stack
│ 0x0804850a 89e5 mov ebp, esp ; moves data from src to dst
│ 0x0804850c 81ec88000000 sub esp, 0x88 ; substract src and dst, stores result on dst
│ 0x08048512 83e4f0 and esp, 0xfffffff0 ; binary and operation between src and dst, stores result on dst
│ 0x08048515 b800000000 mov eax, 0 ; moves data from src to dst
│ 0x0804851a 83c00f add eax, 0xf ; adds src and dst, stores result on dst
│ 0x0804851d 83c00f add eax, 0xf ; adds src and dst, stores result on dst
│ 0x08048520 c1e804 shr eax, 4 ; logic right shift (0 padding)
│ 0x08048523 c1e004 shl eax, 4 ; logic left shift (0 padding)
│ 0x08048526 29c4 sub esp, eax ; substract src and dst, stores result on dst
│ 0x08048528 c704245e8604. mov dword [esp], str.IOLI_Crackme_Level_0x04 ; [0x804865e:4]=0x494c4f49 ; "IOLI Crackme Level 0x04\n" ; moves data from src to dst; const char *format
│ 0x0804852f e860feffff call sym.imp.printf ; calls a subroutine, push eip into the stack (esp) ; int printf(const char *format)
│ 0x08048534 c70424778604. mov dword [esp], str.Password: ; [0x8048677:4]=0x73736150 ; "Password: " ; moves data from src to dst; const char *format
│ 0x0804853b e854feffff call sym.imp.printf ; calls a subroutine, push eip into the stack (esp) ; int printf(const char *format)
│ 0x08048540 8d4588 lea eax, dword [local_78h] ; load effective address
│ 0x08048543 89442404 mov dword [local_4h], eax ; moves data from src to dst
│ 0x08048547 c70424828604. mov dword [esp], 0x8048682 ; [0x8048682:4]=0x7325 ; moves data from src to dst; const char *format
│ 0x0804854e e821feffff call sym.imp.scanf ; calls a subroutine, push eip into the stack (esp) ; int scanf(const char *format)
│ 0x08048553 8d4588 lea eax, dword [local_78h] ; load effective address
│ 0x08048556 890424 mov dword [esp], eax ; moves data from src to dst
│ 0x08048559 e826ffffff call sym.check ; calls a subroutine, push eip into the stack (esp)
│ 0x0804855e b800000000 mov eax, 0 ; moves data from src to dst
│ 0x08048563 c9 leave ; one byte alias for mov esp, ebp ; pop ebp
└ 0x08048564 c3 ret ; return from subroutine. pop 4 bytes from esp and jump there.
[0x080483d0]> 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 ; push word, doubleword or quadword onto the stack
│ 0x08048485 89e5 mov ebp, esp ; moves data from src to dst
│ 0x08048487 83ec28 sub esp, 0x28 ; '(' ; substract src and dst, stores result on dst
│ 0x0804848a c745f8000000. mov dword [local_8h], 0 ; moves data from src to dst
│ 0x08048491 c745f4000000. mov dword [local_ch], 0 ; moves data from src to dst
│ ; CODE XREF from sym.check (0x80484f9)
│ ┌─> 0x08048498 8b4508 mov eax, dword [s] ; [0x8:4]=-1 ; 8 ; moves data from src to dst
│ ╎ 0x0804849b 890424 mov dword [esp], eax ; moves data from src to dst; const char *s
│ ╎ 0x0804849e e8e1feffff call sym.imp.strlen ; calls a subroutine, push eip into the stack (esp) ; size_t strlen(const char *s)
│ ╎ 0x080484a3 3945f4 cmp dword [local_ch], eax ; compare two operands
│ ┌──< 0x080484a6 7353 jae 0x80484fb ; jump short if above or equal (cf=0)
│ │╎ 0x080484a8 8b45f4 mov eax, dword [local_ch] ; moves data from src to dst
│ │╎ 0x080484ab 034508 add eax, dword [s] ; adds src and dst, stores result on dst
│ │╎ 0x080484ae 0fb600 movzx eax, byte [eax] ; move dst register size padding with zeroes
│ │╎ 0x080484b1 8845f3 mov byte [local_dh], al ; moves data from src to dst
│ │╎ 0x080484b4 8d45fc lea eax, dword [local_4h] ; load effective address
| │╎ 0x080484b7 89442408 mov dword [local_8h_2], eax ; moves data from src to dst; ...
│ │╎ 0x080484bb c74424043886. mov dword [format], 0x8048638 ; [0x8048638:4]=0x50006425 ; moves data from src to dst; const char *format
│ │╎ 0x080484c3 8d45f3 lea eax, dword [local_dh] ; load effective address
│ │╎ 0x080484c6 890424 mov dword [esp], eax ; moves data from src to dst; const char *s
│ │╎ 0x080484c9 e8d6feffff call sym.imp.sscanf ; calls a subroutine, push eip into the stack (esp) ; int sscanf(const char *s, const char *format, ...)
│ │╎ 0x080484ce 8b55fc mov edx, dword [local_4h] ; moves data from src to dst
│ │╎ 0x080484d1 8d45f8 lea eax, dword [local_8h] ; load effective address
│ │╎ 0x080484d4 0110 add dword [eax], edx ; adds src and dst, stores result on dst
│ │╎ 0x080484d6 837df80f cmp dword [local_8h], 0xf ; compare two operands
│ ┌───< 0x080484da 7518 jne 0x80484f4 ; jump short if not equal/not zero (zf=0)
│ ││╎ 0x080484dc c704243b8604. mov dword [esp], str.Password_OK ; [0x804863b:4]=0x73736150 ; "Password OK!\n" ; moves data from src to dst; const char *format
│ ││╎ 0x080484e3 e8acfeffff call sym.imp.printf ; calls a subroutine, push eip into the stack (esp) ; int printf(const char *format)
│ ││╎ 0x080484e8 c70424000000. mov dword [esp], 0 ; moves data from src to dst; int status
│ ││╎ 0x080484ef e8c0feffff call sym.imp.exit ; calls a subroutine, push eip into the stack (esp) ; void exit(int status)
│ ││╎ ; CODE XREF from sym.check (0x80484da)
│ └───> 0x080484f4 8d45f4 lea eax, dword [local_ch] ; load effective address
│ │╎ 0x080484f7 ff00 inc dword [eax] ; increment by 1
│ │└─< 0x080484f9 eb9d jmp 0x8048498 ; jump
│ │ ; CODE XREF from sym.check (0x80484a6)
│ └──> 0x080484fb c70424498604. mov dword [esp], str.Password_Incorrect ; [0x8048649:4]=0x73736150 ; "Password Incorrect!\n" ; moves data from src to dst; const char *format
│ 0x08048502 e88dfeffff call sym.imp.printf ; calls a subroutine, push eip into the stack (esp) ; int printf(const char *format)
│ 0x08048507 c9 leave ; one byte alias for mov esp, ebp ; pop ebp
└ 0x08048508 c3 ret ; return from subroutine. pop 4 bytes from esp and jump there.
Pay attention to these instructions:
│ │╎ 0x080484d6 837df80f cmp dword [local_8h], 0xf ; compare two operands
│ ┌───< 0x080484da 7518 jne 0x80484f4 ; jump short if not equal/not zero (zf=0)
│ ││╎ 0x080484dc c704243b8604. mov dword [esp], str.Password_OK ; [0x804863b:4]=0x73736150 ; "Password OK!\n" ; moves data from src to dst; const char *format
│ ││╎ 0x080484e3 e8acfeffff call sym.imp.printf ; calls a subroutine, push eip into the stack (esp) ; int printf(const char *format)
│ ││╎ 0x080484e8 c70424000000. mov dword [esp], 0 ; moves data from src to dst; int status
│ ││╎ 0x080484ef e8c0feffff call sym.imp.exit ; calls a subroutine, push eip into the stack (esp) ; void exit(int status)
These section is where a number is compared to something and if true the password will be valid.
Something is compared against 15 (0xf).
Let’s find out which is the other operand.
│ └──> 0x080484fb c70424498604. mov dword [esp], str.Password_Incorrect ; [0x8048649:4]=0x73736150 ; "Password Incorrect!\n" ; moves data from src to dst; const char *format
│ 0x08048502 e88dfeffff call sym.imp.printf ; calls a subroutine, push eip into the stack (esp) ; int printf(const char *format)
│ 0x08048507 c9 leave ; one byte alias for mov esp, ebp ; pop ebp
└ 0x08048508 c3 ret ; return from subroutine. pop 4 bytes from esp and jump there.
These section reminds me a loop.
What is being compared to 15 is the parameter of the app, in other words the password.
The functin strlen() gets the length of the password we input.
The function sscanf() gets characters from our password “%s”, and then transform it to “%d”.
Using the loop for len times (our password length) these numbers are added together and compared with 0xf(15).
So, any number combinations that number by number added is 15 will be valid.
root@kali:~/IOLI-crackme/bin-linux# ./crackme0x04
IOLI Crackme Level 0x04
Password: 663
Password OK!