Hardware Crackmes: 100 GameBoy Advance Games

Hardware Crackmes: 100 GameBoy Advance Games

A Game Boy Advance cartridge is a frozen binary. There are no patches, no server-side validation and no telemetry. Whatever cheat codes the developer intentionally (or unintentionally) wrote in 2003 is still the exact code running today. That makes an in-game cheat code about as close to a real-world crackme as you can get: there is a validator, there is an input surface on dedicated homogenous hardware, and somewhere in the ROM is the set of inputs it accepts.

Under the hood, it’s useful to read the hardware architecture and the wonderful GBATek to understand what’s going on. At a high level: every game ROM runs on identical hardware, and every input comes through a specific subset of addressable memory. This means that if you have a game and a lot of patience, you can enumerate the full scope of every valid input. It just takes some guidance and effort, not dissimilar to how one might leverage AI agents to reverse engineer a binary for vulnerability discovery.

What makes it particularly annoying to do by hand is that GameBoy Advance ROMs are a custom format and also mix together both ARM and Thumb CPU instructions. Constantly swapping back and forth between static and dynamic analysis is necessary, and learning to play the game in such a way that you don’t taint the result is implied. At its core, a particularly annoying benchmark to meet.

So I pointed AI agents at 100 GBA ROMs and gave each one the same brief.

  1. Search the internet for the game’s known cheat codes.
  2. Find the cheat, password or debug logic in the ROM, whether intended or not.
  3. Reverse the validator.
  4. Enumerate the whole accepted space: confirm every published code, prove where the published lists are wrong, and mint codes nobody has published, then enter each one on the game’s own screen in mGBA emulator and watch it work.

The agents wrote a report per ROM: address maps, pseudocode, tables, rejection tests proving the bounds, and a section on what they couldn’t prove. Then I went back through all 100 and searched the web myself to see whether the “novel” findings really were novel.

Some held up. Some didn’t. Some probably aren’t new, but just seem that way because of the current state of search engine rot and AI slopification. Both lists are below, to at least ensure they’re written about here. Later, I will probably have the AI agents analyze every published GBA game title in existence, but for now I’m out of tokens. One agent in particular spent several billion tokens working through a Yu-Gi-Oh game and while that type of unecessary token burn is easily addressed, I truthfully just let it keep going because it was entertaining to watch. The session devolved from english into pure symbology, and yet it continued anyways.

Following the celebration of the novelties, we’ll do a quick study of the design of hardware specific cheat codes through the lens of a “crackme”.

Novelties

Each entry below was searched against GameFAQs mirrors, SuperCheats, Cheat Happens, Cyberly, CheatCC, Almar’s Guides, VGChartz, TCRF, etc… “Not found” means I couldn’t find it mentioned anywhere. It doesn’t mean it’s never been found before. That would be pretty cool to be able to say, but is also an insanely high bar to meet, especially across 100+ GBA games.

Unless a caption says otherwise, each screenshot is the game’s own UI in mGBA, typed on the game’s own keyboard, with no GameShark, CodeBreaker or memory edit involved.

Barnyard - a single literal behind a feature the manual advertises

The GBA Barnyard has an OPTIONS → Enter Code: screen, and the manual says “You are also able to enter your secret code here to unlock new characters!”. It never prints the code. The only public cheats for the GBA version are CodeBreaker memory writes. Cheat Happens lists none, and the codes on GamesRadar are for the Wii and GameCube.

The validator turns out to be one strcmp against one literal at 0x0809A46C:

0T1S

The entry widget is a four-slot wheel that skips every byte outside 0-9A-Z, so the input space is 36⁴ = 1,679,616 and exactly one value is accepted. Entering it writes a u32 flag at 0x02033D6C, shows “Congratulations! You have unlocked four new colored cows!”, and the character creator’s color list grows from 3 to 4. It’s saved to the cartridge. I couldn’t find 0T1S published anywhere.

Barnyard: 0T1S dialled into the Enter Code wheel, then the unlock dialog

Barnyard: the color picker before (3 cows) and after (4, the new one marked with two stars)

Its nearest neighbour, 0T1T, is rejected.

Army Men: Operation Green - a leetspeak table that runs before the real decoder

