This post is going to continue with learning how to use Radare2 via the IOLI crackmes. Last time was more about one of the included tools, Rabin2. Again this is all from the Radare2 book, I’m just trying to fill in some gaps that I felt could be more pedantic for those new to reverse engineering and get the hang of r2 myself.
Lets Get Started:
$ rabin2 -I crackme0x01 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
Getting the information about the crackme0x01 from rabin2 we learn the following about it:
The code is included with the file again and it’s written in C and it’s a 32-bit ELF executable.
Lets see what the imports tell us about this file:
$ rabin2 -i crackme0x01 [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
This time scanf and printf functions are imported but conspicuously missing is the strcmp function from crackme0x00. That doesn’t look good for being able to determine the password just by looking at the strings.
Lets take a look at the strings:
$ rabin2 -z crackme0x01 vaddr=0x08048528 paddr=0x00000528 ordinal=000 sz=25 len=24 section=.rodata type=ascii string=IOLI Crackme Level 0x01\n vaddr=0x08048541 paddr=0x00000541 ordinal=001 sz=11 len=10 section=.rodata type=ascii string=Password: vaddr=0x0804854f paddr=0x0000054f ordinal=002 sz=19 len=18 section=.rodata type=ascii string=Invalid Password!\n vaddr=0x08048562 paddr=0x00000562 ordinal=003 sz=16 len=15 section=.rodata type=ascii string=Password OK :)\n
There unfortunately isn’t a string standing out as a password to attempt. But we see that the functionality is just requesting a password and telling us if it’s correct or not. Running the program has the expected output.
$ ./crackme0x01 IOLI Crackme Level 0x01 Password: test Invalid Password!
No surprises there. Note that if you’re running a 64-bit system you need to make it so 32-bit executables con run before you can run any of the crackmes.
Lets Do Some Disassembly (Disassembling?):
First things first we are looking for some way of comparing a password. Since we are moving to disassembly we are looking for some way of comparing things in disassembly. That means we are going to be looking for some kind of comparison instruction.
There’s a few ways of comparing things in assembly. We coul have a subtraction and check if the value is zero executing a jump instruction on the result. Or we could use an explicite compare instruction. An indicator of what we want should be a jump after the instruction to the correct print string instruction.
[0x08048330]> pdf@main ;-- main: / (fcn) sym.main 113 | sym.main (); | ; var int local_4h @ ebp-0x4 | ; var int local_4h_2 @ esp+0x4 | ; JMP XREF from 0x08048347 (entry0) | ; DATA XREF from 0x08048347 (entry0) | 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_n ; [0x8048528:4]=0x494c4f49 LEA str.IOLI_Crackme_Level_0x01_n ; "IOLI Crackme Level 0x01." @ 0x8048528 | 0x08048407 e810ffffff call sym.imp.printf ; int printf(const char *format); | 0x0804840c c70424418504. mov dword [esp], str.Password: ; [0x8048541:4]=0x73736150 LEA str.Password: ; "Password: " @ 0x8048541 | 0x08048413 e804ffffff call sym.imp.printf ; int printf(const char *format); | 0x08048418 8d45fc lea eax, dword [ebp - local_4h] | 0x0804841b 89442404 mov dword [esp + local_4h_2], eax | 0x0804841f c704244c8504. mov dword [esp], 0x804854c ; [0x804854c:4]=0x49006425 ; "%d" | 0x08048426 e8e1feffff call sym.imp.scanf ; int scanf(const char *format); | 0x0804842b 817dfc9a1400. cmp dword [ebp - local_4h], 0x149a ; [0x149a:4]=0x2ec0804 | ,=< 0x08048432 740e je 0x8048442 | | 0x08048434 c704244f8504. mov dword [esp], str.Invalid_Password__n ; [0x804854f:4]=0x61766e49 LEA str.Invalid_Password__n ; "Invalid Password!." @ 0x804854f | | 0x0804843b e8dcfeffff call sym.imp.printf ; int printf(const char *format); | ,==< 0x08048440 eb0c jmp 0x804844e | |`-> 0x08048442 c70424628504. mov dword [esp], str.Password_OK_:__n ; [0x8048562:4]=0x73736150 LEA str.Password_OK_:__n ; "Password OK :)." @ 0x8048562 | | 0x08048449 e8cefeffff call sym.imp.printf ; int printf(const char *format); | | ; JMP XREF from 0x08048440 (sym.main) | `--> 0x0804844e b800000000 mov eax, 0 | 0x08048453 c9 leave \ 0x08048454 c3 ret [0x08048330]> ? 0x149a 5274 0x149a 012232 5.2K 0000:049a 5274 "\x9a\x14" 0001010010011010 5274.0 5274.000000f 5274.000000
Unfortunately the output doesn’t fit nicely on the screen, but I’m going to leave it in that format. If I change the format it’s harder to see where the instructions lead.
The bold instructions are the one’s that are interesting. The cmp instruction compares two things, in this case a value in memory located at esp – local_4h and the hex value 0x149a. The next instruction jumps to the instruction at 0x8048442 if the values are equal for cmp. The instruction at 0x8048442 is to load the string Password_OK and print it. So we want to make this compare instruction take the following jump.
We can try to just input the value 0x149a and see what happens.
$ ./crackme0x01 IOLI Crackme Level 0x01 Password: 0x149a Invalid Password!
No luck. The next thing to try is to convert the number to a binary representation. We can do that with the r2 functionality as well.
? 0x149a 5274 0x149a 012232 5.2K 0000:049a 5274 "\x9a\x14" 0001010010011010 5274.0 5274.000000f 5274.000000
The decimal version is 5274. So we can try that and see what happens.
$ ./crackme0x01 IOLI Crackme Level 0x01 Password: 5274 Password OK :)
That’s the answer.
That was a nice intro to using some basic disassembler functionality. We also got a nice peek at some assembly code to figure out the password from. This was a pretty simplistic example but we need to start looking at a lot of assembly code and determining functionality. There will be a lot of that coming in the future. Possibly some assembly programs as well.