Installation & configuration of Android Virtual Device. How to build your debugging environment. Short ARM assembly description. Debugging with GDB inside your Android device. Remote Debugging using gdbserver. Remote debugging using IDA.
- Installation & configuration of Android Virtual Device The first thing to do is to download Android SDK and NDK. We will use GDB, other binutils, and also GCC and LD cross compiling chains. The cross compilers are able to compile binaries for other architectures. In our case, we want to compile ARM binaries from x64 architecture, since we are working on Linux x86_64, and we want to compile binaries for ARM android, so we have to use them. If you are curious about how to build these cross compiling chain tools,here are the commands:
wget -c ftp://ftp.gnu.org/gnu/binutils/binutils-2.11.2.tar.gz # tar xvf binutils-2.11.2.tar.gz # cd binutils-2.11.2 # ./configure –target=arm-linux
In our case, we don’t need to do this, as NDK contains all the things we need. Let’s go back and download Android SDK and Android NDK from here:
https://developer.android.com/tools/sdk/ndk/index.html # https://developer.android.com/sdk/index.html
or we can download the pre-compiled arm-linux-gnueab- toolchain . tarball these file in /opt/ and then add its path in $PATH variable environment. After that we have to install and configure an Android Virtual Device (AVD).This is where our binaries will run. Type :
android avd
Click to New :
Click OK, then start your Virtual Device. However, we don’t need its grapical user interface, we will connect to it using shell. AVD gives as a rooted device, so we can do everything, which will be great when we debug Linux internals and keep tracking syscalls. Once you click on the start button, your Virtual Device appears like this:
In my case, I used Android 4.2 as a target and Nexus 7 as device,though there is nothing wrong with using other targets or devices.
Let’s run our device shell:
[plain] $ adb shell # id uid=0(root) gid=0(root) [/plain]
2.How to build your debugging environment
Nothing’s new here, just mentionning that we will show three ways to debug an Android binary. The first one is to put GDB inside the device and start debugging as you are on a Linux box, thus we can keep track of several things like symbols, GOT, linked libraries, etc.
The second one is by using gdbserver and opening a port in the device and forwarding it to an external port to gain access into the device using a GDB client.
The third is debugging with IDA Pro.
Debugging Android binaries without understanding ARM Assembly is worthless. We will show in the next chapter some basic stuff in ARM Assembly.
3.Short ARM description
Personally, I like ARM assembly because it’s very easy to learn and dive into its programming. We will show some basic instructions and conventions:
3.1.Registers
ARM Assembly has 16 registers. Some of them are for function arguments, others for local variables, program counter, stack pointer, and other registers.
R0 to R3 : for function arguments. Alternative names are a0-a3. R4 to R9 : for local variables. R7 : almost holds the syscall number. R10 (sl) : Stack Limit. R11 (fp) : Fame Pointer. R12 (ip) : Intra Procedure. R13 (sp) : used as Stack Pointer like RSP in x86_64. R14 (lr) : Link Register. R15 (pc) : Program Counter (like RIP in x86_64 & EIP in x86).
3.2.Branching
Branching instructions are used when the program needs some loops, procedures and functions. The behaviour of the calling function in ARM is different from x86 assembly .
Here are the basic branching instructions:
B Branch BL Branch with Link BX Branch with Exchange BLX Branch with Link and Exchange
The B (Branch) doesn’t affect LR. That means if we jump to a subroutine we don’t have any traceback for where we were. It’s like JMP instruction in x86 assembly.
The BL (Branch with Link) instruction makes a subroutine call by storing PC-4 in LR of the current place, and to return from subroutine, we simply need to restore PC from LR like: mov pc,lr.
BX and BLX instructions are used in THUMB MODE which we don’t dive into in this part.
3.3.Data Processing
As we know, ARM is a LOAD/STORE architecture it contains 4 main instructions classes:
– Arithmetic operations:
ADD op1+op2 ADC op1+op2+carry SUB op1-op2+carry-1 syntax :
adb push ~/Bureau/arm-gdb /data
So we make a small ARM binary as an example inside the device and we’ll keep track of its behaviour: [c] #include <stdio.h> #include <string.h> int main(int argc,char **argv) { char buf[16]; if(argc < 2) return -1; strcpy(buf,argv[1]); printf(“Hello : %s n”,buf); return 0; } [/c] Let’s compile it:
arm-linux-gnueabi-gcc -o s s.c -static -zexecstack -fno-stack-protector # adb shell root@generic:/ # /data/gdb -q /data/s
WARNING: generic atexit() called from legacy shared library
Reading symbols from /data/s…(no debugging symbols found)…done.
[c] (gdb) disas main Dump of assembler code for function main: 0x00008c24 <+0>: push {r7, lr} 0x00008c26 <+2>: sub sp, #24 0x00008c28 <+4>: add r7, sp, #0 0x00008c2a <+6>: str r0, [r7, #4] 0x00008c2c <+8>: str r1, [r7, #0] 0x00008c2e <+10>: ldr r3, [r7, #4] 0x00008c30 <+12>: cmp r3, #1 0x00008c32 <+14>: bgt.n 0x8c3a <main+22> 0x00008c34 <+16>: mov.w r3, #4294967295 0x00008c38 <+20>: b.n 0x8c5e <main+58> 0x00008c3a <+22>: ldr r3, [r7, #0] 0x00008c3c <+24>: add.w r3, r3, #4 0x00008c40 <+28>: ldr r3, [r3, #0] 0x00008c42 <+30>: add.w r2, r7, #8 0x00008c46 <+34>: mov r0, r2 0x00008c48 <+36>: mov r1, r3 0x00008c4a <+38>: blx 0x12e00
That’s all, we have described all the techniques. Moreover, I made this tutorial practical and as a reference for those of you who are interested.