Last month was really productive month that I had in my life after college graduation, I end up learning so many new things also did hands on session on CAN-bus forensics with one of the oldest hacker community called Garage4Hackers. Alongside that I learned a lot about ARM architecture from Azeria with her blog. Also lot’s stuff related to learning ARM assembly is available online that’s why I am not gonna write entire blog series ARM assembly instead in this article I will just walkthrough ARM shellcoding subject from an introduction point of view.
To understand working of shellcode in this blog post, first we will look into some basics of ARM and ELF structure in fast track manner. Let’s have look at diffrance between x86 and ARM registers in table given below.
|ARM vs x86 comparison table|
|R0-R5||General purpose register [Useful as function arguments][R0 often holds return value of function call]||EAX, EBX, ECX, EDX, ESI, EDI|
|R6-R10||General purpose register [R7 - Holds syscall number in linux]||-|
|Special Purpose Registers|
|R12||Intra Procedural Call||-|
|R14(LR)||Link Register [Holds return address of leaf function]||-|
|CSPR||Current Program Status Register||EFLASGS|
To write shellcode first we need to know meaning of various kinds of sections for ELF (Executable Linkable Format).
- .text – where program execution instructions are stored in architecture specific assembly language understood by target CPU
- .data – Stack, Heap, local and global variable storage
- .plt – process linkage table which calls external libraries and functions to be loaded by dynamic loader (ld.so/ld-linux.so) and addresses of those libraries and functions gets addresses gets added to .got
- .got – global offset table, it holds addresses of functions to be called during program execution (very important to know while to ret2libc/ROP)
So, shellcode today we will be making one is for forkbomb. Fork Bomb is a program which harms a system by making it run out of memory. It forks processes infinitely to fill memory. The fork bomb is a form of denial-of-service (DoS) attack against a Linux based system. Basically creating new process in infite loop.
ARM has two modes of assembly 32-bit ARM and 16-bit THUMB mode which was supported upto ARMv6 CPUs. For shellcode wirting it is preffered method to write it in THUMB instruction mode since it reduces occurrence of null bytes (0x00) which results size reduction in hex output of shellcode. Why because if we talk about making exploits for IoT devices most of them have very limited memory resources so small shellcode size will help to overcome those constrains. Making your shellcode as small as possible has been a popular choice in x86 exploit development as well.
For playing around with ARM assembly and reverse engineering you can check out my previous blog post on ARM/MIPS Qemu lab setup with complete networking support.
Enough jumping around into concepts now let’s have look at actual shell code.
.text .global _start _start: .code 32 //ARM mode ADD R3, PC, #1 //Switching to Thumb mode BX R3 .code 16 _loop: EOR R7, R7 //R7 = 0 ADD R7, #2 //Syscall to fork() SVC #1 MOV R8, R8 //NOP BL _loop
As you can see all of our insturctions goes to .text section and .global will execute instructions inside _start function label. We add 1 to program counter address and store result to R3 (ADD R3, PC, #1). To switch to THUMB mode we Branch Exchange (BX) to address store inside R3 in as result of previous instruction.
_loop is just a label where we will Branch Link (or you can say jump to it) to execute all instruction again and again. EOR is for XOR. So, R7 (+) R7 = 0, why because to avoid null byte we don’t directly move zero to R7 in output shellcode. We are resetting value of R7 to zero in start of the loop since next instruction is going to be ADD where we add 2 to R7. 2 is syscall number for fork(). You can find syscalls numbers in unistd-common.h file.
Given path in command works for ARMv7 systems such as RaspberryPi, it may vary according to 32-bit/64-bit(aarch64/ARMv8).
With SVC #1 (Supervisor Call) which calls fork() syscall and creates new process. Notice we added MOV R8, R8 (NOP - No operation) for 16-bit THUMB alignment. And in last we Branch Link to _loop to execute all same stuff.
To know more about all other instruction sets in ARM you can check out this document
Note: While writing this blog I come to my silly mistake which is using MOV instead of ADD since it makes no sense to use EOR R7, R7 before MOV instruction since it will always overwrite value to 2 and no need for reset. For this post it was my intension to point out while making shellcode you avoid null bytes at all costs wherever it’s possible.
Resource to Learn ARM assembly⌗
- Billy Ellis - YouTube
- Azeria Labs
- Assembly Language Tutorial on RaspberryPi by Derek Banas
- ARM official docs
That’s it I hope you enjoyed this article, don’t forget to share it on your favourite social media platform so more and more people can benifit from it.