Operation Green has a proper algorithmic password: 8 symbols over a 32-symbol alphabet, 7 data symbols plus one symbol that is both the checksum and the XOR key. There are 3,898,605,568 valid codes, and the published list of 22 level codes is a tiny slice of that. Two of the published cheat codes are OCR errors (FFOOWP36 is really FF00WP36). Classic.

Before that decoder runs, though, the password screen checks a hard-coded 7-entry table of developer strings:

CodeEffect
CR3D1TZZcredits
H4X0RD1Za “1337” screen
T4K34W4Ya photo from the studio
D3BVGPLZ, H34LTHY*, W34P0NZZ, M3H34LTHset RAM flags only

None of these show up on any cheat site I checked, and an exact-string search for them turns up nothing game-related.

Army Men: Operation Green: CR3D1TZZ on the password grid, and the white 1337 screen from H4X0RD1Z

The T4K34W4Y “photo” is a face built out of plastic bags(?) with two clear plastic cups for eyes.

Army Men: Operation Green: the T4K34W4Y photo, a face built out of plastic bags(?) with two clear plastic cups for eyes.

Batman: Vengeance - the password table is hidden from strings

The published Batman: Vengeance list (20 story levels, 16 Advance Mode levels, plus NORA, LSMRTG and CREDITS) is complete and correct. But grep the ROM for GRAYSON, ALFRED or BATARANG and you get nothing, which makes them look fake.

They aren’t fake. The 53-record table at 0x08069614 stores every character as ASCII + K[pos], using the per-position bias 21 14 09 0B 17 45 3C 0A. Undo the bias and you get the full table, including records no list has ever printed:

CodeEffect
HOWDEYDOhidden developer-photo slideshow
QPMTRLhidden team photo
PSCLCTTfreezes the Advance Mode timer at 9:99
MRCLVLRLsets all four cheat bits at once
GLMTKVNthird ammo counter → 255
LHSBST, JQSLGSsame cheat bit, two spellings

There is also a typeable FLYMODE record that the dispatcher deliberately routes to the invalid path. Six other records contain the digit 1, which the A–Z keyboard can’t produce.

Batman: Vengeance: HOWDEYDO on the password screen, and the first developer photo in the slideshow

Batman: Vengeance: QPMTRL, and the hidden team photo composited over the game’s Gotham skyline

Antz Extreme Racing - a two-stage developer handshake

The Antz password is the most over-engineered in the set. 9 symbols (45 bits) go through a fixed invertible affine transform over GF(2) into a 48-bit buffer, then a 4-bit fold checksum, a mixed-radix payload with radices 2,5,7,12,7,50,2, a non-decreasing “opened items” list, and cross-field range checks. That gives 1,655,267,328 accepted strings.

Before the generic validator, though, there’s a little state machine:

  1. Type *CL3M3NT (8 symbols). The entry buffer silently clears and stage 1 is armed. Nothing on screen says anything happened.
  2. Then type *M4M1K0 (7 symbols). The screen fades to a developer photo.

Both literals are stored XORed with a running key (0x55+i and 0x42+i), so they don’t show up in a string dump either. The second code does nothing on its own. Published Antz lists only have the 12 level codes, and one of those, “Level 13”, actually decodes to Level 1 with 12 levels opened.

Antz Extreme Racing: the empty grid right after *CL3M3NT (stage 1 armed, no message), then the developer photo after *M4M1K0

Power Rangers: Ninja Storm - the publisher’s initials

Ninja Storm’s 3-character password packs 11 data bits and a 4-bit checksum into 15 bits. The encoder reproduces 23 of the 24 published codes. The 24th, GLW (Thunder Rangers, level 5), is a typo that’s published online today. The real code is GLG.

The submit handler then makes one extra call. 0x0804955C returns success if and only if the buffer holds symbol indices (15,5,12):

THQ

That plays the credits. The only “THQ credits” code I could find published is for the GameCube Dino Thunder, a different game from the same publisher. It looks like a house habit that went unnoticed on the GBA.

Power Rangers: Ninja Storm: THQ on the password screen, and the credits roll it starts

Back Track - two console commands nobody listed

