Arm Exploitation Series #1 — Introduction to the ARM Architecture

With the increasing growth in Internet-Of-Things (IoT) devices, it is an absolute necessity to scrutinize the security of these devices too, especially when they’re going to be right in our homes.

What better way to start, than at the very instruction set architecture (ISA) that’s most commonly found on these devices — ARM.

ARM, or what was previously known as Advanced RISC Machine is a reduced instruction set computing architecture (RISC). It’s found in the aforementioned IoT devices, along with mobile computing devices like smartphones and tablets.

The instruction set

The ARM instruction set is very unlike the traditional x86 instruction set. The first obvious difference being that ARM is RISC and x86 is CISC (Complex Instruction Set Computing)

ARM instructions operate only on registers with a few instructions for loading and saving data from / to memory while x86 can operate on directly memory as well. Up until v8 ARM was a native 32 bit architecture, favoring four byte operations over others.

With that out of the way, let’s look at a comparison table for the registers used ARM with x86. Both are for 32-bit.

ARMDescriptionX86
r0r3General purpose.
Often used to pass arguments to functions.
eax
r4r10General purpose.ebx, ecx,edx, esi, edi
r11The frame pointer (fp).ebp
r12Intra-procedural call (ip).
r13Stack pointer (sp).esp
r14Link register (lr).
r15Program counter/instruction pointer.eip
CPSRCurrent Program State Register/flags.EFLAGS

An interesting thing to note, is the lr, or the link register. Unlike in x86, where the return address is automatically pushed onto the stack by the call instruction, here, the return address is stored in the link register lr, which is then pushed onto the stack. Here’s an example of how a function is defined in ARM assembly.

1 push {lr}
2 add lr, sp, #0
3 pop {r0, pc}
4 nop
5 add sp, r11, #0
6 pop {lr}
7 bx lr

This is a fairly simple function definition. It starts with a push {lr} instruction, which pushes the return address onto the stack, so program execution can return correctly. Note that ARM 32-bit doesn’t actually have a ret, or a return instruction.

And then there’s an add instruction, which adds the number 0 to the stack pointer sp and stores that value in the lr. In x86, it would look like this: add eax, [eip + 0].

And at line 6, the lr is popped off the stack, and the function returns using a bx or a branch and exchange.

Let’s look at some common instructions that you’ll come across when reading ARM (dis)assembly.

  • pop/push — standard instructions to pop off and push to the stack
  • b/bx/bl etc. — branching instructions, used to change control flow
  • ldr/str — load and store, single data transfer
  • ldm/stm — load and store, block data transfer
  • add, sub, mul, sdiv/udiv — add, subtract, multiply and signed and unsigned division

With the basics out of the way, let’s perform the environment setup for the future installments in this series.

Environment setup

Since we’re using a non-native architecture, we will have to emulate it. Unless, of course, you have an ARM device with you, like the Raspberry Pi.

This guide assumes that the host system is GNU/Linux and preferably, a variant of Ubuntu/Debian.

For our emulator, we’ll be using QEMU. Click on the link to find your OS specific download links. Ubuntu/Debian users can do this:

$ sudo apt install qemu

Download the ARMv6 Stretch image from here. Once extracted, run the start.sh script, and wait for it to boot. You can then ssh into the VM using ssh pi@localhost -p 5022 and login with the password raspberry. This is an image based on Raspbian.

Getting familiar

If all went well, you should now have a shell on your ARM based VM! Go ahead and compile a simple C program and analyze it gdb.

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("hello");
    return 0;
}

You can do that by using gcc

$ gcc hello.c -o hello

and then

$ gdb hello

to drop into a gdb shell. From here, you can use set breakpoints, disassemble functions, single-step through instructions… etc. Learning gdb is immensely useful and I highly recommend reading up on it, if you haven’t already.

Conclusion

With this, we’re done with the first installment of the ARM Exploitation Series. In the next post, we’ll be looking at basic stack based exploits and buffer overflows. See you then.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.