Intro to radare2

Tools

Posted by coastal on October 31, 2017

I’m working on a challenge binary that requires some reverse engineering and it was suggested to work through it with radare2. The radare2 framework (r2 for short) is an oft-talked about open-source reverse engineering framework that has a reputation for being less than welcoming to new users…

However, I’m up to the challenge, and got to cracking. I decided to work with the IOLI-crackme challenge, mainly because I found this great tutorial that uses both r2 and these binaries, which will help if I hit a real roadblock.

setup

Installing r2 couldn’t be more straightforward

git clone https://github.com/radare/radare2
cd radare2/sys
./install.sh

I tried installing via homebrew, but was getting some weird errors that tracked back to a dependency. After uninstalling and installing in the recommended fashion, all those issues disappeared.

0x00

First thing to do is download the binaries and load the first challenge up for inspection:

wget http://pof.eslack.org/tmp/IOLI-crackme.tar.gz
tar -xzvf
cd IOLI-crackme/bin-linux
coastal@ubuntu32server:~/intro-to-r2/IOLI-crackme/bin-linux$ radare2 crackme0x00
 -- Select your architecture with: 'e asm.arch=<arch>' or r2 -a from the shell
[0x08048360]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)

The aaa command is the newer version of the aa command which translates to english as ‘analyze all’. This command abbreviation is a theme that we see throughout r2. As we can see in the output, it is doing a number of analyses on the binary and saving the analyzed information for future use. One of the first things to do when reverse engineering any binary is to check out the strings to get an idea of what the function might be doing. We can do this with the iz command.

[0x08048360]> iz
vaddr=0x08048568 paddr=0x00000568 ordinal=000 sz=25 len=24 section=.rodata type=ascii string=IOLI Crackme Level 0x00\n
vaddr=0x08048581 paddr=0x00000581 ordinal=001 sz=11 len=10 section=.rodata type=ascii string=Password: 
vaddr=0x0804858f paddr=0x0000058f ordinal=002 sz=7 len=6 section=.rodata type=ascii string=250382
vaddr=0x08048596 paddr=0x00000596 ordinal=003 sz=19 len=18 section=.rodata type=ascii string=Invalid Password!\n
vaddr=0x080485a9 paddr=0x000005a9 ordinal=004 sz=16 len=15 section=.rodata type=ascii string=Password OK :)\n

In our challenge file, it appears to take in a password and compare it to an expected password. Let’s run the file and see if that is the case

coastal@ubuntu32server:~/intro-to-r2/IOLI-crackme/bin-linux$ ./crackme0x00
IOLI Crackme Level 0x00
Password: password
Invalid Password!

That does in fact seem to be what is going on. Now given that this is the first challenge (and likely fairly easy), we notice that one of the strings that r2 found looks suspiciously like a password: 250382. What happens if we enter that?

coastal@ubuntu32server:~/intro-to-r2/IOLI-crackme/bin-linux$ ./crackme0x00
IOLI Crackme Level 0x00
Password: 250382
Password OK :)

Pretty easy :)

However, if we hadn’t noticed that nice little obvious answer, we could dive into the disassembly. First thing to do would be to check out the main function, which we can do with pdf@sym.main, or print disassembled function @ offset.

[0x0804a8a0]> pdf@sym.main
            ;-- main:
