This is a walkthrough for the CrackMe0x00A that was part of the Rensselaer Polytechnic Institute’s Binary Exploitation course in Spring 2015. You can follow along by downloading the challenges here. (mirror 1, mirror 2)
Let’s start by running the binary.

We can tell that this program is checking for a password and that something else would probably happen if we input the correct password. Let’s go for the low hanging fruit – strings.
$ strings ./crackme0x00a /lib/ld-linux.so.2 __gmon_start__ libc.so.6 _IO_stdin_used __isoc99_scanf puts __stack_chk_fail printf strcmp __libc_start_main GLIBC_2.7 GLIBC_2.4 GLIBC_2.0 PTRh D$,1 T$,e3 UWVS [^_] Enter password: Congrats! Wrong! ;*2$" g00dJ0B! GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 .symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .text .fini .rodata .eh_frame_hdr .eh_frame .ctors .dtors .jcr .dynamic .got .got.plt .data .bss .comment crtstuff.c __CTOR_LIST__ __DTOR_LIST__ __JCR_LIST__ __do_global_dtors_aux completed.6086 dtor_idx.6088 frame_dummy __CTOR_END__ __FRAME_END__ __JCR_END__ __do_global_ctors_aux pass1.c pass.1685 __init_array_end _DYNAMIC __init_array_start _GLOBAL_OFFSET_TABLE_ __libc_csu_fini strcmp@@GLIBC_2.0 __i686.get_pc_thunk.bx data_start printf@@GLIBC_2.0 _edata _fini __stack_chk_fail@@GLIBC_2.4 __DTOR_END__ __data_start puts@@GLIBC_2.0 __gmon_start__ __dso_handle _IO_stdin_used __libc_start_main@@GLIBC_2.0 __libc_csu_init _end _start _fp_hw __bss_start main _Jv_RegisterClasses __isoc99_scanf@@GLIBC_2.7 _init
There are some interesting lines. Let’s try ‘g00dJ0B!’.

And there we have it – the password is ‘g00dJ0B!’. However, that’s an unsatisfying ending. Let’s take a deeper look at what’s actually happening. We can assume that something like the following is happening:
#include <stdio.h> #include <strings.h> int main(void) { int found = 0; while (!found) { char buffer[1024]; scanf("%s", &buffer); if (strcmp(buffer, "g00d J0B!") != 0) { printf("Wrong!"); } else { printf("Congrats!"); found = 1; } } return 0; }
Let’s take a look through this in gdb.
$ gdb ./crackme0x00a (gdb) disass main Dump of assembler code for function main: 0x080484e4 <+0>: push ebp 0x080484e5 <+1>: mov ebp,esp 0x080484e7 <+3>: and esp,0xfffffff0 0x080484ea <+6>: sub esp,0x30 0x080484ed <+9>: mov eax,gs:0x14 0x080484f3 <+15>: mov DWORD PTR [esp+0x2c],eax 0x080484f7 <+19>: xor eax,eax 0x080484f9 <+21>: mov eax,0x8048640 0x080484fe <+26>: mov DWORD PTR [esp],eax 0x08048501 <+29>: call 0x80483d0 <printf@plt> 0x08048506 <+34>: mov eax,0x8048651 0x0804850b <+39>: lea edx,[esp+0x13] 0x0804850f <+43>: mov DWORD PTR [esp+0x4],edx 0x08048513 <+47>: mov DWORD PTR [esp],eax 0x08048516 <+50>: call 0x8048420 <__isoc99_scanf@plt> 0x0804851b <+55>: lea eax,[esp+0x13] 0x0804851f <+59>: mov DWORD PTR [esp+0x4],eax 0x08048523 <+63>: mov DWORD PTR [esp],0x804a024 0x0804852a <+70>: call 0x80483c0 <strcmp@plt> 0x0804852f <+75>: test eax,eax 0x08048531 <+77>: jne 0x8048554 <main+112> 0x08048533 <+79>: mov DWORD PTR [esp],0x8048654 0x0804853a <+86>: call 0x80483f0 <puts@plt> 0x0804853f <+91>: nop 0x08048540 <+92>: mov eax,0x0 0x08048545 <+97>: mov edx,DWORD PTR [esp+0x2c] 0x08048549 <+101>: xor edx,DWORD PTR gs:0x14 0x08048550 <+108>: je 0x8048567 <main+131> 0x08048552 <+110>: jmp 0x8048562 <main+126> 0x08048554 <+112>: mov DWORD PTR [esp],0x804865e 0x0804855b <+119>: call 0x80483f0 <puts@plt> 0x08048560 <+124>: jmp 0x80484f9 <main+21> 0x08048562 <+126>: call 0x80483e0 <__stack_chk_fail@plt> 0x08048567 <+131>: leave 0x08048568 <+132>: ret End of assembler dump.
Jump statements are a good way to identify branching statements. We have a call to the function ‘strcmp’ at 0x0804852a and a conditional jump to 0x8048554 (at 0x08048531) if the strcmp is not equal – that is, 0x8048554 should handle if a wrong password is input. This is confirmed by 0x0804855b printing something, with a jump back to 0x80484f9 (at 0x08048560) where printf (0x08048501) and scanf (0x08048516) are being called in preparation for another password guess.
The other side of this is if the condition for the jump at 0x08048531 is not fulfilled, the program will continue to print something (0x0804853a) and exit (0x08048550 -> 0x08048567).
So now we know roughly what’s happening, we need to confirm what we are comparing against. Although the assembly does give us a memory address that may be interesting, we can’t really read it.
0x08048523 <+63>: mov DWORD PTR [esp],0x804a024
So to look at the memory address, lets go back to gdb.
$ gdb ./crackme0x00a (gdb) x/s 0x804a024 0x804a024 <pass.1685>: "g00dJ0B!"
And voila – by interpreting that memory address as a string, we get ‘g00dJ0B!’.
Radare2 does this really easily. We can just visually inspect the assembly and radare2 sets out everything for us. Input the commands below, and simply scroll down.
$ r2 ./crackme0x00a [0x08048430]> vpp

And again, red text on the right shows that the password is once again ‘g00dJ0B!’.
If you’re happy with the results of CrackMe0x00A, it’s time to move onto CrackMe0x00B.