Reversing Checklist
A compilation of resources and approaches I’ve taken to search and understand features of a given system. I don’t claim to be any sort of authority in this matter, and there’s probably more efficient alternatives out there. Take these as starting points to develop your own approaches.
Methodology
We focus on making state explicit: annotating disassemblies, adding breakpoints and watches in a debugger, adding instrumentation for printfs, diffing trace logs…
Some domain-specific differences are worth pointing out:
- Code patterns (e.g. main loop, frame cap, memory mapped I/O…);
- Data formats (e.g. 2D tiles with indexed palettes, 3D meshes…);
- Dynamic analysis via pure CPU emulation may not give the expected results due to missing logic happening outside the game’s main loop (e.g. VBlank routine called via interrupt);
But before applying any approaches, let’s avoid guessing: ask “What is already known about what I will reverse?”. For example, these are some questions I would make for the Playstation 1 console:
- General: Any public documentation? Or development guides? Or security CTF writeups?
- Architecture: What CPU? What GPU?
- File formats: These games come in CD-ROMs, which use a well-known filesystem (ISO9660), so we can expect executables and data to be split in files. Any loader for executables? How are these data files open and read?
- Graphics: Are there well-known texture and palette formats? Extraction tools? How would I reverse some 3D model format?
- SDKs / toolchains / libraries: Are there signatures made for labelling functions in the disassembly?
- Source code: Any games with leaked source code? Any examples, either official or made by homebrew communities? Any hardware validation tests?
Hardware architectures
- pc
- msdos, windows: Intel 8086 (32 bits a.k.a. x86, or 64 bits a.k.a. amd64)
- macos 7..9: Motorola 68000
- amiga: Motorola 68000
- consoles
- sms: Zilog Z80
- gb, gbc: Sharp SM83 (~= Zilog Z80, Intel 8080)
- gba: Sharp SM83 + ARM7TDMI, nds: NTR (== ARM7TDMI + ARM946E-S)
- mini: Minx (~= Seiko S1C88)
- nes: Ricoh 2A03 (~= MOS Technology 6502)
- snes: Ricoh 5A22 (~= Western 65C816)
- tg16: Hudson Soft HuC6280 (~= MOS Technology 6502, WDC 65C02)
- 3do: ARM60
- cdi: Philips SCC68070 (~= Motorola 68000, e.g. trap instructions are different)
- ngp: Toshiba TLCS900H
- neo geo: Motorola 68000
- md, mcd: Motorola 68000
- 32x: Hitachi SH7604 (a.k.a. SH-2)
- saturn: Hitachi SH7604 (a.k.a. SH-2)
- dc: Hitachi SH7750 (a.k.a. SH-4)
- n64: NEC VR4300 (== MIPS R4300i)
- psx: CoreWare CW33300 (== MIPS R3051)
- ps2: Emotion Engine (== MIPS R5900)
- calling conventions
- endianess: LE vs. BE
- source / destination operand order
- memory map
- amiga: Address space, CIA registers, Chip registers
- gbc: Memory Map - Pan Docs
- nes: CPU memory map - NESdev Wiki
- snes: SMAS Memory Map - SMW Central
- md: Sega Mega Drive/Memory map - Sega Retro
$000000 $3FFFFF Cartridge ROM/RAM $2000000-$23FFFFF $880000 $8FFFFF 32X cartridge ROM (first 512kB bank only) $900000 $9FFFFF 32X cartridge bankswitched ROM (any 512kB bank, controlled by 32X registers) $A10002 $A10003 Controller 1 data $A10004 $A10005 Controller 2 data $A10006 $A10007 Expansion port data $A10008 $A10009 Controller 1 control $A1000A $A1000B Controller 2 control $A1000C $A1000D Expansion port control $C00000 $C00001 VDP data port $C00002 $C00003 VDP data port (mirror) $C00004 $C00005 VDP control port $C00006 $C00007 VDP control port (mirror) $FF0000 $FFFFFF 68000 RAM
- saturn:
- psx: Memory map - PSXSPX Specifications
CACHE 1f800000 1f8003ff 0x400 MCTRL1 1f801000 1f801023 0x24 IO_PORTS 1f801040 1f80105f 0x20 MCTRL2 1f801060 1f801063 0x4 INT_CTRL 1f801070 1f801075 0x6 RAM 80000000 8000ffff 0x10000 CODE 80010000 801f8fff 0x1e9000 RAM 801f9000 801fffff 0x7000
- Architecture of Consoles | A Practical Analysis - Rodrigo Copetti
- System-specific resources - emudev.org
- Documents - Romhacking.net
- Documents - Zophar’s Domain
CPU debuggers
- pc
- x32dbg, x64dbg, ollydbg (32bit, 95/98 host), Open Watcom C/C++ Debugger (16bit): windows
- DOSBox Debugger: msdos
- Altirra: atari
- The Debugger: macos 7..9
- MacsBug: macos 7..9
- arcade
- consoles
- ares (trace logger): nes, snes, n64, sgg, sms, md, 32x, mcd, gb, pce, pcfx, psx, wswan, ngp, msx
- mednafen (trace logger): nes, md, pce, pcfx, psx, ss, vb, wswan
- FCEUX-DSP, Mesen: nes
- BGB (wine), binjgb (trace logger): gb
- mgba (gdb remote, trace logger): gba
- DeSmuME (gdb remote): nds, melonDS (gdb remote): nds, No$gba (wine): gba, nds
- BizHawk (mono): sgg, sms, md (genesis plus gx), 32x, mcd, nes, snes, n64, gb, psx, ss, vb, wswan
- Emulicious: gb, gbc, sms, sgg, msx
- MEKA: sg1000, sc3000, sms, sgg, colecovision
- bsnes-plus: snes
- BlastEm (gdb remote), Exodus (wine), Gens Kmod (wine, gdb remote), Gens 11 r57shell mod (wine, trace logger): md
- Redream (gdb remote via tty), Flycast dbgnet fork: dc
- mupen64plus: n64
- Dolphin: gamecube
- PCSX-Redux (gdb remote): psx
- PCSX2: ps2
- Assembly Hacking - Wiki - GameHacking.org
CPU disassemblers
- radare2
echo 0800428c | xargs -I{} rasm2 -a mips -b 32 -d {} # lw v0, 8(v0)
- MAME: unidasm or skeleton driver that loads ROM and attaches CPU
make REGENIE=1 TOOLS=1 echo 0800428c | xxd -r -p > /tmp/1 && ./unidasm /tmp/1 -arch mips3le # 0: 8c420008 lw $v0,$8($v0) # rebuild subset of drivers make SUBTARGET=foo SOURCES=src/mame/drivers/foo.cpp
- The Ultimate Disassembly Framework – Capstone – The Ultimate Disassembler
CPU emulators
- multiarch
- tiny
Debugging the emulator
One of the challenges is in figuring out how the emulated system’s memory maps are allocated in the emulator’s process memory maps, for which a byte signature can be used for translating addresses.
Patching the emulator
- Cracking Crash Bandicoot’s password algorithm
- (FR) Write-up FCSC 2021 : Stars:2ndMix CryptoEdition - redoste
Binary patching
- Hack64 - Web Patcher
- High level mods/patches for console video-games
- GameGenie
- GameShark
- gba
- GBATEK GBA Cheat Codes - Gameshark/Action Replay V1/V2
0aaaaaaa 000000xx [aaaaaaa]=xx 1aaaaaaa 0000xxxx [aaaaaaa]=xxxx 2aaaaaaa xxxxxxxx [aaaaaaa]=xxxxxxxx 3000cccc xxxxxxxx write xxxxxxxx to (cccc-1) addresses (list in next codes) aaaaaaaa aaaaaaaa parameter for above code, containing two addresses each aaaaaaaa 00000000 last parameter for above, zero-padded if only one address 60aaaaaa y000xxxx [8000000h+aaaaaa*2]=xxxx (ROM Patch) 8a1aaaaa 000000xx IF GS_Button_Down THEN [a0aaaaa]=xx 8a2aaaaa 0000xxxx IF GS_Button_Down THEN [a0aaaaa]=xxxx 80F00000 0000xxxx IF GS_Button_Down THEN slowdown xxxx * ? cycles per hook Daaaaaaa 0000xxxx IF [aaaaaaa]=xxxx THEN (next code) E0zzxxxx 0aaaaaaa IF [aaaaaaa]=xxxx THEN (next 'zz' codes) Faaaaaaa 00000x0y Enable Code - Hook Routine xxxxxxxx 001DC0DE Enable Code - Game Code ID (value at [0ACh] in cartridge) DEADFACE 0000xxyy Change Encryption Seeds
- GBATEK GBA Cheat Codes - Gameshark/Action Replay V1/V2
- psx
- Cheat Devices - ActionReplay/GameShark Code Format - PlayStation Development Network
1-byte rewrite: 30xxxxxx yy 2-byte rewrite: 80xxxxxx yyyy if [xxxxxx] == yyyy, then execute next code: D0xxxxxx yyyy if [xxxxxx] != yyyy, then execute next code: D1xxxxxx yyyy if [xxxxxx] < yyyy, then execute next code: D2xxxxxx yyyy if [xxxxxx] >= yyyy, then execute next code: D3xxxxxx yyyy
- Cheat Devices - ActionReplay/GameShark Code Format - PlayStation Development Network
- saturn
- Code Types (Sega Saturn) - Wiki - GameHacking.org
Writes YYYY to address XXXXXXX (only once upon bootup): 0XXXXXXX YYYY Writes YYYY to address XXXXXXX: 1XXXXXXX YYYY Writes YY to address XXXXXXX: 3XXXXXXX 00YY If value at XXXXXXX is equal to YYYY, then execute the next code: DXXXXXXX YYYY
- Code Types (Sega Saturn) - Wiki - GameHacking.org
- The Secrets of Professional GameShark(tm) Hacking
- GBA AR Crypt - RAM Editing - Project Pokemon Forums
- gba
ASM patterns
- code not automatically disassembled
- Ghidra > Analysis Options > Enable “Agressive Instruction Finder”
- run in tracing disassembler: e.g. Link’s Awakening disassembly progress report – part 7 | Kzone
- lookup function prologue and epilogue
- m68k:
rts
(4e 75) - arm: e.g. Godzilla Vs. Kong Vs ... Ghidra? - Ghidra Scripting, PCode Emulation, and Password Cracking | Wrongbaud’s Blog
- m68k:
- data without xrefs
- set hardware read breakpoints
- lookup addresses / offsets
- x86: relative offsets
; hex((0x37 + 2) + 0x12) = 0x4b 140001437 75 12 JNZ LAB_14000144b
- m68k: find
bra
bytes (60 00), take relative offset bytes (XX XX), add them to instruction address, check which fall into a given target address - mips: high 2 bytes loaded first, low 2 bytes loaded next
800247f4 0d 80 04 3c lui a0,0x800d 800247f8 b0 f3 84 8c lw a0,-0xc50(a0)=>DAT_800cf3b0
- sh-2: addresses stored in memory table after end of consumer function, accessed by $pc offset
; hex((0x6a + 2) + 0x20 * 4) = 0xec 06006d6a d1 20 mov.l @(PTR_PTR_DAT_06006dec,pc),r1 [...] 06006de6 00 0b rts 06006de8 6e f6 _mov.l @r15+,r14 06006dea 00 ?? 00h 06006deb 00 ?? 00h PTR_PTR_DAT_06006dec XREF[1]: FUN_06006d68:06006d6a(R) 06006dec 06 00 40 d4 addr PTR_DAT_060040d4 = 060e7106
- x86: relative offsets
- indirection:
- x86: e.g. Another kept egg out of the dungeon
- m68k: e.g. Marble Madness (Tengen, Genesis)
00003c7c 30 38 d0 34 move.w (DAT_ffffd034).w,D0w = ?? 00003c80 e5 48 lsl.w #0x2,D0w 00003c82 4e fb 00 04 jmp (0x3c88,PC,D0w*0x1) LAB_00003c86 XREF[1]: 00003c7a(j) 00003c86 4e 75 rts 00003c88 60 00 00 a2 bra.w LAB_00003d2c 00003c8c 60 00 01 1a bra.w LAB_00003da8 00003c90 60 00 01 6c bra.w LAB_00003dfe
- strings
- CaH4e3’s Stuff - gbx_bgm_test.txt
LANG=C grep -Pobari '(bgm|\x82\x61\x82\x66\x82\x6c|music|\x82\x6c\x82\x74\x82\x72\x82\x68\x82\x62|sound|\x82\x72\x82\x6e\x82\x74\x82\x6d\x82\x63|sfx|pcm).*' foo.bin LANG=C grep -Pobari '((level|stage).select)|test.mode|cheat|debug' foo.bin
- User:Andlabs/DEBUG - The Cutting Room Floor
LANG=C grep -Poba '(\x82\x63\x82\x64\x82\x61\x82\x74\x82\x66)|(\x82[\x63\x84]\x82\x85\x82\x82\x82\x95\x82\x87)|(\x83\x66\x83\x6F(\x83\x62)?\x83[\x4e\x4f])' foo.bin
- Kumon no Sukusuku Lesson 1-kara 30-made no Suuji - The Cutting Room Floor
- “サウンドテスト” = Sound Test
- Princess Maker (PlayStation 2) - The Cutting Room Floor
- “おんがく” = Music Player / Sound Test
- Sexy Parodius - Debug Tool | SUDDENデス
- “MUTEKI (むてき) ON” = invincibility
LANG=C grep -Poba '\x82\xde\x82\xc4\x82\xab' foo.bin
- “MUTEKI (むてき) ON” = invincibility
- (REVERSE ENGINEERING) Finding the correct encoding for hiragana text | GBAtemp.net - The Independent Video Game Community
[\x00-\x7f]\xff
: latin[\x00-\x7f]\x30
: hiragana
- CaH4e3’s Stuff - gbx_bgm_test.txt
- button codes
- words
- Cheats in MediEvil | Gallowmere Historia | Fandom
“DUST LUST”: Hold L2 and press DOWN, UP, SQUARE, TRIANGLE, LEFT, UP, SQUARE, TRIANGLE.
- Cheats in MediEvil | Gallowmere Historia | Fandom
- pad checks
- Found a Japanese Debug Menu (Genesis) - Forums - GameHacking.org
lui v1, $800b addiu v1, v1, $5828 ; P1 Normal Joker command = 800B5828 andi v0, v0, $0900 ; Select + Start bne v0, zero, $8002b7c4
- Marble Madness Cheats, Codes, and Secrets for Game Boy Color - GameFAQs
rom2::440e 21 04 cb LD HL,0xcb04 rom2::4411 2a LD A,(HL+)=>DAT_cb04 rom2::4412 fe 40 CP 0x40 rom2::4414 20 46 JR NZ,LAB_rom2__445c rom2::4416 2a LD A,(HL+)=>DAT_cb05 rom2::4417 fe 80 CP 0x80 rom2::4419 20 41 JR NZ,LAB_rom2__445c rom2::441b 2a LD A,(HL+)=>DAT_cb06 rom2::441c fe 20 CP 0x20 rom2::441e 20 3c JR NZ,LAB_rom2__445c rom2::4420 2a LD A,(HL+)=>DAT_cb07 rom2::4421 fe 10 CP 0x10 rom2::4423 20 37 JR NZ,LAB_rom2__445c
- Found a Japanese Debug Menu (Genesis) - Forums - GameHacking.org
- xorshift
- words
- debug displays
- format strings
grep -riwoba '[ :=]%\(0[0-9]\)\?[cdsx]'
- hex charsets
- e.g. Desert Strike: Return to the Gulf (SNES) - The Cutting Room Floor
grep -rioba '0123456789abcdef[^Gg]'
- e.g. Desert Strike: Return to the Gulf (SNES) - The Cutting Room Floor
- format strings
- offset tables
- signatures
This function, up until $8526, doesn’t depend on addressing or offsets for its opcodes and so the raw byte code that represents the first ten instructions of this subroutine can be used in combination of a “binary search” to find other ROM files that utilize the same function.
- Help:Contents/Finding Content - The Cutting Room Floor
Data formats
- psx
- Game File Format Central - XentaxWiki
- Category:Game Formats - MultimediaWiki
- ModdingWiki
- REWiki
- Chokuretsu ROM Hacking Challenges Part 1 – Cracking a Compression Algorithm! - Haroohie Translation Club Blog
- Chokuretsu ROM Hacking Challenges Part 2 – Archive Archaeology - Haroohie Translation Club Blog
Graphics
- formats
- compression
- viewers
- parsers
- font tiles loaded using order of required characters to be displayed
- md: e.g. Asterix and the Great Rescue
- dumped VRAM open in YY-CHR
- tiles displayed referenced by index of tiles loaded (starts at 0x36e in dump, 0xff036e in VRAM)
00000360: 7333 7f33 3333 8833 3333 f733 3333 0001 s3.333.333.333.. 00000370: 0002 0003 0004 0005 0003 0006 0007 0004 ................ 00000380: 0008 0009 0004 000a 000b 0004 000c 000d ................ 00000390: 000e 0004 000f 0006 0010 0011 0004 0008 ................
- dumped VRAM open in YY-CHR
- md: e.g. Asterix and the Great Rescue
- tile block mappings
- lookup addresses from VRAM patterns
locate the uncompressed VRAM data in an emulators save state in TLP, then search for those hex strings in the main rom
- hook SDL
- Adding new features to an old game with Frida, Part I | erfur’s bits and pieces
rpc.exports = { checkDisplay() { var ctx = getContext(); send({ context: ctx, fullScreenFlag: ptr(0x004ECCC6).readU8(), }) }, setDisplay(flag) { var ctx = getContext(); var setDisplay = new NativeFunction(ptr(0x401a50), "void", ["uint8", "pointer"]); var setFullscreen = new NativeFunction(ptr(0x40fc50), "void", ["pointer", "uint8"], "thiscall"); setDisplay(flag, ctx); setFullscreen(ctx, flag); } } // On python: // api = script.exports // api.set_display(int(cmd[1]))
- Adding new features to an old game with Frida, Part I | erfur’s bits and pieces
- Extracting simple models - XeNTaX
Emulator dev
- high-level tests
- GitHub - ArtemioUrbina/240pTestSuite: A homebrew software suite for video game consoles developed to help in the evaluation of upscalers, upscan converters, line doublers and of course TV processing of 240p video. The Wii and Dreamcast versions have modes for 480i and 480p evaluation as well.
- GitHub - pinobatch/240p-test-mini: Size-optimized ports of Artemio's 240p Test Suite to 8-bit consoles
- GBEmulatorShootout
- Gameboy Doctor: debug and fix your gameboy emulator | Robert Heaton
- GitHub - destoer/armwrestler-gba-fixed: armwrestler for gba with multiple load-store tests working
- SNES/CPUADC.asm at master · PeterLemon/SNES · GitHub
- z80emu/zexall.z80 at master · anotherlin/z80emu · GitHub
- cycle / timing tests
- Simple VM - CodeGuppy Playground
- How to write an emulator (CHIP-8 interpreter) | Multigesture.net
- Emulating the Chip8 system - codeslinger.co.uk
- Emulating the Sega Genesis - Part I
- POKEGB: a gameboy emulator that only plays Pokémon blue
- emudev - developing emulators
ROM dumping
- Cracking the GBA BIOS - mGBA
- Dumping the boot ROM of the Gameboy clone Game Fighter
- PokéWalker hacking - Dmitry.GR
- VMU hacks (&C-M23 emulator) - Dmitry.GR
- Furrtek.org : Dumping the NEC PC Engine YUV table
- NMK004 ROM Dumping, Part 3: The Previous | Daifukkat.su
I concluded that I should try to play out the internal ROM with one of the tables, record the outputs, then analyze the sound files to recover the internal ROM. Within a few hours I had cooked up a sample ROM that attempted to play things out from the internal ROM. I did a short test in MAME just to verify that it would make sound, and it did. From there I made a reference ROM that I could use to compare the various values. By this time I had decided on using PSG note length as my dumping table, due to its long spread and easy reconstruction method.
- MAME - emulating a handheld console, with ~20 licensed games from 2014 (videos included)
One of the biggest initial challenges was also that the AX208 has an internal boot 8KB ROM which needed to be extracted in order to even start making progress. This internal boot ROM / BIOS, like so many others, was secured against simple reading out, even with a software attack, but was vulnerable to the same kind of hole that allowed the GBA BIOS to be dumped, namely that code inside the BIOS area could read from the BIOS area, even if code executed outside of it could not. Some code modifications to the game ROM, and Peter Wilhelmsen was able to coax the internal ROM out by spamming data to the DAC audio channel we’d identified while studying the code, essentially using a morse code like protocol of long and short pulses to dump out the ROM.
Exploit dev
- Hacking TMNF: Part 1 - Fuzzing the game server | bricked.tech
- mast1c0re: Hacking the PS4 / PS5 through the PS2 Emulator - Part 1 - Escape
- Hacking the Nintendo DSi Browser | farlow.dev
- Tetsuji: Remote Code Execution on a GameBoy Colour 22 Years Later :: TheXcellerator
- Finding and exploiting hidden features of Animal Crossing’s NES emulator | jamchamb’s blog