Ghidra extension for decompiling code from a Unity IL2CPP game to C#.
Note that it is powered by GPT4 so will require an OpenAI account to decompile functions.
- Get an OpenAI platform account
- Get access to the OpenAI API
- Generate an API key and save it somewhere safe
- Get game outputs from Il2CppDumper
- Install Il2CppDumper
- Dump outputs (example command below)
./Il2CppDumper /path/to/game/GameAssembly.dll /path/to/game/resources/global-metadata.dat ./il2cpp_out
- Set up Ghidra project
- Install Ghidra
- Run
ghidraRun-> create new project -> Import game code file (GameAssembly.dll) - Open game file -> auto analyze (default options) -> wait until done (could be an hour or more)
- File -> Parse C source -> add
il2cpp.hfrom Il2CppDumper output -> parse and wait until done - Window -> Script manager -> add Il2CppDumper directory (the one containing
ghidra_with_structs.py) -> runghidra_with_structs.pyfrom the list -> follow prompts and wait until done
- Check that decompilation works correctly
- Find a function from the game in Ghidra and look through the decompiled C code to make sure it makes sense
- For my game (Boneworks) the
il2cpp.hgenerated by Il2CppDumper was missing a field in all class instances which made all field offsets wrong and caused property accesses to be off by 8 bytes
- Rename functions (usually exception throwing functions)
- The script will not run if there are unnamed functions being called (eg.
FUN_1234abcd) in the method being decompiled - Find your function in Ghidra and look at the decompiled C code
- There should be lots of
if (someVar != (SomeType)0x0) { ... } FUN_1234abcd();which are null checks (since C# will throw aNullReferenceExceptionifxis null inx.y) - Rename the exception function (
FUN_1234abcdin the example above) toThrowNullReferenceExceptionand mark it as "No Return" - In future I may automate this step
- The script will not run if there are unnamed functions being called (eg.
- Install the Il2CppDecompiler.java script
- Download the Il2CppDecompiler.java script
- Open the script manager in Ghidra (Window -> Script Manager)
- Click the "Manage Script Directories" button
- Add the directory containing the downloaded Il2CppDecompiler.java script
- Run the Il2CppDecompiler.java script
- Navigate to the method you want to decompile
- Window -> Script Manager -> Il2CppDecompiler -> Follow the prompts
You can also run it in headless mode. First make sure the project is not currently open in Ghidra then run the command from test-script.sh.
This repo contains an extension which doesn't really do anything yet. I planned to convert Ghidra's P-code (intermediate representation of machine code) to C# and build a UI for viewing/editing decompiled C# inside Ghidra (similar to the existing C decompiler) but this approach was taking too long so I pivoted to writing a script which sends the code to an LLM to do all the work for me. 🤷
./ghidra_scripts/Il2CppDecompiler.java contains the LLM-based script. It also still contains the proof-of-concept hacky string replacement approach I started with. You can edit the file as-is, but I like to clone the Ghidra source code, set that up and symlink Il2CppDecompiler.java into one of the ghidra_scripts folders there so that I get autocomplete with doc comments and can go to source of internal Ghidra functions. Run link.sh to automatically set that up then edit the Il2CppDecompiler.java which is linked inside the ghidra directory.
You can run the script during development by making sure Ghidra is closed then running test-script.sh. Before first run, make sure you set the variables in the script in gradle.properties or as environment variables. You can also attach a debugger by running the script like this:
MODE=debug-suspend ./test-script.shNote that this script is for Unix systems. You're probably using Windows so you'll have to convert it to a .bat script or something. Please open a PR if you do. 😄
Note that the extension doesn't do anything yet. I originally intended to build an interface for the decompiled C# code, like the existing interface for decompiled C.
- Create/modify gradle.properties to contain:
GHIDRA_INSTALL_DIR=<absolute_path_to_your_ghidra_installation>
- Then build with:
gradle