/ (fcn) main 127
|   main ();
|           ; var int local_18h @ ebp-0x18
|           ; var int local_4h @ esp+0x4
|              ; DATA XREF from 0x08048377 (entry0)
|           0x08048414      55             push ebp
|           0x08048415      89e5           mov ebp, esp
|           0x08048417      83ec28         sub esp, 0x28               ; '('
|           0x0804841a      83e4f0         and esp, 0xfffffff0
|           0x0804841d      b800000000     mov eax, 0
|           0x08048422      83c00f         add eax, 0xf
|           0x08048425      83c00f         add eax, 0xf
|           0x08048428      c1e804         shr eax, 4
|           0x0804842b      c1e004         shl eax, 4
|           0x0804842e      29c4           sub esp, eax
|           0x08048430      c70424688504.  mov dword [esp], str.IOLI_Crackme_Level_0x00_n ; [0x8048568:4]=0x494c4f49 ; "IOLI Crackme Level 0x00\n"
|           0x08048437      e804ffffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804843c      c70424818504.  mov dword [esp], str.Password: ; [0x8048581:4]=0x73736150 ; "Password: "
|           0x08048443      e8f8feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048448      8d45e8         lea eax, [local_18h]
|           0x0804844b      89442404       mov dword [local_4h], eax
|           0x0804844f      c704248c8504.  mov dword [esp], 0x804858c  ; [0x804858c:4]=0x32007325
|           0x08048456      e8d5feffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x0804845b      8d45e8         lea eax, [local_18h]
|           0x0804845e      c74424048f85.  mov dword [local_4h], str.250382 ; [0x804858f:4]=0x33303532 ; "250382"
|           0x08048466      890424         mov dword [esp], eax
|           0x08048469      e8e2feffff     call sym.imp.strcmp         ; int strcmp(const char *s1, const char *s2)
|           0x0804846e      85c0           test eax, eax
|       ,=< 0x08048470      740e           je 0x8048480
|       |   0x08048472      c70424968504.  mov dword [esp], str.Invalid_Password__n ; [0x8048596:4]=0x61766e49 ; "Invalid Password!\n"
|       |   0x08048479      e8c2feffff     call sym.imp.printf         ; int printf(const char *format)
|      ,==< 0x0804847e      eb0c           jmp 0x804848c
|      ||      ; JMP XREF from 0x08048470 (main)
|      |`-> 0x08048480      c70424a98504.  mov dword [esp], str.Password_OK_:__n ; [0x80485a9:4]=0x73736150 ; "Password OK :)\n"
|      |    0x08048487      e8b4feffff     call sym.imp.printf         ; int printf(const char *format)
|      |       ; JMP XREF from 0x0804847e (main)
|      `--> 0x0804848c      b800000000     mov eax, 0
|           0x08048491      c9             leave
\           0x08048492      c3             ret

Looking at 0x08048469 we see a strcmp call and a jump to printf of ‘Password OK :)\n’ if the compared strings are indeed equal. If we look at the values that were compared, one of them is our previous string of interest:

0x0804845e      c74424048f85.  mov dword [local_4h], str.250382 ; [0x804858f:4]=0x33303532 ; "250382"

The other is our input from scanf. It becomes clear that our input needs to be 250382. And of course…

coastal@ubuntu32server:~/intro-to-r2/IOLI-crackme/bin-linux$ ./crackme0x00
IOLI Crackme Level 0x00
Password: 250382
Password OK :)

0x01

First step again is to check the strings

[0x08048330]> iz
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

No easy string password this time. However, when we check out the disassembly of main, we see that the comparison branch for success is a fairly obvious one

0x0804842b      817dfc9a1400.  cmp dword [local_4h], 0x149a ; [0x149a:4]=-1

Convert that hex value to decimal

0x149a = 5274

And…

coastal@ubuntu32server:~/intro-to-r2/IOLI-crackme/bin-linux$ ./crackme0x01
IOLI Crackme Level 0x01
Password: 5274
Password OK :)

Nice

0x02

Unfortunately this one doesn’t store our password comparison value straight in the disassembly. Let’s look at the relevant disassembly and see if we can track down the comparison and values,

