Protecting Godot games against reverse engineering
quality 7/10 · good
0 net
Protecting Godot games against reverse engineering Godot games are known to be easy to reverse engineer. Simple tools can extract assets and source code from the packaged files. If you are making commercial games you probably want to take some steps to avoid this. Photo by Jorien Loman Godot RE Tools The most popular Godot reverse engineering software is gdsdecomp aka Godot RE Tools. It’s very multi-platform, simple to use, and even comes with a GUI. In a few clicks, you can “Recover” a project from an executable or .pck file. Gdsdecomp is able to find back the file structure of the project, every asset you exported, and the source code with full variable and function names. This is actually really well-made software. It even comes with convenient utilities for people who want to patch translations (one of the many use cases of reverse engineering games). Encrypting .pck files First let’s preface this section with a warning. There’s no way to 100% guarantee that it’s going to be impossible to decompile a game, aside from never distributing the executable. The only thing we can do is make reverse engineering more difficult and time-consuming. Anyway, the recommended way of protecting Godot games against reverse engineering is to encrypt the files inside it. This is done using AES-256, and requires compiling custom export templates. You can find the details in the official docs , but the general idea is this: Clone the Godot source code: git clone [email protected] :godotengine/godot.git Generate an AES-256 key (32 bits in hex format): openssl rand -hex 32 > godot.gdkey Put this key in your environment variables: export SCRIPT_AES256_ENCRYPTION_KEY = $( cat godot.gdkey ) Compile the new export templates (for wasm in this example): scons platform = web target = template_release && scons platform = web target = template_debug Set the new templates as custom templates in your export: In your export encryption settings, check “Encrypt Exported PCK”, “Encrypt Index”, and do not forget to put the files and folders you want to encrypt in “Filters to include”: Generate an IV (you should use a new one every export): openssl rand -hex 16 Last but not least, set your AES key and IV in the encryption settings page. If you did everything correctly, your exported .pck should not be readable by gdsdecomp without knowing the key. At the same time, players should still be able to play the game as usual without issues or needing to know what AES-256 means. Sadly, the key is stored in plain text in memory, and it is not very hard to find it there. According to an estimate I just made up, it would take a 12 years-old with an hex editor and a YouTube tutorial around 15 minutes to get the key. There’s even a tool that promises it can find it in only 50 ms . Godot-Secure To solve this problem, we need to obfuscate the decryption process a little bit. For those that feel this sounds terribly complicated, there’s a script called Godot-Secure that was built to help you with that. It will modify Godot source code to significantly alter the decryption process. Instead of directly using the key we store in memory, it will use it and a secret token to derivate a second key and decipher the files with it. In addition, to that, it will change a few magic numbers and can switch the algorithm from AES-256 to Camellia-256. Once you have run the script, you will need to recompile both the export templates and the editor. This is because the editor is responsible for encrypting the files during export. After exporting a file with the secured Godot, the attackers can still easily obtain our key from the binary files. However, this key is useless by itself. They will also need to find the secret token, work through the key derivation method and re-implement the decryption algorithm. This can take a lot of time and requires actual programming knowledge. Improving the obfuscation Of course, if you want to play around with some C++, you can make this a bit more robust by adding custom logic of your own. There are two files that will be relevant to you. First one is core/io/file_access_encrypted.cpp . It contains the encryption logic in the function FileAccessEncrypted::open_and_parse and the decryption logic in the function FileAccessEncrypted::_close . CryptoCore :: AESContext ctx ; ctx . set_encode_key ( key . ptrw (), 256 ); // Due to the nature of CFB, same key schedule is used for both encryption and decryption! ctx . decrypt_cfb ( ds , iv . ptrw (), data . ptrw (), data . ptrw ()); The second is core/io/file_access_pack.cpp . This one contains how the key is loaded from memory in the PackedSourcePCK::try_open_pack function and FileAccessPack constructor. Be careful when modifying this part, as changes with how you load the key will need to be reflected in how you set the key from the editor. Vector < uint8_t > key ; #ifdef TOOLS_ENABLED if ( ! p_decryption_key . is_empty ()) { ERR_FAIL_COND_MSG ( p_decryption_key . size () != 32 , "Decryption key must be 256-bit." ); constexpr uint8_t empty_key [ 32 ] = {}; if ( memcmp ( script_encryption_key , empty_key , sizeof ( empty_key )) == 0 ) { key = p_decryption_key ; } } else #endif { key . resize ( 32 ); memcpy ( key . ptrw (), script_encryption_key , 32 ); } The script_encryption_key variable itself is set at compile time by the script core/core_builders.py . Previous entry Making games in Go with Ebitengine