$ cat writeup.md…
$ cat writeup.md…
hackthebox
Task: GBA ROM expects a cheat code (button sequence) to display the flag as a bitmap image. Solution: reverse ARM Thumb code to find 8-button sum=243 validation, then decode PCX-style RLE-compressed image data at structure+0x80 offset to extract the flag.
It's late at night and your room's a mess, you stumble upon an dusty old looking box and you decide to go through it, you start unveiling hidden childhood memories and you find a mesmerising gamebody advanced flash card labeled 'Nostalgia', you pop the card in and a logo welcomes you, this strange game expects you to input a cheatcode. Can you figure it out?
Instructions: Open the rom in a GBA emulator of your choice. Select is to clear the input on the screen and start is to submit it, if the cheatcode is wrong, nothing will happen.
English summary: A Game Boy Advance ROM (Nostalgia.gba, 72812 bytes) presents a cheat code input screen. The player must enter the correct button sequence and press Start to submit. If correct, the flag is displayed on screen as a bitmap image. The goal is to reverse engineer the validation logic and extract the flag.
file Nostalgia.gba → Game Boy Advance ROM imagestrings — no "HTB", "flag", "correct", or "wrong" stringsDisassembled with radare2 (r2 -a arm -b 16 -m 0x08000000). The button input loop at 0x080015F4:
0x040001300x0200B03Ctst r0, r2 (current & ~previous)Standard GBA button masks: A=0x01, B=0x02, Select=0x04, Start=0x08, Right=0x10, Left=0x20, Up=0x40, Down=0x80, L=0x200, R=0x100.
Each button press adds a different value to a running sum stored at [fp+4]:
| Button | Mask | Value Added | Address |
|---|---|---|---|
| A | 0x01 | 3 (0x03) | 0x08001760 |
| B | 0x02 | 14 (0x0E) | 0x08001730 |
| Right | 0x10 | 58 (0x3A) | 0x0800173C |
| Left | 0x20 | 110 (0x6E) | 0x080016E4 |
| Up | 0x40 | 40 (0x28) | 0x08001772 |
| Down | 0x80 | 12 (0x0C) | 0x0800174E |
A counter at [fp] increments with each button press.
When Start is pressed, two checks at 0x08001630–0x08001666:
cmp r2, 7 at 0x08001666 — counter must be > 7 (exactly 8 presses)cmp r3, 0xf3 at 0x08001636 — sum must equal 243 (0xF3)Two valid 8-button combinations summing to 243:
The flag is NOT stored as ASCII text. The success path at 0x0800163A:
0x0404 to DISPCNT register (0x04000000) — switches to GBA Mode 4 (8bpp bitmap) with BG2 enabled[0x080017B8] = 0x02008AAC (EWRAM)0x08000DD0 with r0=structure, r1=0x06000000 (VRAM), r2=fp+80x08000354Critical insight: The render function reads image data from structure+0x80, NOT from the pointer at structure+0. The pointer field at +0 was 0x0801050A (all zeros in ROM — a red herring). The actual image data is embedded inline within the structure at offset 0x80, at ROM offset 0x8B2C (= 0x8AAC + 0x80).
The image uses PCX-style RLE compression:
byte & 0xC0 == 0xC0: run-length marker, count = byte & 0x3F, value = next byteDecoded to 240×160 pixels with two palette indices:
Using radare2, identified the accumulator-based cheat code check: 8 button presses must sum to 243.
Traced the success path to find the render function reads from structure+0x80 at ROM offset 0x8B2C.
#!/usr/bin/env python3 """Decode the RLE-compressed flag image from Nostalgia.gba""" from PIL import Image data = open("Nostalgia.gba", "rb").read() # Flag image structure at ROM offset 0x8AAC # Image data starts at structure + 0x80 = offset 0x8B2C offset = 0x8B2C chunk = data[offset:offset + 5000] # PCX-style RLE decode decoded = [] i = 0 while i < len(chunk) and len(decoded) < 240 * 160: b = chunk[i] if (b & 0xC0) == 0xC0: # Run-length marker count = b & 0x3F value = chunk[i + 1] decoded.extend([value] * count) i += 2 else: # Literal byte decoded.append(b) i += 1 # Render: palette index 225 = white (background), 46 = black (text) img = Image.new("L", (240, 160)) for y in range(160): for x in range(240): idx = y * 240 + x img.putpixel((x, y), 255 if decoded[idx] == 225 else 0) img.save("flag_screen.png") print("Flag: HTB{REDACTED}")
The rendered image clearly shows the flag text in a pixel font on a dark background.
$ cat /etc/motd
Liked this one?
Pro unlocks every complete writeup and expanded API access. $9/mo.
$ cat pricing.md$ grep --similar