We’ve gone through the first two IOLI crackme binaries. That’s where the Radare2 book ends with the walkthroughs. That’s not where things are going to stop here though. In this post we are going to look at reversing the IOLI-crackme0x02.
Lets Get Started:
To start off with we are going to take a look at the strings and imports of the binary with rabin2. Just like the first two crackme’s.
$ rabin2 -I crackme0x02 havecode true pic false canary false nx true crypto false va true intrp /lib/ld-linux.so.2 bintype elf class ELF32 lang c arch x86 bits 32 machine Intel 80386 os linux minopsz 1 maxopsz 16 pcalign 0 subsys linux endian little stripped false static false linenum true lsyms true relocs true rpath NONE binsz 7499
We see again that we have a C program compiled for 32-bit x86 Linux architecture. Lets take a look at the function imports for the binary.
$ rabin2 -i crackme0x02 [Imports] ordinal=001 plt=0xffffffff bind=UNKNOWN type=NOTYPE name=__gmon_start__ ordinal=002 plt=0x080482fc bind=GLOBAL type=FUNC name=__libc_start_main ordinal=003 plt=0x0804830c bind=GLOBAL type=FUNC name=scanf ordinal=004 plt=0x0804831c bind=GLOBAL type=FUNC name=printf 4 imports
We are left without the comparing function in this binary again. Lets take a look at the strings.
$ rabin2 -z crackme0x02 vaddr=0x08048548 paddr=0x00000548 ordinal=000 sz=25 len=24 section=.rodata type=ascii string=IOLI Crackme Level 0x02\n vaddr=0x08048561 paddr=0x00000561 ordinal=001 sz=11 len=10 section=.rodata type=ascii string=Password: vaddr=0x0804856f paddr=0x0000056f ordinal=002 sz=16 len=15 section=.rodata type=ascii string=Password OK :)\n vaddr=0x0804857f paddr=0x0000057f ordinal=003 sz=19 len=18 section=.rodata type=ascii string=Invalid Password!\n
Now we see that we aren’t going to get anywhere using these tools on this binary. So we are going to have to do something else.
Digging in the Radare2 Toolbox:
We have already seen that we can disassemble the binary with Radare2. Lets see what else we can do with it. I want to disassemble the main function and jump execution to the entry point of it. Then I want to see what happens with the registers as we execute main.
To accomplish this I found out the entry point of the main function by executing the
pdf@main commands to find the address of the entry point. There may be an easier way to do this but its what worked for me for now. Then I set a break point using
db 0x080483e4 at the address of the entry point of the main function. I hit
dc to run the program until the breakpoint. Now that we are at the main function we can go into a really cool visual mode using
V! and we get the following awesome display:
So we have a nice layout of our disassembled main function, the symbols in the program, the stack, the registers, and the RegisterRefs.
Looking For Clues:
So now the task is figuring out what the correct password is and giving it to the program as input. We have execution stopped right at the construction of the stack frame for main. We can step through the program using the
ds command. The highlighted address is the location of EIP.
I’m going to go ahead and dig all the way into this discussing what happens at each step. Then we can see how program execution happens.
- push ebp : pushes the value of ebp onto the stack to use as a return address.
- mov ebp, esp: moves the value of esp into ebp. starting out ebp has value 0x00000000 in our register window. After execution we can see the value is changed to 0xffc95128 which is what was held in esp. There is also a nice highlighting effect of which registers have changed values. In this step eip, and ebp.
- sub esp, 0x18: 0x018 is subtracted from esp extending the stack by 0x18 bytes. esp is now 0xffc95110
- and esp, 0xffffff0: this zeros out the least significant bit of the esp register. Since it was already 0 nothing is changed here.
- mov eax, 0: moves 0 into eax.
- add eax, 0xf: moves the value 15 into eax in the form of 0xf.
- add eax, 0xf: again we add 15 into eax so the value of eax is now at 0x1e = 30
- shr eax, 4: shift right by 4 bits which leaves 0x1 in eax.
- shl eax, 4: shift left by 4 bits which puts 0x10 in eax.
- sub esp, eax: subtract eax from esp and store in esp. This has the effect of extending the stack by 16 bytes or by 0x10.
- mov dword[esp], str.IOLI_Crackme_Levl_oxo2_n: moves the string into the memory address pointed at by esp.
- call sym.imp.printf: calls the printf function to print the string at esp.
- This is repeated for the other outputs and then we scan in the attempted password.
- lea eax, dword [ebp – 4]: loads the address of the value in ebp – 4 into eax.
- mov dword [esp + 4], eax: moves the value from eax to esp + 4. Which means we moved the memory value at ebp – 4 to ebp + 4. We can trace these two easily enough by comparing their values.
- eax = 0xff813714 after we load the address of ebp – 4. Note that it’s the address we are moving into eax, not the value stored in the memory.
- Then esp + 4 = 0xff8136f4. Which is where we are storing the value 0xff813714.
- mov dword [esp], 0x804856c: This means we are storing the value 0x804856c at the memory address of esp. This looks like an instruction address and it points to an and operation on eax. Then scanf is called.
- mov dword[ebp – 8], 0x5a: we are storing 0x5a at ebp – 8 = 0xff813710. Note that 0x5a = 90.
- mov dword[ebp – oxc], 0x1ec: sore 0x1ec = 492 in the address epb – 0xc =0xff81370c.
- mov edx, dword[ebp – 0xc]: move the value in address ebp – 0xc = 0xff81370c. So now edx has the value 0x1ec stored in it.
- lea eax, dword[ebp – 8]: store the address of ebp – 8 = 0xff813710 in eax.
- add dword [eax], edx: add the value stored in ebx to value at eax. This is 0x5a + 0x1ec = 0x246. Remember that there is a pointer to ebp – 8 in eax, so those are the values being added.
- mov eax, dword[ebp – 8]: move the value stored at ebp – 8 = 0x246 into eax.
- imul eax, dword[ebp – 8]: multiply eax and the value at ebp – 8 which becomes 0x52b24.
- mov dword [ebp – 0xc], eax: so we move the value in eax to the address ebp – 0xc.
- We can watch this as we do this in the stack window as well. You can see the value 0x052b24 stored on the stack in the second row under the letter C. Remember that x86 is little endian so its least significant bit first.
- mov eax, dword[ebp – 4]: move the value at ebp – 4 to eax. Which is where the entered password from the prompt should be stored.
- cmp eax, dword[ebp – 0xc]: This is comparing eax and the value we just put into memeory at ebp – 0xc. So we should know what the correct password is now 52b24 = 338724. Lets see if we got it right:
Testing Out the Password:
$ ./crackme0x02 IOLI Crackme Level 0x02 Password: 338724 Password OK :)
We got it correct.
This crackme involved dealing with the disassembled program more than the previous one. We actually had to trace through the instructions to determine what the password was. I went through the instructions in a bit more detail than was probably necessary, as we could have just put a break point at the compare instruction and examined the stack to pull the value out. But I think it’s good to get as much practice with reading assembly and following it as possible. I also like going through assembly to see what’s happening.
We also learned a really useful visual mode for Radare2. We now have some more time with the program as much as more time doing some reverse engineering.