|           0x0804842b      c745f85a0000.  movl $0x5a, local_8h        ; 'Z' ; 90
|           0x08048432      c745f4ec0100.  movl $0x1ec, local_ch       ; 492
|           0x08048439      8b55f4         movl local_ch, %edx
|           0x0804843c      8d45f8         leal local_8h, %eax
|           0x0804843f      0110           addl %edx, (%eax)
|           0x08048441      8b45f8         movl local_8h, %eax
|           0x08048444      0faf45f8       imull local_8h, %eax
|           0x08048448 b    8945f4         movl %eax, local_ch
|           0x0804844b      8b45fc         movl local_4h, %eax
|           0x0804844e b    3b45f4         cmpl local_ch, %eax
|       ,=< 0x08048451      750e           jne 0x8048461
|       |   0x08048453      c704246f8504.  movl $str.Password_OK_:__n, (%esp) ; [0x804856f:4]=0x73736150 ; "Password OK :)\n"
|       |   0x0804845a      e8bdfeffff     calll sym.imp.printf        ; int printf(const char *format)
|      ,==< 0x0804845f      eb0c           jmp 0x804846d
|      |`-> 0x08048461      c704247f8504.  movl $str.Invalid_Password__n, (%esp) ; [0x804857f:4]=0x61766e49 ; "Invalid Password!\n"
|      |    0x08048468      e8affeffff     calll sym.imp.printf        ; int printf(const char *format)

I changed syntax to AT&T because that’s what I’m used to with the command e asm.syntax = att, but you can use whatever syntax you’re most comfortable with. Additionally, this dissambly snippet has some b characters after the addresses. This is because I opened the file in debug mode r2 -d {binary}. Breakpoints are set with the command db {address} and removed with db -{address}.

If we look at the dissassembly, we see that at 0x0804844e we do a comparison whose return value branches on failure to our fail condition. Looking back up the instructions, we see that local_8h and local_ch both are assigned values that are not from scanf. Tracing these values down, there is some addition and multiplication that happens to these values before they are compared to our input value that is stored in local_4h. The final value after the operations are performed on local_8h and local_ch (aka the password) is stored in local_ch. local_ch is stashed out of eax after the operations into local_ch at 0x08048448, so if we break on that address and inspect our registers, we should find the password value in eax without having to figure out the exact computation that is performed on the values 90 and 492.

Sidenote, when you’ve run through a binary in debug mode, you can reset your execution context with the do command. dc for continuing after hitting a breakpoint or starting from do

[0xb7f2eb20]> dc
IOLI Crackme Level 0x02
Password: 1       
hit breakpoint at: 8048448
[0x08048448]> dr
eax = 0x00052b24
ebx = 0x00000000
ecx = 0xbfd1fd44
edx = 0x000001ec
esi = 0x00000001
edi = 0xb7f1c000
esp = 0xbfd1fd40
ebp = 0xbfd1fd68
eip = 0x08048448
eflags = 0x00000206
oeax = 0xffffffff
[0x08048448]> pf r (eax)
  : eax : 0x00052b24

Converting that to decimal we get 338724.

coastal@ubuntu32server:~/intro-to-r2/IOLI-crackme/bin-linux$ ./crackme0x02
IOLI Crackme Level 0x02
Password: 338724
Password OK :)

Bingo!

If we did want to figure out the computation, it’s pretty straightforward as well

|           0x0804842b      c745f85a0000.  movl $0x5a, local_8h        ; 'Z' ; 90
|           0x08048432      c745f4ec0100.  movl $0x1ec, local_ch       ; 492
|           0x08048439      8b55f4         movl local_ch, %edx
|           0x0804843c      8d45f8         leal local_8h, %eax
|           0x0804843f      0110           addl %edx, (%eax)
|           0x08048441      8b45f8         movl local_8h, %eax
|           0x08048444      0faf45f8       imull local_8h, %eax

First we add 90 to 492 at 0x0804843f, then we multiply that result by itself. The function’s pseudocode is something like:

def iscorrect(input_val):
	a = 90
	b = 492
	if input_val == (90 + 492) ** 2:
		print("win")
	else:
		print("fail")

I’ll be continuing this series on this post in the future so keep an eye out