Radare2 — Keep It Or Leave It?

Sagi Dana
12 min readMay 6, 2019

--

Last Monday I attended a lecture from pancake (the author of the Radare2 framework) about the Radare2 framework and its features. I’ve used r2 a few times before but I never knew the amount of features it has and how deep the rabbit hole goes.

This is a story about how I examine r2 and its features to see how it fits my needs as reverse engineer and rank its convenience and efficiency.

  • I only have about 15 hours of playing with r2 so keep that in mind.
  • A lot of r2 features and capabilities are not going to be explained here, both because I don’t know them all, and I don’t find them relevant for my use.

The features I’m looking at:

  • Graph view for analyzing program behavior.
  • Debugging capabilities.
  • Memory manipulation.
  • Renaming and refactoring capabilities - to ease the reversing process.
  • Comments.
  • Cross referencing of functions, variables, memory addresses.
  • Decompilation capabilities.
  • Automation of debugging process - modify program behavior while debugging using scripting language.
  • Patching and modifying the binary capabilities.
  • Shellcoding.
  • Saving and reloading projects.
  • Documentation and community.

Static analysis:

When it comes to reversing and analyzing code I look for convenient views to see everything I need, so I listed the views I use the most:

  • Functions
  • Graph view of the assembly
  • Text view of the assembly
  • Strings
  • Cross references
  • Decompile

So let’s see what r2 has to offer:

r2 ls ; Open ls binary
[0x0005350]> aaa ; To analyze

Let’s see some useful information about the file:

iI

How about some visualization?

After some time of playing with r2 I think one of my favorite views is the “functions view”:

v ; Enter visual mode.
v ; Enter functions view. (can do 'vv' in one command).

To the right we have the disassembly of the function and to the left its name. Its also possible to change the function’s name, local variables’s names and see it’s cross references, this is pretty cool and easy.

From there we can simple press “enter” and the disassembly view will appear with the selected function.

  • One important note, while in visual mode you can execute r2 commands using the “:” key.

r2 has the seek command (“s”) that controls the location you are currently at within the file. When selecting a function, for example, it jumps to the relevant location.

s ; print the current location (seek)

If we need to change location to a specific function or address simply type:

s <function_name>/<address>

OK, we can view the functions pretty nicely, what about graph view?

V ; Uppercase v or space switches to the graph view of the function you are in

So, we can view the function’s flow, we can also control the zoom level using

+ ; zoom in
- ; zoom out

Also while in graph mode we have a selected node, the selected node can be changed

TAB ; 
t ; follow the true condition branch
f ; follow the false condition branch
u ; undo follow

One important thing is when we want to follow a function call for example we simply type the letters printed in the comment next to the call instruction (starts with “o”):

Cross- references of the function can be viewed by pressing “x”

And using the arrow keys you can choose which xref to follow. Remember you can always return back pressing “u” (undo seek) key.

  • You can close what ever state you’re in and return back at anytime by pressing “q”.

What about comments and renaming? Well, renaming a function

dr ; rename the current function

The best way I found of renaming a local variable is by a command (press “:” to run a command)

afvn <new_name> <old_name>

After the command:

Comments. Well, you can always add a comment using the “;” key and while in graph mode the comment will be added to the top of the currently selected node.

In case I want my comment to be in a specific location (like most cases), well, this one was a bit tricky… I have to admit I am disappointed, again this is the best option I found and I am not happy about that approach, but that’s what it is:

By pressing “D” we can add the disassembly view next to the graph view:

This time when we press the up and down arrows we are able to choose a specific instruction in the disassembly view, once we reached the specific instruction we can press “;” and write our comment, this time, in the right location.

  • Please note that we have more methods of adding comments (appending, overriding, etc…)

OK, one more mandatory thing for me that took forever to understand, I wanted a way to name a specific location in memory, sound easy but here is what I did:

I created a flag in that address and named it the way I wanted

f name @<address>

I expected it to rename the address “0x00017c4a” to “name” but it ended up as a comment instead.

I wanted it to replace the address completely (this would prevent me from accidentally ignoring the comment). It seems that while creating a flag and choosing its name, we have prefixes that behave differently, like “sym.”, “fcn.”, etc…

So when I create a symbol for that address I start the name with the “sym.” prefix and it will replace the address completely.

f sym.name @0x00017c4a

Strings in the file is pretty easy

izz~<string_we_are_looking_for>

For the strings view and any other view for that matter r2 has a powerful concept of visual panels, to enter visual panels mode write this command

v!

This mode is amazing, each panel’s content is the output of the command it’s running (written next to the name).

While in that mode we can press tab to switch between and navigate through the panels.

We can press “m” to get to the menu bar and select options there.

If we choose a specific panel and press “e” we can rename it and change its content, so lets modify the Functions panel to be the Strings panel:

And after “enter” (set the cache setting to “off” if you want the view to be updated automatically):

Let’s say we want to change the layout of that screen, press “w” to enter window mode. Now we can modify the width and height of each panel, close panels and create panels.

<up> ; select
<down> ; select
<left> ; select
<right> ; select
<shitf>+<up> ; resize
<shitf>+<down> ; resize
<shitf>+<left> ; resize
<shitf>+<right> ; resize
X ; close panel (notice uppercase x)
| ; split panels vertically
- ; split panels horizontally

As for decompiling goes, it’s quite interesting, I needed to use r2pm (the r2 package manager) to install r2dec package that enable me to decompile.

r2pm init
r2pm install r2dec

Once installed let’s return to “main” and…

s main
pdda ; Decompile current function (with assembly)
pdd ; Decompile current function (without assembly)

