CTF Write-up: Solving the “ARMs Race” Challenge — Automating Reverse Engineering with Unicorn & Pwntools
Hey everyone! In this write-up, I’ll walk you through my solution for a fun Reverse Engineering CTF challenge that required dealing with ARM architecture and, more importantly, beating a very strict timeout restriction.
The Scenario
The challenge starts with a backstory about a hacker named “Script K. Iddie” who left behind a server broadcasting mysterious data, promising his 0-days to anyone who could solve his 50-level puzzle.
Solve ( >,<)
Upon connecting to the target host via nc 154.57.164.72 31919, I was greeted with this:
Plaintext
Level 1/50: 370301e3a9120fe39a184ee3352e07e3ac2d4ce3010040e00200c0e0...
Register r0:The Trap
Before I could even process the long hex string, the server dropped the connection with a frustrating message: Timeout! You took too long to provide input.
Get APT’s stories in your inbox
Join Medium for free to get updates from this writer.
At this point, two things became clear:
- The pun in the challenge description (ARMs race) was a huge hint. The hex string is actually ARM architecture Machine Code.
- The server expects me to provide the final value stored in the
r0register after executing these instructions.
Since there are 50 levels and the connection drops in seconds, doing this manually (disassembling the hex and calculating the values line by line) was completely out of the question. Automation was the only way forward.
The Approach
Instead of writing a complex script to disassemble the instructions and mathematically guess the output, I decided to go with Emulation. The idea is simple: build a virtual ARM processor inside a Python script, throw the machine code into its memory, run it, and fetch the final value directly from the r0 register.
To do this, I relied on two powerful Python libraries:
- Pwntools: For handling the socket connection and automating the I/O.
- Unicorn Engine: A lightweight multi-architecture CPU emulator framework to execute the ARM code.
Quick tip for Kali Linux users: If you try to install these libraries using pip and get the
externally-managed-environmenterror, you can bypass it quickly in your CTF environment by using the--break-system-packagesflag:
pip install pwntools unicorn --break-system-packages
The Exploit (Solve Script)
I wrote the following script to automate the entire process. It reads the hex code, emulates it, and sends the answer back to the server in milliseconds for all 50 levels:
from pwn import *
from unicorn import *
from unicorn.arm_const import *
# Setup target connection
host = '154.57.164.72'
port = 31919
r = remote(host, port)
for i in range(50):
# Receive level prompt and hex data
r.recvuntil(b'Level ')
level_info = r.recvuntil(b': ').decode()
hex_code = r.recvline().strip().decode()
print(f"[+] Solving Level {level_info.strip(': ')}")
# Convert hex string to raw bytes
machine_code = bytes.fromhex(hex_code)
# Initialize Unicorn emulator for ARM (32-bit Little Endian)
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# Map 2MB of memory for this emulation
ADDRESS = 0x10000
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# Write the machine code to memory
mu.mem_write(ADDRESS, machine_code)
# Start emulation
try:
mu.emu_start(ADDRESS, ADDRESS + len(machine_code))
except UcError as e:
print(f"[-] Emulator Error: {e}")
# Read the final value from the r0 register
r0_value = mu.reg_read(UC_ARM_REG_R0)
print(f"[+] Register r0 = {r0_value}")
# Send the answer back to the server
r.recvuntil(b'Register r0: ')
r.sendline(str(r0_value).encode())
# Drop into interactive mode to grab the flag!
r.interactive()Running the script blasted through all 50 levels in no time, and the server finally handed over the flag.
This challenge was a great reminder of how CPU emulation can save hours of tedious manual reverse engineering.





