How to run HelloWorld in radare2 (installed from a snap package)

You can write instructions in a language like English, just like I do now with this blog post. If you want to write instructions for a computer, you are likely to use some sort of intermediate language that will then be converted into raw instructions suitable for the computer. Those intermediate languages are the programming languages. The software that you install on your computer is made of such raw instructions.

You can learn to make sense of these raw computer instructions, however if they are too many you get overwhelmed and you are unable to make sense of what is going on. There are cases where you want to make sense, and there are tools that help you to make sense. radare2 is one such tool.

radare2 is a reversing framework that helps you make sense of the raw computer instructions. You can also modify these instructions and make the software do things that it was intended to perform.

In the following, we are going to see

  1. How to install radare2 as a snap package.
  2. How to prepare a small HelloWorld-style example
  3. How to solve the HelloWorld example using radare2

Installing radare2 as a snap package

Run the following command

$ snap install radare2-simosx
radare2-simosx 2.6.0 from 'simosx' installed

This is an unofficial snap package of radare2. Quite soon there will be an official package and you can use that instead. This post will be updated when the official radare2 snap package is released.

You can run the program as radare2-simosx.radare2. When the official snap package is released and installed, you will be able to run it as just radare2.

Preparing a HelloWorld for radare2

Here is the source code (in the C programming language) of the HelloWorld for radare2. That’s the intermediate language mentioned in the introduction. You can make sense of it with a bit of effort.

$ cat helloworld-radare2.c 
#include <stdio.h>

int main()                            // @main function, execution always starts here.
{
  int i = 0;                          // declare an integer variable, set it to value of 0.

  if (i != 0)                         // If that variable is not 0, then
    puts("Hello, world!\n");          //   print to the screen "Hello, World!"
  else                                // Else
    puts("Go away!\n");               //   print to the screen "Go away!"

return 0;                             // Finish up.
}

We can see that in this program it will never print the Hello message because this i variable does have the value of 0. There is nothing in the code that can change the value of i. Let’s verify by compiling the source code and create the computer instructions file (the helloworld-radare2 file).

$ gcc helloworld-radare2.c -o helloworld-radare2

$ ./helloworld-radare2 
Go away!

The file helloworld-radare2 has the raw computer instructions. In the next section we are going to use only helloworld-radare2 to change the functionality of the program with radare2.

Understanding HelloWorld with radare2

We are going to run radare2 with the name of the file helloworld-radare2 as argument.

$ radare2-simosx.radare2 helloworld-radare2
[0x00400430]>

We are in radare2! If you want to quit at any time, you can type q and press Enter.

The next step is to get radare2 to auto-analyse the raw computer instructions for us. That would be the a command, and in the current version of radare2 you can get the full available analysis automation by typing up to three a’s (aaa). Please do not try with seven a‘s.

[0x00400430]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[0x00400430]>

Now that radare2 made sense of the raw computer instructions, we can ask it to show the instructions of the default main entry point that all programs have. Execution always starts atmain, and we use the mnemonic @main.

We use the command pdf @main. pdf stands for Print Disassembly of Function and then specify the function (“main”).

You can see three columns of information. The middle column is the raw computer instructions and they are pairs of hexadecimal numbers. The right column is an interpretation of the raw computer instructions (called assembly language). The first column is the incremental index of each instruction, in hexadecimal numbers. Even if you do not know much about what is shown, you can deduce the control flow between this output and the initial source code in the C programming language. Note those arrows in the screenshot which show the control flow (the if else statement in the C source code).

Let’s use a different visual view of the structure of the program. There is a VV command in radare2. Type VV @main and press Enter. Here is how it looks. To Quit back to the radare2 command line, press q twice.

The top box has instructions that does a CoMParison (cmp, cyan color), comparing with 0. Then, it Jumps if  Equal (JE, green color) to one of the two appropriate boxes below. The box on the left is for Hello, world!. The box on the right is for Go away!. The green line towards the box on the right is followed when the variable is equal with 0. Wait, in the source code the condition is whether i is equal to 0. Why was it changed to NOT being equal? The compiler that generates the instructions has the freedom to optimize them as required. Obviously, by switching the comparing, the compiler also switched the two branches. At the end the program works exactly as intended.