Back Track hides a fake DOS prompt (Welcome to BSBODSOM / DOS Version 0.9Beta) behind Select, L, Right, B, L, R, Left on the pause menu. The published lists have nine commands (GOD, WEAP, AMMO, HEA, KEY, FRE, ARM, MON, COOR). The table has eleven in that context. TI turns on a hidden numeric debug readout in the HUD, and FAIL writes 0x1FFF to the level’s failsafe word. A second, mutually exclusive context (the in-level computer terminals) has PASSWORD and JV alongside the published JAG and VINCE.

Back Track: the BSBODSOM console, and the TI readout (the small number in the HUD’s top-left corner)

FAIL doesn’t change anything visible, so it isn’t pictured. The picker also caps input at 17 characters, and the screenshots show that too.

Backyard Baseball 2006 - the unlock conditions had coach-name shortcuts

Published GBA lists give two coach names (BABYBEAR for Sammy Sosa, BIGFISH for Dontrelle Willis). They list every other unlock as a gameplay task: “earn 20 skill certificates” for Abner, “complete the fielders challenge” for Barry Dejay, and so on. The 9-entry table in the ROM has a coach name for each of them:

Backyard Baseball 2006: HOOPS typed as a new coach name, then YOU HAVE UNLOCKED BARRY DEJAY

CodeUnlock
NACHOSAbner Dubbleplay
HOOPSBarry Dejay
LIBERTYJim Thome
ANCHOVYMike Piazza
SPACEMANQuantum Field
SHOWTIMEHumongous Memorial Stadium
ATLANTISAquadome

HOOPS has a known ancestor: it’s the Barry Dejay coach name in the PC Backyard Baseball 2005. The other six I couldn’t find for any version. The ROM also contains a complete Cheat Manager v1.0, gated on a held-input value of 0x3080. No controller can produce that, because bits 12–13 aren’t buttons. The 2007 sequel ships the same table minus ATLANTIS, with the player rewards swapped for 2007 rosters.

Big Mutha Truckers - a second code in a two-entry table

The one published GBA code (Up L Right Right A B Down Down R at the title screen → Evil Bob’s truck) lives in a two-record button table at 0x082812AC. The second record, Up Down Left R B A Right L, is accepted by the same validator with the same flash and sound. It sets bit 1 of the cheat bitmap at 0x02000145. Nothing in the ROM reads bit 1, so it’s a working cheat code with no cheat attached.

Big Mutha Truckers: character select after code 1 (Evil Bob’s rig), and after code 2 (the stock truck, unchanged)

Both codes get the same white-flash acknowledgement. Only one of them changes anything.

Kelly Slater’s Pro Surfer - four toggles behind a 12-press shoulder code

Every published Kelly Slater code is for the console versions’ in-game cell phone. The GBA cart has its own system:

L R L L R R L L L R R R    (main menu)

That sets bit 0 of 0x020041E4, and the Extras screen switches to an 11-item template with four new toggles: (Cheat) Beach (13/13 beaches), (Cheat) Boards (13/13 boards), (Cheat) Clips (all 13 Clip Book photos) and (Cheat) Champ (forces your heat score to a perfect 10.0). None of it is saved, so the code has to be re-entered every session.

Kelly Slater’s Pro Surfer: the four (Cheat) toggles in Extras, and Beaches/Boards unlocked 13/13 on the surfer select

Dragon Ball GT: Transformation - the unlock-all code

GT’s title menu has a 9-entry button table. Five codes unlocking one character each are on every cheat site. The other four aren’t: an unlock-all-five code, a +20,000 Zeni code, and two flag-setters whose visible effect wasn’t pinned down. The first two were confirmed in mGBA. I found character-by-character codes and CodeBreaker Zeni codes published, but no button-entry versions of either.

Both are 17 presses long (L/R are the shoulder buttons):

Unlock all five:  Down Up Left Left Down Up Left Left Down Up Left Left Up Up Down Up B
+20,000 Zeni:     Up Down B Left Right B L R B Up Left Down L R L R B

Dragon Ball GT: Transformation: the Secrets screen showing 20000 Zenie after the Zeni code, and all characters unlocked (26%) after the unlock-all code

