Today we will take a look at how to patch a simple binary using radare2.
You can download, compile and install radare2 from https://github.com/radare/radare2
For my system setup, please check out My current hardware
The test program we will use will be a basic C program that prints a string, here is the code:
#include "stdio.h" int main(int argc, char **argv) { char *string1 = "you lose"; char *string2 = "you win"; printf(string1); return 0; }
After you compile, I recommend making a copy to work on to make it easier to recover if you mess something up.
cp re_me_1 re_me_1_patch
If we run the program it should output “you lose”
~/Workspace/C/examples » ./re_me_1 aaron@azurite you lose%
Lets open the program in radare2:
Aw tells radare2 to open the file in a writeable mode and analyze all referenced code.
r2 -Aw re_me_1_patch
If you aren’t familiar with radare2, I’d highly recommend reviewing their introductory guide here https://radare.gitbooks.io/radare2book/content/.
The first thing I like to do is look for interesting strings in the binary:
iz will show us strings parsed from the data section.
[0x00401040]> iz [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x00002010 0x00402010 8 9 (.rodata) ascii you lose 001 0x00002019 0x00402019 7 8 (.rodata) ascii you win
So when we ran the program it displayed “you lose”. We want the program to display “you win” instead so lets see where the text is referenced and figure out how it’s being used. We can search for the string a few ways:
axt will find data references to a specific address, so we can use it like this:
[0x0000201d]> axt 0x00402010 sym.main 0x401135 [DATA] mov qword [local_8h], str.first_string
You can also make radare2 use the output of one command within a new command. So instead of writing 0x00402010 we can do:
axt `iz~:2[2]`
The `iz~:2[2]` part takes the 3rd row and 3rd column from the iz command.
[0x00402010]> iz [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x00002010 0x00402010 8 9 (.rodata) ascii you lose 001 0x00002019 0x00402019 7 8 (.rodata) ascii you win [0x00402010]> axt `iz~:2[2]` sym.main 0x401135 [DATA] mov qword [format], str.you_lose [0x00402010]>
From here, we see that the string was referenced by sym.main at address 0x401135. Lets seek there and see what’s going on:
s 0x401135
print ddisassemble function will print the function at our current address
[0x00401135]> pdf ;-- main: / (fcn) sym.main 55 | sym.main (int argc, char **argv, char **envp); | ; var char **local_20h @ rbp-0x20 | ; var int local_14h @ rbp-0x14 | ; var int local_10h @ rbp-0x10 | ; var char *format @ rbp-0x8 | ; arg int argc @ rdi | ; arg char **argv @ rsi | ; DATA XREF from entry0 (0x401061) | 0x00401126 55 push rbp | 0x00401127 4889e5 mov rbp, rsp | 0x0040112a 4883ec20 sub rsp, 0x2
|0 | 0x0040112e 897dec mov dword [local_14h], edi ; argc | 0x00401131 488975e0 mov qword [local_20h], rsi ; argv | ;-- hit0_0: | 0x00401135 ~ 48c745f81020. mov qword [format], str.you_lose ; 0x402010 ; "you lose" | ;-- hit0_1: .. | 0x0040113d 48c745f01920. mov qword [local_10h], str.you_win ; 0x402019 ; "you win" | 0x00401145 488b45f8 mov rax, qword [format] | 0x00401149 4889c7 mov rdi, rax ; const char *format | 0x0040114c b800000000 mov eax, 0 | 0x00401151 e8dafeffff call sym.imp.printf ; int printf(const char *format) | 0x00401156 b800000000 mov eax, 0 | 0x0040115b c9 leave \ 0x0040115c c3 ret
So the interesting bits are:
| 0x00401135 ~ 48c745f81020. mov qword [format], str.you_lose ; | 0x0040113d 48c745f01920. mov qword [local_10h], str.you_win ; 0x402019 ; "you win"
This part shows us that the strings “you lose” and “you win” are stored in local variables.
afvd local_10h will show the actual location of the variable relative to the base pointer
[0x00401135]> afvd local_10h pxr $w @rbp-0x10
Now lets take a look at the following
| 0x00401145 488b45f8 mov rax, qword [format]
It looks like this is where the local variable is moved into the register RAX right before the call to printf. So all we should have to do is change this line to mov the local_10h variable (rbp-0x10) into rax instead. Let’s try!
V will take us into visual mode, then use p to cycle through until you get to the disassembly view.
Once you’re in disassembly view, type o and then 0x00401145 to seek to the offset of the above line.
Now we press A to bring up the assemble command. If we type the following,
mov rax, [rbp-0x10]
you will notice radare2 start to change the line automatically to
mov rax, qword [local_10h]
Once satisfied, press enter and if you type everything correctly it should ask to save your changes. Save and exit radare2.
Run the program and you should see the text “you win”
~/Workspace/C/examples » ./re_me_1_patch aaron@azurite you win%