reversefreehard

Favorite Potato

b01lersc

Task: predict 20 random (A,X,Y) 6502 register inputs from their outputs after running a 5.8 MB C64 binary made of ~250k invertible macro blocks. Solution: write a minimal 6502 emulator, pattern-match the ~10 macro types (ADD/EOR/SWAP/ROR/reg-combine), invert each op and apply the chain in reverse to recover the input.

$ ls tags/ techniques/
macro_pattern_matchingoperation_inversionregister_permutationcustom_emulator

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Favorite Potato — b01lers CTF

Description

Yay, at last - I managed to upgrade my old potato. Now I can run suuuuper loooong binaries that nobody can reverse (RAM is still 64k but I only need a minimal amount).

ncat --ssl favorite-potato.opus4-7.b01le.rs 8443

Files provided:

  • favorite_potato.py — server loop. Generates 20 random (A0,X0,Y0) triples, runs run_c64("code.bin", A0, X0, Y0) on each, prints only the outputs, and asks the player to tell it back the 20 inputs. Getting all 20 right prints the flag.
  • code.bin — 5.8 MB of 6502 machine code.
  • test.bin — a 9-byte sanity-check binary used by the T)est menu option.
  • screenshot.png — a C64 BASIC screen showing the calling convention: POKE 780,A : POKE 781,X : POKE 782,Y : SYS … then PEEK(780/781/782) to read results.

Goal: invert the 5.8 MB program on 24 bits of state.

Analysis

Calling convention

The screenshot shows the standard C64 KERNAL convention — three memory cells (780/781/782) shadow the CPU registers A/X/Y. Before SYS, BASIC copies them into the 6502, then PEEK reads them back after RTS. So the entire "program I/O" is really (A,X,Y) ∈ [0,255]³ — just 24 bits. That's the crucial clue: whatever the 5.8 MB binary does, it is a map {0,1}²⁴ → {0,1}²⁴.

Why the "potato upgrade" is a joke

A real C64 has 64 K of RAM, so you physically cannot load a 5.8 MB image. The emulator used on the server clearly doesn't try — the binary is executed as one long straight-line blob. A quick opcode histogram of code.bin confirms there are no memory loads/stores at all: only register moves, stack ops, and register arithmetic. Everything lives in A, X, Y and on the hardware stack page.

...