These are two separate sessions. Neither effect survives a trip into Story Mode, which reloads the profile from SRAM.

Smaller confirmed finds

Board Game Classics. The six-digit password screen compares against exactly three strings. 192845 and 446218 are known. The ROM’s level-1 entry, 000413 (Fantasy set), isn’t on Cyberly or anywhere else I looked.

Board Game Classics: 000413 typed on the keypad, then Password Accepted

Golden Nugget Casino. The 13-character bankroll password packs a 24-bit money field. Minting the maximum, !!!!v#dbbbxb= ($16,777,215), triggers a “You’re a Multi-Millionaire — Time to retire!” ending I couldn’t find described anywhere. One caveat: the keypad cursor is so timing-sensitive that the agent placed this code in the game’s own password buffer from a savestate and submitted it through the normal OK handler. The ending is real. The keystrokes weren’t typed. The lower novel code, BPRDv#dbbbxb3 ($9,100,000), was typed normally.

Golden Nugget Casino: the max-bankroll retirement ending

Barbie as the Princess and the Pauper. The portrait password table has 46 accepted entries, and only about half are published. 6,6,6,6 (four Julians) jumps straight to the end credits.

Barbie as the Princess and the Pauper: four Julians on the password screen, and the end credits they jump to

Bomberman Tournament. TCRF documents a ROM patch that boots the TEST SELECT menu. The report goes on to enumerate all 16 entries, including FLAG SET, a 16×16 editor for the game’s 256 runtime flags at 0x02035274. This one isn’t reachable on a retail cart: it needs the one-byte patch (movs r0,#0x3C → #0x5A at 0x08000CBA).

Bomberman Tournament (patched boot): TEST SELECT, and the FLAG SET editor with the first 16 flags set

Battle B-Daman. Every list I found prints the first Zero Code as B-DAMAN. The validator rejects it. The ROM stores GO B-DAMAN (1,000 B-Dabucks). There’s no screenshot: the Zero Code screen sits behind a story-gated shop, so this result comes from calling the game’s own validator through the GDB stub, not from the retail UI.

Minor novelties

These were flagged as “unpublished” in the agent reports. A web search said otherwise:

Cheat input architectures

Once you have 100 of these side by side, they sort into a small number of designs. Each design has its own failure modes, and the gaps are where the interesting findings come from.

flowchart TD
    IN[Player input] --> T{Input surface}
    T -->|text / symbol grid| P[Password screen]
    T -->|controller edges| B[Button-sequence matcher]
    T -->|held chord| C[Chord / mask gate]
    T -->|name field| N[Name-as-code]
    P --> L[Literal table<br/>strcmp / memcmp]
    P --> O[Obfuscated table<br/>bias / XOR / custom charset]
    P --> A[Algorithmic codec<br/>state + checksum]
    P --> H[Hybrid<br/>special cases before decoder]
    B --> S1[Strict state machine]
    B --> S2[Subsequence / suffix / substring]
    C --> M1[Exact equality]
    C --> M2[AND mask]

1. Literal tables: the honest crackme

Most of the time a cheat is a fixed table compared with strcmp. A Sound of Thunder walks an 11-record {char *code; void (*apply)();} table. Ballistic: Ecks vs. Sever has two closed tables (9 cheats and 20 levels) behind one screen. Alex Rider: Stormbreaker binds each of its 10 strings to a slot, so a correct code typed under the wrong cheat name is rejected. Blades of Thunder doesn’t even store strings: it converts four digits to an integer and runs a 30-constant comparison tree.

The portrait passwords in licensed kids’ games work the same way. Atomic Betty packs four portrait choices as a base-8 number and searches a 21-entry u32 table, and three valid slots (including the screen’s default Betty×4) were missing from published lists. Barbie in the 12 Dancing Princesses has a 416-record table (13 levels × 32 variants). 232 of those records contain a symbol the 8-icon ring can’t produce, leaving 184 enterable codes, of which 13 are published.

The gap with literal tables is completeness. The developer often tested more codes than were ever announced. Running the game’s own validator over the entire input space, as was done for Blades of Thunder’s 10,000 inputs and Pegasus’s 1,296, turns “I found N codes” into “exactly N exist” and shakes out the leftovers.

2. Obfuscated tables: hiding from strings

The step up from a literal table is a literal table that doesn’t look like one. In this set:

None of this is security. It’s a speed bump against a hex editor. The main effect, twenty years on, is that it makes valid published codes look fake. An analyst who greps for ALFRED, finds nothing and calls the published code wrong has been fooled by a one-byte add.

3. Algorithmic codecs: the password is a save file

Games without battery saves serialize their state into a password, and these are the true crackmes. The game ships both an encoder (the screen that shows you your password) and a decoder, so once you’ve reversed the pair you can mint a password for any state the format can represent.

The checksums vary a lot more than “sum the characters”:

GameIntegrity check
American Idol8-bit sum of nibbles + 13
ATV: Thunder Ridge Riders10-bit sum split across two symbols
Fievel’s Gold Rush5-bit checksum, exactly one valid string per payload
Army Men: Operation Greenthe checksum symbol is the XOR key
Scooby-Doo Cyber Chasesix even-parity bits over sliding windows, per-column Caesar key
Power Rangers Time Forceper-field parity plus a parity-of-parities
Boktai 2inverted CRC-16/CCITT + LCG descrambler over a 192-bit stream
Velocity Xbase-9 → 40-round permute/rotate/toggle network → popcount checksum
Antz Extreme Racingaffine GF(2) transform + 4-bit fold
Ace Combat Advancenone, only marker bits identify each symbol’s field
Hot Wheels World Racenone, 20 bits must be zero, the other 20 are payload
SpongeBob: Revenge of the Flying Dutchmannone, validity is that three 5-bit groups are unary

The most interesting one is BattleBots: Design & Destroy, where codes are coupled. The 22-character bot password carries a 6-bit checksum that has to equal a per-class seed stored in the separate 15-character team password. Published bot codes fail on a fresh game because the lists never included the team code that goes with them. Seven of the ten published bot codes turned out to be genuine. They just needed a minted companion team password.

The gaps in algorithmic codecs are the most consistent across the whole set:

4. Hybrids: special cases before the decoder

Several games run an algorithmic codec but check a few magic strings before the decoder: Ninja Storm’s THQ, Operation Green’s leet table, the Antz handshake, Rise of Sin Tzu’s _NDTH_ and _FLYM_, Scooby-Doo’s 15 literal special codes sharing a screen with its 40-bit progress passwords. This is where developer Easter eggs live, because it’s the cheapest place to add one. The decoder can’t see these strings, so minting codes from the format will never find them. You have to read the submit handler.

5. Button-sequence matchers: the semantics are the attack surface

Button codes look trivial: compare presses against a list of masks. But the matching semantics decide which input language is actually accepted, and they differ widely:

6. Chord gates: equality vs. mask

Boot-time chords are a single comparison, and that one comparison decides how many inputs pass. Advance Wars uses exact equality against 0x214 (L + Right + Select): one state out of 1,024, plus an explicit guard that refuses the soft-reset chord. Banjo-Kazooie: Grunty’s Revenge tests (~KEYINPUT & 0x214) == 0x214, an AND mask, so every superset passes: 128 of 1,024 combinations erase your save, Select + L + Right + Up included.

7. Dead, stubbed and impossible: the developer did lock the door

The last category isn’t a way to enter cheats, it’s a way to remove them. It’s the most common finding in the set, and each one needs different proof:

What 100 crackmes teach

Most of what the agents got wrong was process, not reverse engineering. They were very good at turning a validator into pseudocode and a working Python codec. They were bad at saying “this is new/novel” with confidence, and occasionally bad at noticing that the harness, not the game, was eating input. A key repeat double-stepping cursors, a cracktro that broke the joypad reader, a single-client GDB stub silently shared by two agents, etc…

The other lesson is about published cheat lists. Across 100 games they contained OCR O/0 swaps, truncated codes, wrong difficulty labels, codes from other platforms and editions, and SEO-generated fakes. Meanwhile the ROMs, which never got a patch, still held codes that never made it into any list.

If you want ground truth for a 2003 cartridge, it’s in the ROM. If you want accuracy, you validate through process.