DEV Community

Aaron Rubesh
Aaron Rubesh

Posted on

Compiling for ARM from x86-64

Interacting with low level Linux libraries can make reversing, debugging and analyzing application much simpler.

Libraries, such as ptrace, can allow us to attach to running processes and execute functions from the tracee (the process we attach to).

Since Android is basically a Linux operating system we can call some of these same libraries to trace android application processes.

For example, the following code can be compiled and ran to dump the register values of an attached process:

//main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/ptrace.h>

int main(int argc, char **argv) {
    pid_t traced_process;
    long rt;
    struct user_regs_struct regs;

    if(argc != 2) {
            printf("usage: %s PID\n", argv[0]);
        return -1;
    }

    traced_process = atoi(argv[1]);
    rt = ptrace(PTRACE_SEIZE, traced_process, NULL, NULL);

    if(rt != 0) {
        printf("Failed to attach to process. Are you root?\n");
        return -1;
    }

    ptrace(PTRACE_GETREGS, traced_process, NULL, &regs);

    printf("RBP: %llx\nRBX: %llx\nRSI: %llx\nRIP: %llx\n", regs.rbp, regs.rbx, regs.rsi, regs.rip);

    ptrace(PTRACE_DETACH, traced_process, NULL, NULL);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

If you compile this with gcc: gcc main.c -o dump-regs and then execute it on a random process running on your machine: sudo ./dump-regs $PID

We will get a dump of some of the register values of the process:

$ sudo ./dump-regs 10245                                                                             
RBP: 7f7be53dda98
RBX: 0
RSI: 0
RIP: 9
Enter fullscreen mode Exit fullscreen mode

This works perfectly. The problem is that this program is compiled to run on a x86_64 system, not ARM. So we cannot simply transfer this program to our Android device and run it.

$ file dump-regs
dump-regs: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=36122682e0cf5cac23820af5c691d3b2c2aa374b, not stripped
Enter fullscreen mode Exit fullscreen mode

Luckily, we can solve this issue with a build of GCC that supports cross compiling to ARM.

On Debian based distros, you should be able to install the packages gcc-arm-linux-gnueabi and binutils-arm-linux-gnueabi.

sudo apt install gcc-arm-linux-gnueabi and binutils-arm-linux-gnueabi
Enter fullscreen mode Exit fullscreen mode

Now we have a build of GCC that supports compiling to ARM. However, some of the included headers are different on ARM than on x86_64 Linux so we need to make a few changes to our code before we can compile it. The main difference well note here is the user_regs struct.

I'm not going to explain to difference between the registers of two different CPU architectures here but if you are curious I'd read up on ARM CPU architecture. This knowledge will come in handy when we get more into reversing some of the Android processes.

You can also look into the headers included for binaries compiled by each gcc and arm-linux-gnueabi-gcc to see some of the differences to see why our code had to change.

//main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/ptrace.h>

int main(int argc, char **argv) {
    pid_t traced_process;
    long rt;

#ifdef __arm__
    struct user_regs regs;
#endif
#ifdef __x86_64__
    struct user_regs_struct regs;
#endif

    if(argc != 2) {
        printf("usage: %s PID\n", argv[0]);
        return -1;
    }

    traced_process = atoi(argv[1]);
    rt = ptrace(PTRACE_SEIZE, traced_process, NULL, NULL);

    if(rt != 0) {
        printf("Failed to attach to process. Are you root?\n");
        return -1;
    }

    ptrace(PTRACE_GETREGS, traced_process, NULL, &regs);

#ifdef __x86_64__
    printf("RBP: %llx\nRBX: %llx\nRSI: %llx\nRIP: %llx\n", regs.rbp, regs.rbx, regs.rsi, regs.rip);
#endif

#ifdef __arm__
    for(int i = 0; i < 18; i++) {
        printf("REG[%d]: %lx\n", i, regs.uregs[i]);
    }
#endif

    ptrace(PTRACE_DETACH, traced_process, NULL, NULL);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Now, lets compile for ARM: arm-linux-gnueabi-gcc main.c -o arm-dump-regs -static

We now have an ARM binary ready for running on our Android device!

$ file arm-dump-regs                                                                                 
arm-dump-regs: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=8b02c787af1e98e974720d1e6822c7ba99dd2e59, not stripped
Enter fullscreen mode Exit fullscreen mode

And then, we can push to our Android device via ADB: adb push arm-dump-regs /data/local/tmp

[Note: Running this application requires Root on your device!]
And Run:

OnePlus3:/data/local/tmp # ./arm-dump-regs 2206
REG[0]: 888f4
REG[1]: 88914
REG[2]: 88908
REG[3]: 0
REG[4]: ffd3da73
REG[5]: 2f
REG[6]: ffd3da72
REG[7]: 10158
REG[8]: 4990c
REG[9]: 0
REG[10]: 87098
REG[11]: 88448
REG[12]: 10b80
REG[13]: 10b28
REG[14]: 10158
REG[15]: 88460
REG[16]: 10158
REG[17]: 0
O
Enter fullscreen mode Exit fullscreen mode

And you'll see the register dump!

Top comments (2)

Collapse
 
renshake7 profile image
Tony • Edited

Hi! I followed all the steps, but on the final I get the next message (see imagen bellow)
also, my phone is Motorola One hyper, and is rooted.

dev-to-uploads.s3.amazonaws.com/i/...

Collapse
 
renshake7 profile image
Tony

Finally, it runs. But it doesn't show anything (dev-to-uploads.s3.amazonaws.com/i/...)