
Breaking Godot's Encyrption
I posed a challenge to myself last weekend. Can I break Godot’s encryption system? Godot’s build system basically just packages your source files then ships that off to users in a bundle with a runner, so its fairly trivial to unpack a game if unencrypted. Confidant I could easily hack the game in an hour or to, I started down the rabbit hole. Projects like GDRE exist to make the unpacking process near instant, if its encrypted you need to do a little more work. GDRE supports setting a key so all you have to do is get the key.
I started looking for ways to extract the key, when I saw the second thing that really pissed me off.
The Godot docs were calling me a script kiddie, and if I couldn’t break this encryption I was stupid. But, hi! If you’re a script kiddie you’re in the right place. You’re going to learn how to break encryption in your favorite Godot 4.3 game, and maybe if you read these words you learn something about assembly.
Just for demo, I’m using my own blank Godot game to crack with the example key. Before we open up Ghidra we need to understand how Godot does decryption, it uses decrypt_cfb and set_encode_key and it does that here. The two functions are really just wrappers for mbedtls functions mbedtls_aes_crypt_cfb128 and mbedtls_aes_setkey_enc. So I first wrote up a demo program that just used these functions in my own C++ to verify how it worked. When doing that, I had an idea I could just cross reference each function in Godot executable with a library copy of mbedtls to find where each mbedtls function had been compiled. The instructions won’t be exact since I didn’t want to bother with compiling the Godot runners myself with symbols turned on, but there’ll be enough context to make educated guesses. My first target was mbedtls_aesni_has_support because I made a guess that the CPUID instruction didn’t appear very frequently, and mbedtls_aesni_has_support is used in mbedtls_aes_setkey_enc.
Finally I find a function that matches what I’m looking for.
And using it is a function that looks familiar…
The 0xc0, 0x100, and 0x80 constants were big indicators since 0x100 in decimal is 256 the same number that gets passed through. We’re basically done now. We now have a location where our key is going to be sent, we just need to hook into the location in GDB. The problem is the address Ghidra gives us isn’t the one that GDB lets us set a breakpoint at.
So we can instead set a breakpoint in a standard library function we know the location of. In GDB now we can see what the debugger thinks the address of the instruction is.
So we can breakpoint our target function now, and the Ghidra of the library shows us where our variables are going to be.
Then we just keep cycling the function until one of the keys matches, or you could do more work to find out the specific decryption call you need.
Format and put that key into GDRE and voila, we have our unencrypted game files.
If you’re better with Ghidra feel free to email me tips and improvements at foxmoss@mediaology.com or on discord as foxmoss_ and I’ll add them here.
Credit to GDKE who did very similar work before me.