Let's run the crackme:
$ ./crackme0x00
IOLI Crackme Level 0x00
Password: foo
Invalid Password!
As we can see, the goal is to patch the binary file to accept any password.
We're now going to inspect the strings on the binary, normally you would use the unix strings command to do such task, but we can also use radare:
$ rabin -z crackme0x00
0x00000154 A /lib/ld-linux.so.2
0x00000249 A __gmon_start__
0x00000258 A libc.so.6
0x00000262 A printf
0x00000269 A strcmp
0x00000270 A scanf
0x00000276 A _IO_stdin_used
0x00000285 A __libc_start_main
0x00000297 A GLIBC_2.0
0x00000568 A IOLI Crackme Level 0x00
0x00000581 A Password:
0x0000058f A 250382
0x00000596 A Invalid Password!
0x000005a9 A Password OK :)
[...]
With -S[len] radare prints sequences of characters that are at least len characters long, in the example we used 5. As you can see, radare prints the offset within the file before each string, and an A or U indicating whether it is either a Ascii or Unicode string.
At offset 0x0000058f we can see an ascii string which looks suspicious, let's try it as password -- this is level 0 by the way, so it should be that easy ;)
$ ./crackme0x00
IOLI Crackme Level 0x00
Password: 250382
Password OK :)
Yay! we found it, it was really that easy, but now we want to patch the binary to accept any password, so let's disassemble it with radare:
$ radare crackme0x00
open ro crackme0x00
Adding strings & symbol flags for crackme0x00
7 strings added.
15 symbols added.
[0x08048360]>
As you can see, the strings and symbols have been flagged automatically for us, also the seek has been changed to the entry point (0x08048360). The prompt always shows the current seek (position on the file, rebased to virtual memory address).
We can dump the flags, to see what has been flagged:
[0x08048360]> flag
000 0x00000360 512 entrypoint
002 0x000002f8 512 sym._init
003 0x00000320 512 sym.__libc_start_main
004 0x00000330 512 sym.scanf
005 0x00000340 512 sym.printf
006 0x00000350 512 sym.strcmp
007 0x00000360 512 sym._start
008 0x000003b0 512 sym.__do_global_dtors_aux
009 0x000003e0 512 sym.frame_dummy
010 0x00000414 512 sym.main
011 0x000004a0 512 sym.__libc_csu_init
012 0x00000510 512 sym.__libc_csu_fini
013 0x00000515 512 sym.__i686.get_pc_thunk.bx
014 0x00000520 512 sym.__do_global_ctors_aux
015 0x00000544 512 sym._fini
016 0x08048368 512 "PTRh"
017 0x08048568 512 "IOLI.Crackme.Level.0x00"
018 0x08048581 512 "Password:."
019 0x0804858f 512 "250382"
020 0x08048596 512 "Invalid.Password!"
021 0x080485a9 512 "Password.OK.:)"
022 0x0804858c 512 "%
[0x08048360]>
Now we can print a disassembly of the main() function, to do this we will change the seek to the position of sym.main and use the "pD" command to print disassembly:
[0x08048360]> s sym.main
[0x08048414]> pd
TODO: http://radare.nopcode.org/img/wk/crackme0x00_pD_sym.main.png
For example, to print a disassembly of 126 bytes starting at position sym.main, we would do:
If we just type pd, it will print the disassembly from the current seek until the end of the block.
Now let's print a code graph of the main() function, as our current seek is sym.main, we just type "ag":
TODO: Add graph in ascii art here TODO: http://radare.nopcode.org/img/wk/crackme0x00-sym.main.png
Now let's do the real work here...
If we look at the disassembly we can see, it calls printf() and scanf() to ask for the password, then it calls strcmp() to compare the string we've entered with the hardcoded string "250382". Now look at the graph, you can see it branches into two blocks of code, depending on the result of the string comparison.
If the password is correct the code will follow the green line, and the program will print "Password OK :)", otherwise the code will follow the red line and print "Invalid Password!". We have to patch the "jz" (jump if zero) to make the code always jump to the right block. To do this, we just change "jz" for "jmp". We will now patch the binary using radare:
First we change the current seek to the position of the instruction we want to patch:
[0x08048414]> s 0x8048470
[0x08048470]>
Now we can print the instruction in this position, for example we will print 15 bytes at the current seek:
[0x08048470]> pD 15
0x08048470 740e v jz 0x480 ; sym.main+0x6c
; ------------------------------------
0x08048472 c7042496850408 dword [esp] = 0x8048596 ; "Invalid.Password!"
0x08048479 e8c2feffff ^ call 0x340 ; sym.printf
; ------------------------------------
0x0804847E eb0c v goto 0x48C ; sym.main+0x78
; ------------------------------------
[0x08048470]>
Here, the instruction 'jz 0x480' corresponds to the bytes '740e', if we change the '74' for an 'eb' we will change the 'jz' for 'goto' (or jmp). You can see the 'eb' is used for goto in the last instruction of the disassembly we've just printed.
To write 'eb' in the current seek we must open the file in write mode (by default radare opens everything in read-only mode). To switch to write mode we can type:
[0x08048470]> eval cfg.write=true
warning: Opening file in read-write mode
open rw crackme0x00
[0x08048470]>
And now we're ready to patch, just use the 'wx' command to write hex values and 'px' to print the result:
[0x08048470]> wx eb
[0x08048470]> px 20
offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 0123456789ABCDEF012345
.--------+-------------------------------------------------------+-----------------------
0x08048470 eb0e c704 2496 8504 08e8 c2fe ffff eb0c c704 24a9 ....$.............$.
[0x08048470]>
Now let's see the disassembly again, to see everything was patched as we expected:
[0x08048470]> pD 15
0x08048470 eb0e v goto 0x480 ; sym.main+0x6c
; ------------------------------------
0x08048472 c7042496850408 dword [esp] = 0x8048596 ; "Invalid.Password!"
0x08048479 e8c2feffff ^ call 0x340 ; sym.printf
; ------------------------------------
0x0804847E eb0c v goto 0x48C ; sym.main+0x78
; ------------------------------------
[0x08048470]>
And let's graph it again using the PG command, to see the code will flow only through the "Password OK" branch:
[0x08048470]> s sym.main
[0x08048414]> ag
TODO: Add ascii-art disassembly here
Finally just press 'q' to quit radare, and try the cracked program:
$ ./crackme0x00
IOLI Crackme Level 0x00
Password: foo
Password OK :)
Done! :D