Skip to main content

Reverse Engineering an ARM binary

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:

  • 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

Popular posts from this blog

ARM Trustzone - An overview on how SMC calls are handled by the EL3 Monitor

In this write up, we will focus mainly on the ARMv8-A exceptions, the role of ARM Trusted Firmware (that provides Secure Monitor functionality) and how the World Switch happens between Secure and Normal. If we look on the the architectural diagram of ARM Trustzone w.r.t ARMv8-A, the Execution Level is divided into four levels namely: EL0 (Secure & Non-Secure) - User Application EL1 (Secure & Non-Secure) - Kernel EL2 - Hypervisor for running different OS's simuntaneously EL3 - Security Monitor Now, whenever a normal world User Application calls for some Secure Operation, the calls goes via IOCTL call to the Linux Driver, which ultimately calls the smc instruction. To understand what the smc instruction, we have to look on the Exceptions in ARMv8 ARMv8 Exceptions In ARMv8 the exceptions are divided into two categories: Synchronous & Asynchronous.  An exception is described as synchronous if it is generated as a result of execution or attempted executi

Setting my Yocto qemu environment for reverse engineering experimental purpose

In this post I have discussed about, how I set my ARM reverse engineering platform in Yocto Qemu. Generally when we are talking about reverse engineering then we need a target platform where we could exercise our experiments. We Can choose Raspberry pi, Beagle Bone etc. for these kind of experimentation. But what we can do in these platforms, can also be performed in some virtual environments. Only for experimenting on some kind of side channel attacks, we would need the actual hardware. For making the setup, you would need some PC with very good configuration likely atleast 4 GB of RAM, 100 GB of free space, and with atleast Quad Core Processor. I have installed VMWare (non-commercial version) which is running Ubuntu 18.04. If you have ubuntu installed in your PC itself then it is well and good. I have followed this link to install Ubuntu. The following steps would help: 1. First clone the source code of Yocto. You might also require some dependencies to get it installed: $ c

An overview of ARM Memory Management Unit

The scope of this documentation is to understand the Memory Management Unit for ARMv8 Based processor. Memory management Unit converts the virtual Address (in CPU's logical space) into Physical Address. For an example let us suppose in the following program: int variable; printf("Addrss of variable = 0x%x\n", &variable); The address could be anything (Let's assume  0x40000200 ). Now 0x40000200 may or may not the actual memory address in the Physical Memory (RAM). It could be anything thing (lets assume  0xA0000200 ). Thus the CPU produce the logical address 0x40000200 which is converted into the physical address 0xA0000200 by the Memory Management Unit. Now the question remains Why we require an Address Translation, or in other word in the above program why we don't operate on actual physical memory 0xA0000200? Let us suppose a program that requires a huge amount of contagious memory in the RAM. Now our external memory would have that much memory requ