In continuation to my previous experiment, this experiment is all about hacking into an ELF binary file. Means we will change the characteristics of and ELF file by reverse engineering its assembly instructions. For this experiment I have choose AARCH64 binary which is suppose to run in an 64 bit ARM machine. Here I have used these utilities:
ELF Header:
- readelf / aarch64-linux-gnu-readelf
- aarch64-linux-gnu-objdump - Used to dump all the assembly instructions in a binary
- xxd (which I feel one of most power free weapon of reverse engineering).
NOTE that the toolchain I have installed while building the raspberry secure images.
To understand the reverse engineering, one should atleast know the forward engineering that means the basis conditional statements (if, else) and loop statements (for / while).
I've demonstrate a program which takes input string (key), compare it with some hard-coded one and accordingly execute the access condition. This is something like an decade old software setups would execute like. It took the license key, compare it or its HASH value with some hard-coded value and accordingly install the program in the system. Eventually this process is not followed now a days. Perhaps we can dig some detail on how patches were created back those days.
So, here goes my program and its output:
#include <stdio.h>
#include <string.h>
#define MAX 30
const char pass[] = "PASSKEY";
int main()
{
char a[20] = "REVERSE ENGINEERING";
char key[MAX];
printf("HELLO WORLD\n");
printf("GIVE PASSKEY: ");
memset(key, 0, sizeof(key));
gets(key);
if (!strcmp(key, pass)) {
printf("ACCESS GRANTED\n");
} else {
printf("ACCESS DENIED\n");
}
return 0;
}
On building we get the a.out or the outfile which yeilds following output on entering wrong key:
$ ./a.out
HELLO WORLD
WRONGKEY
GIVE PASSKEY: ACCESS DENIED
Our objective is to Hack & grant the access even when the key entered is wrong. I divided the whole reverse engineering process in following steps:
1. Firstly, objdump the ELF into dissemble form , hexdump the ELF and get the ELF-Header information.
$ aarch64-linux-gnu-objdump -d a.out > outfile.dmp
$ xxd a.out > a.out.hdmp
$ readelf -h a.out
2. Next, get the entry-point address from the ELF-Header information and go thru the dissemble dump. Locate the entry-point address
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
...
Entry point address: 0x4005d0
Start of program headers: 64 (bytes into file)
Start of section headers: 11600 (bytes into file)
...
3. Next, locate the branch instruction that take all the way to ACCESS DENIED due to wrong passkey entry.
This process is quite a long and need some understanding about the assembly language. So, while viewing the disassembled output of a.out, we will get the __start function in the entry point address. The __start calls the __libc_start_main (which is part of glibc). The function does some necessary initialization of the execution environment (can read more detail here) and finally it calls main with the arguments (if passed in command line with a.out).
I have highlighted the branch instruction that lead to the respective access conditions. So, at address 0x40073c, the strcmp function is executed with the x0 and x1 register holding address of user input and hardcoded key respectively. The output will be in W0/X0 register which is then compared against 0 at address 0x400740. If values are not equal, it branch to the ACCESS DENIED condition otherwise the ACCESS GRANTED condition is executed.
4. Modify the branch instruction in the hexdump.
At this point we know the branching of instructions, we can conclude that at address 0x400740, we have to only branch the instruction to the ACCESS DENIED statement if and only if the compare result at address 0x40073c is equals zero. Which means we just need to replace the b.ne (branch if not equal) instruction at address 0x400744 with b.eq (branch if equal) instruction.
To do this job we need to know the machine code equivalent to b.eq which we can get in some ARM reference manual. But here for simplicity I searched for b.eq in the same disassembled file and found out the machine code for the same is 540000a0.
Now, I open the hexdump file (a.out.hdmp) and go to the offset (0x400744 - virtual start address) 0x744 and replace the machine code to 540000a0.
5. Reverse hexdump to get the modified form of a.out.
$ cat a.out.hdmp | xxd -r > a.out.modified
Thus on executing the binary a.out.modified, I get the following outcomes:
$ ./a.out.modified
HELLO WORLD
WRONGKEY
GIVE PASSKEY: ACCESS GRANTED
Thus I performed the experiment for my first reverse engineering. In the next series of post we will find out the difference between flat binary and an ELF.
Comments
Post a Comment