Introduction to Radare2 Debug Mode and r1 program reversing (x86-64, Linux)

Radare2 allows not only to browse and analyze programs, but also to debug them. There are a huge amount of commands and developers continue to work on it.

To run radare2 in debug mode, use -d option.

Let’s download the training program and make it executable:

cd /tmp
wget https://github.com/wapiflapi/exrs/raw/master/reverse/r1
chmod +x ./r1

Now run this program in debug mode of radare2. Use -d option and the program name, pass some argument because r1 (our reversing program) requires it (this is a password we need to find out):

r2 -d ./r1 argument 

We are in radare2 commandline. To see all debug-specific commands, use d? command:

d? 		# List of debug commands
...
db[?]                   Breakpoints commands
dc[?]                   Continue execution
dm[?]                  Show memory maps
dr[?]                    Cpu registers
ds[?]                    Step, over, source line
... And more

Breakpoint allows us to stop a program execution when it is reached. Let’s set a breakpoint on main function. This is the first function of a program, after the initialization of additional libraries which are specific for operating system:

db?	          # Breakpoints commands
db main     # Set breakpoint on main function

Now we need to run the program until this breakpoint:

dc?     #  Execution continuation commands
dc	  #  Continue execution until breakpoint

We are in main function. To see a program code, it is better to enter the visual mode. Type ‘V’ and press p/P to switch between visual modes:

V       # Enter Visual mode, press p/P to switch between visual modes

As we can see, here is a simple code without other prompts. It is possible for radare2 to analyze the program code and name the functions and references.

Visual mode before program analysis. There is no tips, functions and variables named
Visual mode before program analysis

To analyze the program without leaving the visual mode, press : ,then type ‘aaa‘ and press [Enter]. After analysis press [Enter] in a empty commandlime to return to the visual mode:

aaa
How aaa command works in Radare2
Make program analysis

Now there are more tips, there are names of functions and references. This really simplifies work in radare2.

Visual mode after program analysis (aaa command) in Radare2
After analysis. More tips and references, function and variable names are visible

Note, at the very beginning of main function rsi register contains a pointer to argv array (it contains all argument strings passed to a program). This pointer is placed in var_10h variable (rbp-0x10 address, located in programs stack) – see tips on the image above.

This is the most comfortable visual mode for working in radare2 debug mode. At each step in debugger, the registers visually change their values and are highlighted after:

Radare2 Visual debug mode, how it looks like
The most comfortable Visual mode for debugging

argc contains the number of arguments that were passed to a program. argc value is placed in var_4h variable (see the image below). Then it is compared with 2. This means that the program must contain two arguments – the name by which it was called and password:

cmp dword [var_4h], 2
Checks the number of arguments passed by user

To step on the next instruction press ‘s‘ key being in Visual mode. Make steps until we reach the instruction as on the image above.

Now we can look inside argv and see all the pointers it contains. By using [] we automatically follow the address contained in rbp-0x10: rbp-0x10 content -> 8-byte reference -> content:

pxr @ [rbp-0x10]    #  rbp-0x10 is var_10h value. Note, usage of '0x' is really necessary!
argv and envp content
Arguments passed by user and environment variables, argv and envp are separated by a null pointer

So, argc stores the number of arguments passed exactly by the user, and not by the environment. After user arguments a null pointer follows, and then the environment variables.

Continue to step the instructions, there will be a jump. Scroll down until we see this picture:

Step on compare_pwd function by using dcc command
dcc command allows to execute all instructions right until some function will be called

Here the second argument of argv is moved (the address to our password string) to rdi register in order to pass it to compare_pwd function (this function checks our password).

There is a way not to walk all instructions and move right before function call, use dcc command:

dc? 		# Execution continuation commands
dcc 		# Continue until call

Then make a step and we are in password verifying function:

compare_pwd function content
compare_pwd function content, we can already see the password hint

Radare2 already shows us a string with a password and its address. A password passed by us is compares with this string, and if they are equivalent, the program says that the password has been entered correctly.

my_password_to_easy is a password
Address to a correct password string is 0x4006d8, this string contains “my_password_to_easy”

Strings that are used with C functions must be \x00-terminated, so that C functions know where the string ends. Use password string address and psz command:

psz @ 0x4006d8     # Print zero-terminated string. 
...
my_password_to_easy

OK, what if we want to give the program the correct password right now, without leaving radare2?

So, we are right on the following instruction, right before string comparison function:

my_password_to_easy is a password
We are before the string comparison function will be called

First, let’s allocate some memory in the program itself. This memory will contain the correct password.

dm?               # Memory maps commands
dm -1 2048    # -1 means to allocate memory anywhere, 2048 is bytes value, so it allocates 2 kilobytes of memory.
...
ra0=0x7fcbeac10000   # This is the address for memory block we allocated.

Write the correct password in the allocated memory. Use wz (write zero terminated string) command:

wz my_password_to_easy @ 0x7fcbeac10000       # wz - write zero-terminated string
px @ 0x7fcbeac10000                                             # Check it
...
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x7fcbeac10000  6d79 5f70 6173 7377 6f72 645f 746f 5f65  my_password_to_e
0x7fcbeac10010  6173 7900 0000 0000 0000 0000 0000 0000  asy.............

Then, we need to change the string pointer in rsi register: replace our program argument (password string) with the new allocated memory address (which contains zero-terminated string with correct password):

dr?                                                          # Register commands
dr rsi=0x00007fcbeac10000
...
0x7ffd582c6727 -> 0x7fcbeac10000     # Register value has been changed. Now it points to the string with the correct password.

Step on strcmp function call, but don’t go inside this function! C functions are very long and take a long time.

Use dso command instead (Step over). This will execute the function and we will step over it:

ds?          # Step commands
dso          # Step over

Make a few steps. As we can see, the program wants to show a message about successful password check:

Password is correct string
Before “password OK” message. Well done

Of course, we could just call a program with a password and make sure that it works:

./r1 my_password_to_easy

Or simply show all strings in .data section:

iz
...
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x000006d8 0x004006d8  19  20 (.rodata) ascii my_password_to_easy
001 0x000006ec 0x004006ec  11  12 (.rodata) ascii password OK
002 0x000006f8 0x004006f8  21  22 (.rodata) ascii password "%s" not OK\n
003 0x00000710 0x00400710  81  82 (.rodata) ascii Usage : %s password\nFor this first exercice the password is stored in plaintext.\n

This is a simple program, with unencrypted passwords.

Leave a Reply

Your email address will not be published.