What is Reverse Engineering
Reverse Engineering (RE) is an ad hoc and creative process of extracting the knowledge of design and implementation information from anything we use in reality. Similarly, reversing a software is a practice of analysing the software to examine its internals when we don’t have the source code and utilizing the knowledge we make fun from it in a beneficial manner.
Why Reverse Engineering a Software
In today’s world, there are many reasons for reversing a software. These days the primary purposes of reversing a binary or software are to develop competing products or cracking the existing commercial software. Reversing is very popular when it comes to crackers that defeat various copy protection mechanisms of the software available. Software Reverse Engineering comes in various forms; a malware analyst needs to perform RE to analyse things like malicious software, a Worm or a Trojan which are wide spread. A cryptanalyst will choose reverse engineering to learn cryptographic algorithms and subsequently attempt different ways to crack the encryption/decryption algorithms. In addition, tech companies, that develop million dollar products, will employ a reverse engineer to audit the software to comply with digital rights management (DRM). In all the above cases, we really don’t need the source code of the software. In fact, if we have the source code we don’t even need to conduct RE.
What is Radare2
radare2 is an open source reverse engineering framework used for static and dynamic analysis, digital forensics or software exploitation supporting multiple platforms, architectures and binary formats. Radare2 is shipped with a few other important tools for file metadata extraction, base conversion, unified binary diffing and many others. This program, is not only a great disassembler but also a good debugger, especially when you love working on CLI (command-line interface). Mastering radare2 is not simple since one must remember a multitude of commands in order to use it. But again, it has intuitive command-line control to write, as compared to writing code using the IDA bindings. If you are familiar with IDA Pro or Hopper and you are a CTF player then you should try radare2.
At Loginsoft, we believe that radare2 is an essential tool for reverse engineering any software. In this series of articles, we are going to walk you through the basics of software reverse engineering using radare2 by analysing the crackme challenge (crackme0x04) which can be downloaded here.
Installing radare2 on Linux
We can install radare2 either by cloning from Github repository or from the package managers like (apt install, yum install, brew install). We recommend cloning GitHub repo as following
#cloning from Github repository git clone https://github.com/radare/radare2 #change the current the working directory cd radare2 # run install.sh sudo sys/install.sh
Radare2 can be installed on Windows platform just by downloading the executable (https://www.radare.org/r/)
Set of utilities comes with radare2
1. rax2 – A base converter utility
rax2 -s 0x61646164 returns ‘adad’
2. rabin2 – Binary program info extractor
rabin2 -e $binary: Show the entrypoints
rabin2 -i $binary: Returns information about the binary
3. radiff2 — unified binary diffing utility
radiff2 $original_binary $modified_binary
4. radare2 (r2) – Main binary for reversing the software
r2 -w $binary – Running the binary in writable format
Cracking the Challenge crackme0x04
Once we have the binary downloaded, we need to learn the behaviour by dynamic analysis; run it with different set of inputs in order to do this.
Now, it is very clear that a simple ordinary brute force technique is not going to work to capture the flag. It’s time to utilize radare2 libraries to extract the information of the binary ‘crackme0x04’. Using rabin2, retrieve information out of a binary like strings, sections, entry points.
This tells us that this program will run on Linux and it was coded in the C programming language. We can also see the bin-type being “elf”. And there are few strings used in the binary. Let’s roll up radare2 (r2), the main binary.
‘r2’ command takes path of the binary as an argument along with few useful switches (‘-w’,’-d’) which will be covered in the next article. Following the syntax ‘r2 ./crackme0x04’ will bring up the binary on neat and intuitive radare2 CLI.
One of the most important commands in radare2 is ‘?’. It not only lists all the commands available but also works with almost every subcommand. For any binary which we want to dissect, ir can be analysed by the command ‘aaa’. In our case, to find the list of functions in the binary and the address of entry point and main function, we use the commands
- ‘afll’ – List the available functions
- ‘ie’ – Returns the address of the entrypoint
- ‘iM’ – Returns the address of the main function
By observing the functions listed above, we can see several functions ‘printf’, ‘scanf’, ‘main’, ‘check’ being used in the binary. On performing dynamic analysis initially, the function ‘sym.check’ seems interesting. We can use command ‘axt $address’ to check where exactly the code was referenced. The function ‘check’ is called in the ‘main’ function at the address ‘0x8048559’. From this we can determine whether or not the validation part exists in the function. Let’s dig into the function using the command seek ‘s’. As previously mentioned, we can view a list of subcommands by using ‘?’ as a suffix to the seek command.
Now the fun part comes with disassembling the function ‘check’. Radare2 provides another command to print disassembly code of the function using ‘pdf’. ‘pdf @ main’ will analyse the main function and print its disassembly. Similarly, ‘pdf @ sym.check’ prints its disassembly of the ‘check’ function.
[0x080483d0]> pdf @ sym.check / (fcn) sym.check 133 | sym.check (int arg_8h); | ; var int local_dh @ ebp-0xd | ; var int local_ch @ ebp-0xc | ; var int local_8h @ ebp-0x8 | ; var int local_4h @ ebp-0x4 | ; arg int arg_8h @ ebp+0x8 | ; var int local_4h_2 @ esp+0x4 | ; var int local_8h_2 @ esp+0x8 | ; CALL XREF from 0x08048559 (sym.main) | 0x08048484 55 push ebp | 0x08048485 89e5 mov ebp, esp | 0x08048487 83ec28 sub esp, 0x28 ; '(' | 0x0804848a c745f8000000. mov dword [local_8h], 0 | 0x08048491 c745f4000000. mov dword [local_ch], 0 | ; JMP XREF from 0x080484f9 (sym.check) | .-> 0x08048498 8b4508 mov eax, dword [arg_8h] ; [0x8:4]=0 | | 0x0804849b 890424 mov dword [esp], eax ; const char * s | | 0x0804849e e8e1feffff call sym.imp.strlen ; size_t strlen(const char *s) | | 0x080484a3 3945f4 cmp dword [local_ch], eax ; [0x13:4]=256 | ,==< 0x080484a6 7353 jae 0x80484fb | || 0x080484a8 8b45f4 mov eax, dword [local_ch] | || 0x080484ab 034508 add eax, dword [arg_8h] | || 0x080484ae 0fb600 movzx eax, byte [eax] | || 0x080484b1 8845f3 mov byte [local_dh], al | || 0x080484b4 8d45fc lea eax, dword [local_4h] | || 0x080484b7 89442408 mov dword [local_8h_2], eax | || 0x080484bb c74424043886. mov dword [local_4h_2], 0x8048638 ; [0x8048638:4]=0x50006425 ; "%d" | || 0x080484c3 8d45f3 lea eax, dword [local_dh] | || 0x080484c6 890424 mov dword [esp], eax ; ... | || 0x080484c9 e8d6feffff call sym.imp.sscanf ; int sscanf(const char *s, | || 0x080484ce 8b55fc mov edx, dword [local_4h] | || 0x080484d1 8d45f8 lea eax, dword [local_8h] | || 0x080484d4 0110 add dword [eax], edx | || 0x080484d6 837df80f cmp dword [local_8h], 0xf ; [0xf:4]=0x3000200 | ,===< 0x080484da 7518 jne 0x80484f4 | ||| 0x080484dc c704243b8604. mov dword [esp], str.Password_OK__n ; [0x804863b:4]=0x73736150 ; "Password OK!\n" ; const char * format | ||| 0x080484e3 e8acfeffff call sym.imp.printf ; int printf(const char *format) | ||| 0x080484e8 c70424000000. mov dword [esp], 0 ; int status | ||| 0x080484ef e8c0feffff call sym.imp.exit ; void exit(int status) | `---> 0x080484f4 8d45f4 lea eax, dword [local_ch] | || 0x080484f7 ff00 inc dword [eax] | |`=< 0x080484f9 eb9d jmp 0x8048498 | `--> 0x080484fb c70424498604. mov dword [esp], str.Password_Incorrect__n ; [0x8048649:4]=0x73736150 ; "Password Incorrect!\n" ; const char * format | 0x08048502 e88dfeffff call sym.imp.printf ; int printf(const char *format) | 0x08048507 c9 leave \ 0x08048508 c3 ret [0x080483d0]>
In addition to the command ‘pdf’, radare2 has a great feature called Visual Mode which can be seen by running ‘V’ command. To view various modes we can use the command ‘p/P’ as shown below:
Observing the above disassembled code, we can see a comparison happening ‘cmp dword [local_8h], 0xf’ at the address ‘0x080484d6’ after the call ‘sscanf’. We can crack this challenge by analysing the instruction ‘cmp dword [ebp-0x8], 0xf’ (which compares the sum of input with 15 in decimal); once it matches, it prints the text “Password OK!”
Another way we can do this is by modifying the binary at address ‘0x080484da’ by overwriting with NOP (\x90) to avoid the conditional jump and to print “Password OK!” on any input that user provides. In order to do that, we must run the binary with writable mode (-w) using radare2. Here comes the final solution.
Challenges like ‘crackme’ will help us learn how to reverse software. We will look at a few other features of radare2 by cracking another challenge in the next article. We hope this guide has given you a better understanding of radare2.
Radare2 book: https://www.radare.org/get/radare.pdf
Credit: ACE Team – Loginsoft