Both boxes have the MOVe instruction that moves the message to print into a destination named edi and then they call an IMPorted function called puts to PUT the String of characters (from destination edi) to the screen. Again, both boxes lead to the bottom box, which is the termination of the program. Note that the Hello, world box ends with a JuMP (jmp) instruction, and jumps to the bottom box. The reason is that sequentially, the Hello, world box is first and then the Go away box. Without the jmp, the Hello, world box would have followed though to the Go away box, and both messages would have been printed (and would have been wrong).

The bottom box does a LEAVE, and then a RETurn back to the system.

We understand more or less how the raw instructions are supposed to work.. In the next section we modify those raw instructions in order to make the application to do other things than from what was intended.

Modifying (patching) the HelloWorld with radare2

Let’s see again the disassembly of the main function. radare2 allows as to change anything and the easiest way to manipulate the program is to overwrite some instructions with something else. Such an instruction is No OPeration (nop), which does nothing. We could replace some instructions with nops and affect in that way the execution. Which instruction should we nop out?

We can change the je 0x400547 instruction into NOPs. If we do that, then the comparison will not happen and the execution will follow through the Hello, world box. Then, the jmp instruction will jump the execution to the end of the program, and the program will terminate naturally.

The raw instructions of je 0x400547 are 74 0c (middle column), and the address is 0x00400539 (first column). We should seek into this address location, and then overwrite those raw instructions with as many nops as required.

For radare2 to be able to modify a file, it needs to run with the -w switch (w for writing). Therefore, exit radare2 and start it again as follows:

$ radare2-simosx.radare2 -w helloworld-radare2
[0x00400430]> aaaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Emulate code to find computed references (aae)
[x] Analyze consecutive function (aat)
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Type matching analysis for all functions (afta)
[0x00400430]> pdf @main
/ (fcn) main 50
|   main ();
|           ; var int local_4h @ rbp-0x4
|           ; DATA XREF from 0x0040044d (entry0)
|           0x00400526   55              push rbp
|           0x00400527   4889e5          mov rbp, rsp
|           0x0040052a   4883ec10        sub rsp, 0x10
|           0x0040052e   c745fc000000.   mov dword [local_4h], 0
|           0x00400535   837dfc00        cmp dword [local_4h], 0
|       ,=< 0x00400539   740c            je 0x400547
|       |   0x0040053b   bfe4054000      mov edi, str.Hello__world ; 0x4005e4 ; "Hello, world!\n"
|       |   0x00400540   e8bbfeffff      call sym.imp.puts
|      ,==< 0x00400545   eb0a            jmp 0x400551
|      ||   ; CODE XREF from 0x00400539 (main)
|      |`-> 0x00400547   bff3054000      mov edi, str.Go_away ; 0x4005f3 ; "Go away!\n"
|      |    0x0040054c   e8affeffff      call sym.imp.puts
|      |    ; CODE XREF from 0x00400545 (main)
|      `--> 0x00400551   b800000000      mov eax, 0
|           0x00400556   c9              leave
\           0x00400557   c3              ret
[0x00400430]>

In the screenshot below, we Seek to the address 0x00400539 (s 0x00400539). Any write we do, will be on that location. A command to write raw instructions (also called operation codes, opcodes) is wao. There are a few ways to use wao, and the one we choose is wao nop; modify the current opcode and replace accordingly into the nop opcode (raw instruction).

Here is another glorious screenshot showing the before and after. The freshly added opcodes appear in blue. The instruction je 0x400547 is two bytes long (740c), and has been replaced by two nop (90).

Do we need to save the file now? No need, our change has already been written to the file.

Press q and then Enter to exit radare2. Finally, run the program again.

$ ./helloworld-radare2 
Hello, world!

Success, Hello, world!

What next?

You can read the radare2 book online, also available as PDF and learn more about radare2.

There is also a GUI tool for radare2 called Cutter. I have tried to create a snap package for Cutter but did not succeed yet. The reason is that currently the snaps are based on the Ubuntu 16.04 Core image, and Cutter uses some Qt libraries that have not been packaged for 16.04. It is doable to package them, but ain’t nobody have time for that. So, we will compile Cutter from source in a LXD GUI container. Finally, we will do the HelloWorld task again using Cutter.

Installing Cutter in a LXD GUI container

Visit the following post in order to setup LXD on your Linux box and prepare it for GUI LXD containers.

