r/RELounge • u/DavidEstrenhido • 8h ago
Recovering a key that ships with the client, using the encrypted file table as its own oracle
Interesting little problem I ran into while looking at Godot games: the engine can encrypt the pack's file table, and the key is shipped inside the game binary, because the client needs it to decrypt at runtime.
So recovering it is a search problem with a very nice oracle. The encrypted table is [MD5 of plaintext][length][IV][AES-256-CFB ciphertext], and the plaintext always begins with the length of the first path and the path itself. That means a candidate key either produces something that looks like a resource path immediately, or obvious garbage.
The approach that worked:
- Take every 32 byte window in the binary as a key candidate.
- Decrypt only the first blocks of the table. If the structure doesn't look like [u32 length][path], drop it. This is cheap enough to do over the whole file with rayon.
- For the survivors, decrypt the entire table and check the MD5 stored in its header. That makes false positives impossible, the table itself proves the key.
The fun part is that step 3 is what makes step 2 safe to be sloppy. You can be aggressive filtering candidates because the final check is cryptographic, not heuristic.
It runs in seconds on a 759 MiB executable. PE/ELF section parsing narrows the first pass to the data sections, then falls back to the whole file.
I wrapped all of it in a small Rust CLI called gore, which also handles the extraction and calls GDRE Tools for the project recovery, since no reason to reimplement a decompiler. https://github.com/figurophobia/gore
Curious if people here have seen the same pattern in other engines: a key that has to ship with the client, protected by an encrypted blob that carries its own checksum. It feels like a general shape, not a Godot thing.






