ARM64 Assembly for Beginner Pentesters
Introduction
Welcome to a tutorial focused on ARM64 assembly language for budding penetration testers! This blog will walk you through an ARM64 (AArch64) program designed to check if a number is prime, and explore the key assembly instructions that are integral to understanding ARM64 assembly programming. The blog is perfect for those interested in exploring Android-like devices or any system using the ARM64 architecture, which is common in mobile devices and IoT devices. Mastering this architecture will empower pentesters to better analyze and exploit ARM64-based systems.
Introduction to ARM64 Assembly Language and its Role in Pentesting
ARM64 (also known as AArch64) is a 64-bit architecture widely used in mobile devices, servers, and embedded systems. It has become increasingly popular in Android devices, making it crucial for penetration testers to learn ARM64 assembly. Unlike x86 or x64 architectures, ARM64 requires specific tools and knowledge that help in reverse engineering and exploiting ARM64-based systems.
In this blog, we will dive into an ARM64 program that checks whether a number is prime. Not only will you learn how the code works, but you’ll also understand key assembly instructions, their functions, and why they are important in the context of pentesting and reverse engineering.
Prerequisites: Setting Up the ARM64 Environment
Before diving into the code, let’s first ensure that you have the required tools installed to compile and run ARM64 assembly code on a Linux-based system.
Install the Required Tools:
Run the following commands in your terminal to install the necessary tools for compiling and running ARM64 code:
1
2
3
sudo apt install binutils-aarch64-linux-gnu
sudo apt install qemu-user
sudo apt install qemu qemu-user-static
Compile the Code:
To compile the ARM64 assembly program, use these commands:
1
2
aarch64-linux-gnu-as -o prime.o prime.s
aarch64-linux-gnu-ld -o prime prime.o
Run the Code:
Finally, to run your program on an ARM64 emulated environment, use the following command:
1
qemu-aarch64 ./prime
ARM64 Code Walkthrough: Prime Number Check
This code will return 1
if the number is prime
else it will return 0
. Now, let’s break down the code below and go over each instruction in detail.
.global _start
.global is_prime
.type is_prime, %function
_start:
mov x0, #29 // Set x0 to 29 (testing with prime number)
bl is_prime // Branch to is_prime function
mov x8, #93 // Syscall number for exit (Linux x64)
svc #0 // Make the system call to exit with status in x0
is_prime:
cmp x0, #2 // Compare n with 2
blt not_prime // If n < 2, it's not prime (branch to not_prime)
cmp x0, #2
beq is_prime_label // If n == 2, it's prime (branch to is_prime_label)
and x1, x0, #1 // Perform bitwise AND between n and 1
cmp x1, #0 // If result is 0, the number is even
beq not_prime // If even, it's not prime (branch to not_prime)
mov x1, #3 // Set x1 to 3 (starting divisor)
check_divisibility:
cmp x1, x0 // Compare current divisor (x1) with n
bge is_prime_label // If x1 >= n, we've finished checking divisors
udiv x2, x0, x1 // x2 = n / x1 (integer division)
mul x3, x2, x1 // x3 = (n / x1) * x1 (to see if it's divisible)
cmp x3, x0 // Compare x3 with n
beq not_prime // If n is divisible by x1, it's not prime (branch to not_prime)
add x1, x1, #2 // Increment divisor by 2 (check next odd number)
b check_divisibility // Repeat loop
is_prime_label:
mov x0, #1 // Set x0 to 1 (return true: prime)
ret
not_prime:
mov x0, #0 // Set x0 to 0 (return false: not prime)
ret
Breaking Down the Key Instructions
mov
:- The
mov
instruction is used to load an immediate value into a register. For example,mov x0, #29
sets the value of registerx0
to 29, which is the number we want to check for primality. - This is a straightforward instruction to set up the initial value.
- The
bl
(Branch with Link):- The
bl
instruction is used to branch to a function or label while saving the return address in the link register (lr
). It’s essential for calling functions likeis_prime
. - For example,
bl is_prime
transfers control to theis_prime
function.
- The
b
(Branch):- The
b
instruction is a simple unconditional jump to a label. It’s used when you want to jump to another part of the code, such asb check_divisibility
to repeat the loop. - Unlike
bl
, it doesn’t save the return address in a register.
- The
cmp
(Compare):- The
cmp
instruction compares two values by subtracting them, but without storing the result. It affects the condition flags, which can then be used by subsequent branch instructions. - For example,
cmp x0, #2
compares the value in registerx0
with 2, setting flags for conditional branches likeblt
orbeq
.
- The
blt
(Branch if Less Than):- The
blt
instruction branches if the result of the previous comparison was less than zero (signed comparison). - In the code,
blt not_prime
ensures that ifx0
is less than 2, the program jumps to thenot_prime
label.
- The
beq
(Branch if Equal):- The
beq
instruction branches if the values compared by thecmp
instruction are equal (i.e., zero flag is set). - Example:
beq is_prime_label
branches if the number is exactly 2 (a prime number).
- The
bge
(Branch if Greater or Equal):- The
bge
instruction branches if the first value is greater than or equal to the second value in a signed comparison. - Here,
bge is_prime_label
checks if the divisor has reached or exceeded the number being tested. If so, it concludes that the number is prime.
- The
udiv
(Unsigned Division):- The
udiv
instruction performs unsigned integer division. In this case,udiv x2, x0, x1
calculates the quotient ofx0
divided byx1
and stores the result inx2
. - This is used to check if
n
is divisible by any divisor.
- The
mul
(Multiply):- The
mul
instruction multiplies two registers and stores the result in the destination register. For instance,mul x3, x2, x1
calculates the product ofx2
andx1
. - This helps verify if the result of division multiplied back equals the original number (i.e., checking divisibility).
- The
Conclusion: Why Understanding These Instructions Matters for Pentesters
In conclusion, mastering ARM64 assembly will enhance your reverse engineering and pentesting skills, especially in Android-like environments that utilize ARM64 processors. So, the next time you’re working with ARM64 assembly, remember these instructions, and you’ll have a deeper understanding of how ARM64-based devices work and how to leverage their weaknesses!
Happy hacking!