Then, follow these commands. First, launch a GUI container, copy helloworld-radare2 over to the container and prepare the environment to compile Cutter. The command to copy the file over to the container is lxc file push, then the filename to copy, and finally the destination cutter (name of container) /home/ubuntu/ (directory).

$ lxc launch --profile default --profile gui ubuntu:18.04 cutter
Creating cutter
Starting cutter

$ lxc file push helloworld-radare2 cutter/home/ubuntu/

$ lxc exec cutter -- sudo --user ubuntu --login
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@cutter:~$ git clone --recurse-submodules https://github.com/radareorg/cutter
Cloning into 'cutter'...
remote: Counting objects: 7296, done.
remote: Compressing objects: 100% (64/64), done.
remote: Total 7296 (delta 47), reused 60 (delta 35), pack-reused 7197
Receiving objects: 100% (7296/7296), 7.85 MiB | 2.06 MiB/s, done.
Resolving deltas: 100% (5568/5568), done.
Submodule 'radare2' (https://github.com/radare/radare2) registered for path 'radare2'
Cloning into '/home/ubuntu/cutter/radare2'...
remote: Counting objects: 163731, done. 
remote: Compressing objects: 100% (54/54), done. 
remote: Total 163731 (delta 37), reused 44 (delta 26), pack-reused 163651 
Receiving objects: 100% (163731/163731), 84.29 MiB | 1.68 MiB/s, done.
Resolving deltas: 100% (122529/122529), done.
Submodule path 'radare2': checked out '7743169a9b7343d08e5153158a65176ef44d63f0'
ubuntu@cutter:~$

Next, install the necessary development packages.

ubuntu@cutter:~$ cd cutter

ubuntu@cutter:~/cutter$ sudo apt install build-essential pkg-config python3-dev python3-pip qt5-default qtwebengine5-dev libqt5svg5-dev
ubuntu@cutter:~/cutter$ pip3 install notebook jupyter_client

Finally, start compiling cutter.

ubuntu@cutter:~/cutter$ ./build.sh 
A (new?) version of radare2 will be installed. Do you agree? [Y/n] Y
...
Build complete. Binary available at: build/Cutter
ubuntu@cutter:~/cutter$

Cutter has been compiled, and the binary is found at build/Cutter.

Time to run Cutter!

ubuntu@cutter:~/cutter$ build/Cutter

We click on the Select button to select the file helloworld-radare2.

Next is the dialog for the load options. We enable the -w flag (Load in write mode), which means that any changes we make, will be applied directly to the file. Note that if you later make a mistake when editing the file, them compile it again from scratch and put it back here in order to reopen.

We keep the Auto-Analysis level aaa (three as). In my tests, if I increase the level to aaaa (four as, Include Experimental Auto-Analysis) then Cutter would hang. Most likely an issue with Cutter because radare2 is OK with aaaa.

Click on OK to continue.

Here is the interface of Cutter. There are many things to see. For this post, we focus on two of the windows, the Functions and the Disassembly windows. The Disassembly window shows the disassembly of the file starting from the beginning, while we are only interested in the function main.

We need to select the main function from the Functions window in order to have the instructions of @main appear in the Disassembly window. @main is listed as sym.main in the Functions window.

Just double-click on sym.main in the Functions window.

The output in the Disassembly window is quite familiar. It is the same output from radare2. In fact, Cutter is a GUI interface that makes use of radare2. When we compiled Cutter, it asked as to compile radare2 as well.

We place the mouse pointer over the je 0x400547 instruction and right-click with the mouse. Then, click on Edit, then Nop Instruction. That should replace the je 0x400547 instruction with as many nop as needed.

Everything went well, and we can see the two nop instructions. We do not even need to save. The changes were written already to the file and we just need to click on File → Quit. It asks us if we want to save the project. We do not need to, so we select No and exit Cutter.

The final step is to run again the file helloworld-radare2 and see whether we Go Away or Hello, world.

ubuntu@cutter:~/cutter$ /home/ubuntu/helloworld-radare2 
Hello, world!

Hello, world! It worked.

Discussion

There are many amazing free and open-source projects. Radare2 and Cutter are just two of them. It is good to have at least some understanding on how they work and give them our support so they become even better.

Permanent link to this article: https://blog.simos.info/how-to-run-helloworld-in-radare2-installed-from-a-snap-package/

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.