https://github.com/jakzo/il2cppdecompiler
Ghidra extension for decompiling code from a Unity IL2CPP game to C#.
https://github.com/jakzo/il2cppdecompiler
Last synced: 5 months ago
JSON representation
Ghidra extension for decompiling code from a Unity IL2CPP game to C#.
- Host: GitHub
- URL: https://github.com/jakzo/il2cppdecompiler
- Owner: jakzo
- Created: 2024-01-28T05:19:21.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-02-09T16:03:04.000Z (over 1 year ago)
- Last Synced: 2024-11-14T04:44:15.872Z (7 months ago)
- Language: Java
- Size: 37.1 KB
- Stars: 32
- Watchers: 4
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Ghidra extension for decompiling code from a Unity IL2CPP game to C#.
## ➡️ Usage
> Note that it is powered by GPT4 so will require an OpenAI account to decompile functions.
1. Get an OpenAI platform account
- [Get access to the OpenAI API](https://platform.openai.com/signup)
- Generate an API key and save it somewhere safe
1. Get game outputs from Il2CppDumper
- Install [Il2CppDumper](https://github.com/Perfare/Il2CppDumper)
- Dump outputs (example command below)
```sh
./Il2CppDumper /path/to/game/GameAssembly.dll /path/to/game/resources/global-metadata.dat ./il2cpp_out
```
1. Set up Ghidra project
- Install [Ghidra](https://github.com/NationalSecurityAgency/ghidra/releases)
- 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.h` from Il2CppDumper output -> parse and wait until done
- Window -> Script manager -> add Il2CppDumper directory (the one containing `ghidra_with_structs.py`) -> run `ghidra_with_structs.py` from the list -> follow prompts and wait until done
1. 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.h` generated 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
1. 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 a `NullReferenceException` if `x` is null in `x.y`)
- Rename the exception function (`FUN_1234abcd` in the example above) to `ThrowNullReferenceException` and mark it as "No Return"
- In future I may automate this step
1. Install the Il2CppDecompiler.java script
- Download the [Il2CppDecompiler.java script](./ghidra_scripts/Il2CppDecompiler.java)
- Open the script manager in Ghidra (Window -> Script Manager)
- Click the "Manage Script Directories" button
- Add the directory containing the downloaded Il2CppDecompiler.java script
1. Run the Il2CppDecompiler.java script
- Navigate to the method you want to decompile
- Window -> Script Manager -> Il2CppDecompiler -> Follow the promptsYou 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](./test-script.sh).
## 🛠️ Development
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. 🤷
### Script
[./ghidra_scripts/Il2CppDecompiler.java](./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](./link.sh) to automatically set that up then edit the [Il2CppDecompiler.java which is linked inside the ghidra directory](./ghidra/Ghidra/Features/Decompiler/ghidra_scripts/Il2CppDecompiler.java).
#### Debugging
You can run the script during development by making sure Ghidra is closed then running [test-script.sh](./test-script.sh). Before first run, make sure you set the variables in the script in [gradle.properties](./gradle.properties) or as environment variables. You can also attach a debugger by running the script like this:
```sh
MODE=debug-suspend ./test-script.sh
```Note 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. 😄
### Extension
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.
1. Create/modify [gradle.properties](gradle.properties) to contain:
```env
GHIDRA_INSTALL_DIR=
```
1. Then build with:
```sh
gradle
```