16 KiB
Local SPIKE / Ghidra pilot
Validated on 2026-09-11 in /home/jordan/verstack. Firmware sources are under /srv/firmware/images/stern_game_code; no source files were changed or removed.
Installed tools and configuration
- Ghidra 12.1.3:
tools/ghidra_12.1.3_PUBLIC. - Temurin JDK 21.0.12.1+1:
tools/jdk-21.0.12.1+1. - Launch GUI:
./tools/bin/ghidra(requires a graphical display). - Launch headless:
./tools/bin/ghidra-headless. - SPK extraction:
tools/bin/spike-spk, built from upstream commit63c5d9a527a4431086cfa828d72fe71fb343b9f7.
Official download URLs and checked SHA-256 digests are in toolchain.lock.json. python3 scripts/install_local_tools.py installs Ghidra and Java without modifying system packages. The SPIKE executable was separately built with Cargo from the pinned source; its built digest is also recorded. Tool binaries/downloads and local config.json are ignored by source control.
config.json points at the firmware source root, local tools, and the shared archive in data/archive. It registers zip-extract, spike-probe, spike-extract, and ghidra. Start the UI with ./target/release/verstack serve and open http://127.0.0.1:8080. No service is installed or started automatically.
This execution environment reports 16 GiB RAM. The pilot uses disk workspaces, two Ghidra analysis CPUs, a 2 GiB JVM heap, and a 100 GiB workspace limit. The configuration can be raised on a larger machine. The limit is polled, not a filesystem quota.
Corpus inventory and keys to supply
The supplied directory contains 252 files totaling 304,821,524,946 bytes (about 305 GB). Its 73 SPK downloads comprise 17 direct packages (16 SPKS, one gzip wrapper) and 56 ZIPs (48 containing SquashFS-first split packages, eight containing LUKS2-first split packages). Legacy SAM/ROM inputs and supporting documents are also present. This is a header inventory, not proof of complete extraction compatibility. Full inventory.
The following eight encrypted containers need a LUKS unlocking keyfile or passphrase. Headers expose keyslot 0 and AES-XTS-plain64 with 4096-byte sectors. They do not reveal the credential, or prove that one key unlocks every title.
| Download | LUKS UUID |
|---|---|
pokemon_le-0_85_0_spike3.spk.zip |
5b22533c-7ee6-4e7c-8e9b-abb2392be418 |
pokemon_pro-0_85_0_spike3.spk.zip |
6beaec1c-b400-423e-aa75-143a01ee3772 |
star_wars_2025_le-0_97_0.spk.zip |
0d86aaf6-3d98-42e3-afdf-5f74ddafd83d |
star_wars_2025_pro-0_97_0.spk.zip |
9536b5fa-6e29-4ba9-85d2-14314fce911f |
star_wars_elg-1_10_0_spike3.spk.zip |
3a85d189-c46c-4908-9eaf-98999b276347 |
transformers_mtmte_le-0_90_0_spike3.spk.zip |
80fd253e-bb24-4d7e-8818-4656cebad5eb |
transformers_mtmte_pro-0_90_0_spike3.spk.zip |
e35e5c9e-6626-441b-8ace-81ddedaa2af8 |
walking_dead_remastered_le-0_93_0.spk.zip |
6b4aa8a3-d5d9-4d20-bbd1-96156de0c2a3 |
Provide private keyfile paths, not key values in chat. The five title families are Pokémon, Star Wars 2025, Star Wars ELG, Transformers MTMTE, and Walking Dead Remastered. Keep secret contents outside plugin settings and manifests: settings are durable public provenance. Machine-readable key requirements.
spike-probe emits the non-secret requirement identifier stern.spike3.luks. It detects these wrappers; an adapter to unlock and decode them is still pending. Supplying keys alone does not yet enable LUKS extraction. No additional credential was needed for the older SPKS pilot. Firmware signing private keys are not needed for read-only extraction. No additional encryption requirement has been established for the preserved inner image.bin format.
Real package and storage results
Input: GOT-1_37_0.spk, 801,626,655 bytes. The pilot labels it Game of Thrones / 1.37.0 / Pro / generation 1; edition and generation are supplied catalog labels, not independently verified automatic detection.
| Stage | Snapshot | Logical bytes | New chunk bytes | New packed bytes |
|---|---|---|---|---|
| Original import | a9dc4bef-4bd9-4ffd-a8aa-56d55889c07e |
801,626,655 | 797,357,229 | 735,748,176 |
| Package extraction | e246f1eb-8cbb-4281-9d3c-8b1b47fad5a3 |
801,623,588 | 12,640,199 | 4,929,676 |
Extraction produced 49 manifest entries, including 32 regular files. The core verified read-back artifact hashes before publication and restored the extracted snapshot to data/validation/got-restored. The original and extracted snapshots remain independently addressable. New packed bytes are backend per-commit metrics; they exclude some catalog/index overhead and are not total directory usage. These results show reuse between one package and its extraction, not cross-version corpus savings.
The main executable is package-0000/GOT-1_37_0/GOT/GOT/game (6,187,348 bytes), a symbol-bearing, statically linked 32-bit little-endian ARM ELF. Its SHA-256 is 637acf6d6ff171def2c6e75437534ece8ea4017959eccd616829ccd621f97828. Ghidra selected ARM:LE:32:v8; that is its analysis language, not proof of the physical CPU generation. Imported executables were not run.
The main image.bin (784,411,100 bytes) remains opaque. Package extraction does not yet deliver individually browsable sounds/images/scripts from that container. Binaries, firmware, and extracted files can be browsed/downloaded now.
Native analysis validation
A generated x86-64 ELF and the real ARM game both completed headless analysis, exported a packed program database, and reopened that database in a second headless invocation with matching input identity. Direct ARM output contains 11,549 non-external function inventory records; the reopened database reports 11,550 total functions including external functions. Analysis did not time out. Loader warnings, including unsupported thread-local symbols, remain relevant limitations; successful export is not proof of perfect code recovery.
The pilot fixed external/uninitialized function-body handling, GZF export transaction handling, local Java runtime/cache selection, and Ghidra's rejection of dot-prefixed workspace path elements. Failed jobs remain recorded and leave their parent snapshots intact. Structured bounded failure details and successful Ghidra logs are now retained.
Function matching currently covers unique exact bodies of at least 32 bytes, excluding thunks and unavailable hashes. BSim/Version Tracking and broader structural matching remain unimplemented. No names are transferred automatically.
Completed archive analysis
The core ghidra operation completed as run 289ad050-5ce1-4ddc-8024-9285c6cccbaa, publishing derived snapshot db17ab15-cfaf-40cb-8ac1-dbb19e29b497 with extraction snapshot e246f1eb-8cbb-4281-9d3c-8b1b47fad5a3 as parent. It retains five files: function facts, .gzf, reopen receipt, and both headless logs. Logical output is 28,233,847 bytes and newly packed storage is 25,546,814 bytes. A separate CLI verification passed, followed by restoration to data/exports/game-of-thrones-1.37.0/.
The program database is data/exports/game-of-thrones-1.37.0/637acf6d6ff171def2c6e75437534ece8ea4017959eccd616829ccd621f97828/program.gzf. It is also downloadable from the analysis snapshot in the WebUI. Restored files are disposable copies of the retained archive artifacts.
Related-build comparison: Game of Thrones Pro / LE 1.37.0
The second real input was GOT_LE-1_37_0.spk, imported as snapshot 8e210c4c-d2d5-4bb8-967b-9aeacf021e01, then extracted as c9adc8f4-5505-4f71-a5f2-63f863e06e80. Both publisher packages are version 1.37.0. This is an edition comparison, not an earlier/later release experiment.
| Stage | Logical bytes | New chunk bytes | New packed bytes |
|---|---|---|---|
| LE original | 801,706,834 | 7,677,126 | 2,889,302 |
| LE extraction | 801,703,734 | 3,508,037 | 1,545,623 |
Of the LE extraction's 32 regular files, 30 have artifact identities already present in the Pro extraction, totaling 795,436,059 bytes. The two commits introduced 4,434,925 packed bytes, excluding Ghidra output and catalog/index overhead. These are incremental measurements against the populated archive; they do not predict savings on other games. Firmware source files remain untouched.
LE Ghidra analysis snapshot 19e0e553-0d2c-4d7e-a674-2ade38d87798 completed and reopened its .gzf. Its outputs were restored to data/exports/game-of-thrones-le-1.37.0/.
Comparison report format exact-functions/2 found 5,526 unique exact body matches between 11,549 Pro and 11,724 LE inventory entries. There are 6,023 unmatched baseline and 6,198 unmatched target entries. Target reasons: 3,310 below the 32-byte minimum, 48 excluded thunks, 187 ambiguous exact bodies, and 2,653 with no exact body match. These counts are not counts of developer-added functions. Relocation and compilation changes can defeat exact matching; structural matching remains future work.
The comparison took approximately 2.25 seconds including opening the archive and reading inventories. It was saved as immutable derived snapshot 045e7e69-84fa-4af2-ac5a-993b8b807e70, then separately verified and restored to data/exports/got-pro-vs-le-1.37.0/function-comparison.json. Both input snapshot/artifact/run identities and release labels are retained in the report. Existing analysis outputs were not rewritten.
LAN and Pokémon SPIKE 3 test
The user approved full unauthenticated LAN access. The service listens on 0.0.0.0:8080, reachable on this host at http://172.16.0.87:8080. It is a running process, not an installed boot service.
Pokémon LE 0.85.0 original ZIP is archived as snapshot bf7e6f96-8022-4c97-be30-172078ec5455. Both LE and Pro LUKS2 headers were tested with the credential referenced by /srv/firmware/spike3_key.txt. The supplied file is a sixteen-word hexadecimal representation; raw text, stripped text, packed little/big-endian words, and concatenated hexadecimal text all failed unlocking. The same checking code correctly accepted a generated fixture's known credential and rejected a wrong one. Key contents were not logged, archived, or modified.
The local luks-check plugin records a diagnostic derived snapshot for LE. No Pokémon inner SPK, assets, executable, or Ghidra output has been extracted yet. Correct credential/derivation information is required before decryption can proceed; this result does not establish that a particular untested key derivation would fail.
Pokémon SD-card image key check
The user supplied the Pokémon LE 0.85.0 secure SD-card ZIP at the Backblaze gamecode download URL recorded in data/validation/pokemon-sd/report.json. HTTP range requests confirmed a 3,430,070,310-byte ZIP containing one 61,924,705,792-byte raw image. Streaming reads avoided materializing the whole raw image; headers were retained for reproducibility. The complete ZIP CRC/raw image was not verified.
The partition table contains a FAT boot partition, two primary LUKS2 partitions, and an extended partition with two logical LUKS2 partitions. All four encrypted partition headers were tested, at byte offsets 67,109,376; 696,254,976; 721,421,312; and 26,491,225,600. The supplied key failed raw, stripped-text, little/big-endian word, and concatenated-hex keyslot tests on each. Direct volume-key digest tests also found no match in the tested byte representations; that verification method passed a generated known-volume-key fixture.
This does not establish whether a separate key derivation or different historical key would work. The next useful input is the exact command/tool and key conversion previously used to unlock an SD image. No decrypted Pokémon content was extracted, no key contents were logged, and the user's keyfile was unchanged. The LAN service remains available on port 8080.
Correct key and successful SPIKE 3 extraction
The corrected credential reference /srv/firmware/luks.key was accepted by all eight local encrypted update-container headers (Pokémon LE/Pro, Star Wars 2025 LE/Pro, Star Wars ELG, Transformers MTMTE LE/Pro, Walking Dead Remastered). Header validation results. This is not full extraction validation for all eight titles. The same credential did not unlock any of the four tested SD-card partitions; update and SD-image results remain separate.
Pokémon LE 0.85.0 completed the HTTP pipeline: original ZIP → LUKS2/ext4 wrapper extraction → inner SPK extraction. Every published snapshot passed read-back artifact verification. The original parser rejected numeric package type 4; the separately pinned spike3-extract tool adds only that variant and retains all upstream MD5/HMAC checks. Those checks passed for all 708 package files. The previous failed run remains recorded; its input snapshot was retained intact.
| Stage | Snapshot | Logical bytes | Newly packed bytes |
|---|---|---|---|
| Original ZIP | bf7e6f96-8022-4c97-be30-172078ec5455 |
2,405,286,702 | 2,405,088,162 |
| Decrypted wrapper output | 35015e3b-f151-451a-a329-97e1ea49219e |
2,319,247,828 | 1,879,769,642 |
| SPK extraction | e632b195-3a5c-419b-9e5e-45d17ce3cdde |
2,319,134,644 | 339,687,643 |
The final snapshot includes 708 package files plus package-evidence.json: 311 PNGs, 278 .asset files, 51 .radium files, 11 TTF fonts, and other content. Proprietary asset decoding is still incomplete; native PNGs can be previewed now. The main game is a 53,037,544-byte stripped, dynamically linked AArch64 ELF. A Pokémon Ghidra analysis has not yet been run.
The live browser successfully previewed an extracted Pokémon PNG. An HTTP-downloaded PNG also matched the independently extracted file exactly. The service remains at http://172.16.0.87:8080. New plugin configurations use the corrected key reference; no key contents were logged or archived. The earlier rejection reports remain historical records of the earlier key.
The local parser source is tools/src/bdash-spike-spk-63c5d9a-type4, built with the upstream Cargo lockfile plus patches/spike-spk-type4.patch. Source commit, patch digest, and executable digest are recorded in docs/toolchain.lock.json; the original parser binary remains available. New wrapper dependencies are libcryptsetup, Python cryptography, and read-only debugfs, without root or kernel mounts.
SD boot credential format resolved (2026-09-13)
The Pokémon LE 0.81.0 boot partition contains a nested FAT BOOT.IMG and
rootfs.cpio.zst. Its /init retrieves eight OTP words using mailbox tag
0x00030021, extracts characters 77–164 from the mailbox response, and converts
that hex text to the LUKS credential. The supplied spike3_key.txt contains the
complete 16-word mailbox response, including seven leading protocol words and
the terminator. Earlier tests packed that envelope along with the key.
The vcmailbox encoding validates the envelope and selects only its eight key
words. This unlocks all four encrypted partitions in the retained 0.81.0 image
(partitions 2, 3, 5, and 6); the local receipt is
data/validation/pokemon-081-boot-key-check.json. No key contents are recorded.
The import adapter uses separate disk_key_file / disk_key_encoding settings
so this format can coexist with the raw update-package credential.
Full-card AES-XTS decryption is now bounded across up to 16 workers. A 256 MiB
sample from partition 5 matched the previous serial decoder byte-for-byte
(data/validation/pokemon-parallel-decrypt-check.json). Synthetic round trips
also verify 512- and 4096-byte sectors, nonzero IV offsets, and chunk boundaries.
The full SD import published extracted snapshot
830d0450-50ee-4159-a0d5-3fafb1410b74: 6,181 regular files (2,823,135,524
logical bytes), including 615 files from the games partition. The workbench
successfully displayed Pokémon's 1920×1080 game logo and 275 native images from
that partition. Receipts and a screenshot are in data/validation/ under
pokemon-081-extraction-summary.json, pokemon-081-browser-check.json, and
pokemon-081-import-fixed.png.
Media snapshot 2a5c3f6e-485f-4efb-ba2a-250c2268bf9f contains 2,505 WAVs,
4,392 images, and 262 videos. Browser playback of the main game's first sound
passed (pokemon-081-playback.json); the extracted snapshot also passed the
archive's full artifact verification (pokemon-081-archive-verify.json). Video
preview generation and the requested code analysis continue in the background.