I’ll remind you that we can write this command (or any other desired command) inside the panel’s view and it will show it.

This makes r2 a very powerful tool.

So I think I covered most of the mandatory views and functionality I needed while doing some static analysis in native binaries. I checked, and everything is the same and works perfectly when dealing with the ARM architecture too.

Dynamic analysis:

When it comes to debugging I look for the following views to be present:

  • Graph view of the assembly
  • Text view of the assembly
  • Cross references
  • Hexdump
  • Registers
  • Stacktrace

So let’s see what r2 has to offer:

If we want to debug with r2 we can open r2 with the “-d” flag (make sure to run with sufficient privileges)

r2 -d ls
aaa

These are the most common debugging commands in r2:

d? ; get help on debugger commands
ds 3 ; step 3 times
db 0x8048920 ; setup a breakpoint
db -0x8048920 ; remove a breakpoint
dc ; continue process execution
dcs ; continue until syscall
dd ; manipulate file descriptors
dm ; show process maps
dmp A S rwx ; change permissions of page at A and size S
dr eax=33 ; set register value. eax = 33

Once launched with “-d”, switch to visual panels view (“!v”)

afl~main ; list functions and grep for main function
db <main_address> ; add breakpoint in main
;db main ; also an option ;)
dc ; Continue execute until hits the main breakpoint.

There are many ways we can debug a program: without visual mode, in visual mode, disassembly view, graph view, visual panels mode — all of these methods are possible and this makes it pretty awesome.

I have yet learned how to save a customize panels and completely master all the possible views. The level of customization is impressive though.

  • Note that while in menu (entered by pressing “m”) we have the common panels just one click away, that way we don’t need to memorize all the panel commands

For now let’s see what views I found interesting:

dr ; registers view
agf ; graph view
xc @<address> ; hexdump view
px 256@r:SP ; stack view
adt ; backtrace
dm ; maps view

We can create the screen with whatever layout we wish.

  • After a few minutes of playing with the panels view I noticed a few bugs that caused me to reopen the mode and start over, I believe that this mode is not as stable as the other modes for now.

Here is an example of a simple layout I did for myself.

  • For now I have to say that there is a lot to learn (commands to remember, modes to became familiar with) and this gives me reasons both to leave it and to stay with it.

Now that the visual panels are open and configured to our needs, we can start debugging.

Press “s” and “S” (“F7” and “F8” also work) to step into and step over respectively inside the visual mode.

Notice that the current instruction is pointed out by the “; — rip:” comment:

While debugging in panels view we can still press “x” to get cross references to the current function.

To get xref to an address or a flag

ax <addr> ; get cross references to address
axF <flag_name> ; get cross references to flag

To get xref of local variables

afvR <var_name> ; list of addresses that read from that variable.
afvW ; list of addresses that write to that variable.
  • For me it would be good if I could rotate between different visual modes whilst still being able to return to the visual panels mode at any time, the problem is that the visual panels always reset which is extremely irritating.
  • I have to say that the debugging experience I’ve had in r2 wasn’t that great especially compared to IDA. It is possible though that this was caused by my lack of experience with r2.
  • One thing though I will say in favour of r2 is that scripting debugging sessions and automating bypasses for some anti-debugging techniques is as straight forward as walking, once we’re already debugging with r2 that is.

Memory manipulation in r2 is probably the most convenient and powerful I’ve seen.

Enter visual mode and change modes (by pressing “p”) until you reach hexdump view. In this view you can simply hit “i” (insert mode) and start typing the data you wish to modify.

And this also works in visual panels mode with any view you choose, even the disassembly

This is just so cool and useful when it comes to patching programs.

  • My suggestion for you is take a day and read at least the “Basic commands” section in the r2 book to understand what can be done with the “c” (compare), “w”(write), “p” (print) and “y” (yank) commands.

As for memory representation we have the “p” (print) command that lets us view the memory in countless ways.

Just pick the command you need:

r2 also enable us to see memory mapping

iS ; List sections 
iSS ; List segments
dm ; Show memory map

We can also load specific libraries in specific locations and control the exact memory layout of everything, this is done using the “o” command (I won’t show examples of this, but you can search for examples by yourself ;)).

Shellcoding and patching assembly

A super convenient and pretty awesome feature of r2 is that we can open disassembly view, hit “i” and edit the hex of the instruction, but you already knew that (if you paid attention), but this is not all, while in visual mode we can press “A” to bring the interactive assembler of r2 and write the assembly instruction we wish directly to the file.

What’s more, in the disassembly view we can press TAB to change between different assembly views

And by pressing “c” (cursor mode) we can point to specific locations within the disassembly view without scrolling the whole page down, this is so convenient while assembling.

But one of the most impressive things is that we can combine this with the built-in emulator simply by pressing “s” and “S” without even needing a real binary and simply iterate through your own instructions and see their effects

  • In the example above I opened r2 with the “-” flag to work in memory.
r2 -

What about saving the work we did and reload it?

Well the basics are straight forward, we use the “P” command.

I also wanted to write about android reversing and debugging using r2 because we meet there different challenges as a researches and it could have being very interesting to compare it to JEB and the likes, but this one is getting too long so I decided to finish it here.

The overall documentation of r2 is not that impressive and I personally expected a bit more. That being said, the book is pretty amazing and I managed to learn quite a lot in a short period of time which is nice.

Overall, r2’s capabilities are overwhelming and very impressive compared to any tool I know.

Here you can find an r2 cheat-sheet that contains all of the commands discussed here.

I know that I’m definitely keeping r2 ;)

Good luck and happy reversing!

--

--