Update readme and minor clang-format fixup

This commit is contained in:
pancake committed 2026-05-07 18:09:47 +02:00
1 parent bc49ade2ee
commit ac757ccd8f
7 files changed
+109 -121

No files matched your search

+9 -10
View File
@@ -1,4 +1,12 @@
EXEC = r2unity
SOEXT = $(shell r2 -H R2_LIBEXT)
ifeq ($(SOEXT),dylib)
PLUGIN_SHFLAGS = -dynamiclib
else
PLUGIN_SHFLAGS = -shared
endif
CORE_PLUGIN = src/r2/core_r2unity.$(SOEXT)
BIN_PLUGIN = src/r2/bin_r2unity.$(SOEXT)
PLUGINS = $(CORE_PLUGIN) $(BIN_PLUGIN)
@@ -15,13 +23,6 @@ CORE_PLUGIN_LDFLAGS = $(shell pkg-config --libs r_core 2>/dev/null || echo "")
BIN_PLUGIN_CFLAGS = -Wall -Wextra -g -fPIC $(shell pkg-config --cflags r_bin 2>/dev/null || echo "")
BIN_PLUGIN_LDFLAGS = $(shell pkg-config --libs r_bin 2>/dev/null || echo "")
SOEXT = $(shell r2 -H R2_LIBEXT)
ifeq ($(SOEXT),dylib)
PLUGIN_SHFLAGS = -dynamiclib
else
PLUGIN_SHFLAGS = -shared
endif
R2_USER_PLUGINS = $(shell r2 -H R2_USER_PLUGINS 2>/dev/null)
LIB_SRCS = $(wildcard src/lib/*.c)
@@ -39,7 +40,7 @@ plugin: $(PLUGINS)
install-plugin: $(PLUGINS)
@[ -n "$(R2_USER_PLUGINS)" ] || (echo "r2 not found; cannot resolve R2_USER_PLUGINS"; exit 1)
mkdir -p "$(R2_USER_PLUGINS)"
cp $(PLUGINS) "$(R2_USER_PLUGINS)/"
cp -f $(PLUGINS) "$(R2_USER_PLUGINS)/"
uninstall-plugin:
@[ -n "$(R2_USER_PLUGINS)" ] || (echo "r2 not found; cannot resolve R2_USER_PLUGINS"; exit 1)
@@ -56,8 +57,6 @@ user-install: install-plugin
user-uninstall: uninstall-plugin
rm -f "$(R2PM_BINDIR)/$(EXEC)"
$(EXEC): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
+73 -108
View File
@@ -1,132 +1,97 @@
# r2unity
<img src="r2unity.png" alt="logo" width="150px" height="150px" align="left">
[![CI](https://github.com/radareorg/r2unity/actions/workflows/ci.yml/badge.svg)](https://github.com/radareorg/r2unity/actions/workflows/ci.yml)
`r2unity` is a commandline tool and radare2 plugin for inspecting Unity apps.
<img src="r2unity.png" alt="r2unity logo" width="140px" height="140px" align="left">
Unity apps are written in **C#**, compiled into **MSIL** and then transpiled into **C++** with the il2cpp program.
`r2unity` is a command-line tool and radare2 plugin set for inspecting Unity
IL2CPP builds. It parses `global-metadata.dat`, correlates it with the native
IL2CPP binary, and exposes managed metadata for reverse engineering.
The resulting binary is an AOT executable that picks from the `global-metadata.dat` file all the strings, symbols, classes, structs and many other ecma335 metadata from DotNet.
## Highlights
With this plugin we load all that information and expose it as plaintext, json or inside radare2, ready for proper analysis.
For idiomatic decompilation it is recommended to use **decai** with `-e decai.lang=csharp`.
--pancake
## Features
* Support from il2cpp from v24 to v31, plus Unity 6 metadata v39
* Support 32 and 64bit ELF, FatMACHO, MACHO and PE
* Heuristic method-pointer scanner
* Works on Windows, Linux, macOS, iOS and Android
* Mainly validated on arm64, but should support Intel too
* Parse headers, strings, literals, method names, types, images, assemblies
* Decodes managed `ldstr` string literals from the payload table region
* Find il2cpp companion files relative to the main app executable (`-D`)
* Interop (managed <=> native) listings (PInvoke and reverse-PInvoke calls)
* Support SBOM listings, emit CycloneDX 1.5 JSON with the assemblies (`-S`)
radare2 plugin support:
- `make plugin` builds `src/r2/core_r2unity.$(so|dylib)`.
- `make install-plugin` copies the plugin into the user radare2 plugin
directory.
- The plugin adds `r2unity.*` eval variables and can auto-detect metadata
and IL2CPP library paths from the currently loaded binary.
- Method-symbol mode can apply flags/comments directly to the r2 session,
print r2 commands, or return JSON.
- Parses IL2CPP metadata wire versions v24.1+ through v35, plus v38/v39
metadata used by recent Unity 6 builds.
- Detects companion files for iOS, Android extracted APKs, macOS, Windows,
Linux, and flat fixture layouts.
- Recovers managed images, assemblies, types, methods, method flags, and `ldstr`
string literals.
- Finds method-pointer tables heuristically in ELF, Mach-O, and PE binaries.
- Lists P/Invoke and v29+ reverse-P/Invoke metadata, and emits CycloneDX 1.5
SBOMs for managed assemblies.
- Provides both a core r2 command plugin and an `r_bin` plugin for direct
`global-metadata.dat` inspection.
## Build
The commandline tool and the r2 plugin require **pkg-config** and **radare2** libraries to work:
`r2unity` requires radare2 development files available through `pkg-config`.
```sh
make
make plugin
make user-install # install user-plugin and cli in the r2pm path
make user-install
```
Most users may just run `r2pm -ci r2unity` to get the repo cloned, built and installed.
Most users can install it through r2pm:
After installing the plugin, open a Unity binary in r2 and use:
```sh
r2pm -ci r2unity
```
`make` builds the CLI. `make plugin` builds `core_r2unity` and `bin_r2unity`.
`make user-install` installs both the CLI and plugins for the current user.
## CLI
The normal inputs are the native IL2CPP binary and the matching
`global-metadata.dat`.
```sh
# detect companion files and platform
./r2unity -D /path/to/unity-build
# compact metadata summary
./r2unity -j /path/to/GameAssembly.dll /path/to/global-metadata.dat
# recover method flags/comments as r2 commands
./r2unity -f /path/to/GameAssembly.dll /path/to/global-metadata.dat > methods.r2
# list managed strings, interop metadata, or managed-assembly SBOM data
./r2unity -z /path/to/global-metadata.dat
./r2unity -P -j /path/to/GameAssembly.dll /path/to/global-metadata.dat
./r2unity -R -j /path/to/GameAssembly.dll /path/to/global-metadata.dat
./r2unity -S /path/to/GameAssembly.dll /path/to/global-metadata.dat > sbom.json
```
## radare2
After installing the plugins, open a Unity binary in r2 and use:
```text
r2unity? show help
r2unity-D detect and cache companion file paths
r2unity-i[j] show metadata summary
r2unity-s apply managed method flags/comments
r2unity-s* print the r2 commands instead of applying them
r2unity-z[j] list managed string literals
r2unity-P[*j] list P/Invoke entries
r2unity-R[*j] list reverse-P/Invoke entries
r2unity-S emit managed-assembly CycloneDX JSON
```
And you may also have it available via `r2pm -r r2unity` unless you setup the *PATH* environ.
Set `r2unity.metadata` and `r2unity.library` manually when auto-detection is not
enough. The `bin_r2unity` plugin also lets radare2/rabin2 treat
`global-metadata.dat` as a binary format, exposing sections, strings, symbols,
classes, imports, libraries, and header fields.
## Current Limits
## CLI Usage
- v24.0 metadata, v36/v37 metadata, and WebAssembly are not supported.
- Method-pointer recovery is heuristic; manual `-a` / `-c` pointer reads are
not implemented yet.
- P/Invoke and reverse-P/Invoke output is metadata-first and does not fully
recover native wrapper addresses or every `DllImportAttribute` detail.
- SBOM output covers managed assemblies only, not native dependencies or file
hashes.
```text
Usage: ./r2unity [options] <executable> <global-metadata.dat>
Options:
-a 0xADDR Read the method pointer table starting at virtual address 0xADDR
-c N Read N pointer entries (pair with -a)
-D Detect companion files from the given executable path and exit
-f Fast path: auto-detect ELF/Mach-O/PE and scan method pointers
-h Show help and exit
-j One-line JSON status, or JSON output with -P
-l N Limit emitted entries to N
-P Enumerate P/Invoke (managed -> native) methods
-q Quiet mode: omit banner and informational comments
-r Emit r2 script commands (flags + comments); pairs with -P/-R
-R Enumerate reverse-P/Invoke (native -> managed) methods (v29+)
-S Emit a CycloneDX SBOM (JSON) of the managed assemblies
-v Verbose debug tracing on stderr
-z Enumerate managed string literals (`ldstr`) from metadata
```
Normal symbol recovery needs two inputs but the `-D` will automatically resolve it for you.
1. The native IL2CPP binary:
- iOS: `UnityFramework`
- Android: `libil2cpp.so`
- macOS: `GameAssembly.dylib`
- Windows: `GameAssembly.dll`
- Linux: `GameAssembly.so`
2. The matching `global-metadata.dat`
## radare2 Plugin
After installing the plugin, open a Unity binary in r2 and use:
```console
r2unity-i metadata summary
r2unity-ij metadata summary as JSON
r2unity-s apply managed method flags/comments to the current r2 session
```
The commands suffixed with `*` will print r2 commands, so you can run them by prefixing it with a dot `.`:
```text
.r2unity-R*
```
Plugin configuration variables:
```text
r2unity.metadata path to global-metadata.dat
r2unity.library path to IL2CPP native library
```
If either variable is empty, the plugin tries to detect it from the loaded binary path using the `-D` approach.
## Known Limitations
- `-a` / `-c` are wired into the CLI, but `r2unity_read_method_pointers_at()` is still a stub.
- Method-pointer discovery is heuristic and can lock onto a wrong count-prefixed table on unfamiliar binaries.
- No full `Il2CppCodeRegistration` or `Il2CppMetadataRegistration` structural recovery exists yet.
- P/Invoke currently identifies managed extern methods, but does not decode `DllImportAttribute` DLL names, entry-point names, charset, calling convention, or `SetLastError`.
- Reverse-P/Invoke currently identifies annotated v29+ managed methods, but does not recover native wrapper VAs from `reversePInvokeWrappers`.
- Pre-v29 reverse-P/Invoke attribute argument recovery is not implemented; those values live in native custom-attribute generator thunks.
- Interop wrapper recovery from `Il2CppInteropData` is not implemented.
- SBOM output is managed-metadata-only. It does not enumerate native dependencies, imports, build IDs, UUIDs, PDB GUIDs, code signatures, toolchain data, or file hashes.
- Field, parameter, property, event, generic, interface, vtable, RGCTX, default-value, marshaling, and field-offset tables are declared in the header but not exposed by the CLI/plugin yet.
- Type signatures and parameter names are not printed yet, so methods are named with an argument count rather than a full C# signature.
- WebAssembly is unsupported.
- The plugin and CLI SBOM paths are similar but not identical; the CLI currently emits more assembly properties.
Deep technical notes live in `doc/`.
+1
View File
@@ -814,6 +814,7 @@ Ordered by RE value vs. implementation effort:
- **ELF**: packed Android relocations (`DT_ANDROID_RELA =
0x60000010`, `DT_ANDROID_RELR = 0x6fffe000`) not handled —
Play-Store builds compressed with packed relocs read as "no
relocations".
`DT_VERSYM`/`DT_VERNEED` not consulted (would help identify
Unity version from the `.so` directly). `ET_EXEC` vs `ET_DYN`
branching (we assume PIC shared objects).
-1
View File
@@ -1126,7 +1126,6 @@ Data present on disk / in the binary but **not yet consumed**:
- `reverse-pinvoke-count`, `reverse-pinvoke-list` — `-R`
- `strings-list` — `-z`
## 9. Further reading
Primary references used by r2unity's decoders:
+1 -1
View File
@@ -6,4 +6,4 @@ EXTERNAL_PLUGINS+=r2unity
r2unity: p/r2unity
p/r2unity:
cd p && git clone https://github.com/trufae/r2unity
cd p && git clone https://github.com/radareorg/r2unity
+10
View File
@@ -0,0 +1,10 @@
all:
$(MAKE) plugin
plugin user-install user-uninstall:
$(MAKE) -C ../.. $@
clean:
rm -f *.dylib *.so *.dSYM
.PHONY: all clean
+15 -1
View File
@@ -4,9 +4,23 @@
#include <r_lib.h>
#include "../lib/lib.h"
// clang-format off
static const char *g_help_msg[] = {
"Usage:", "r2unity[-subcmd]", " Unity IL2CPP analyzer", "r2unity", "", "show this help", "r2unity?", "", "show this help", "r2unity-D", "", "auto-detect companion files from current binary path", "r2unity-i", "[j]", "summary (metadata version, type/method counts)", "r2unity-s", "[*j]", "apply/list managed method symbols as flags + comments", "r2unity-z", "[j]", "list managed string literals", "r2unity-P", "[*j]", "list P/Invoke (managed -> native)", "r2unity-R", "[*j]", "list reverse-P/Invoke (native -> managed)", "r2unity-S", "", "emit CycloneDX SBOM (JSON)", "Variables:", "", "", "r2unity.metadata", "", "path to global-metadata.dat", "r2unity.library", "", "path to IL2CPP native library", NULL
"Usage:", "r2unity[-subcmd]", " Unity IL2CPP analyzer",
"r2unity", "", "show this help",
"r2unity?", "", "show this help",
"r2unity-D", "", "auto-detect companion files from current binary path",
"r2unity-i", "[j]", "summary (metadata version, type/method counts)",
"r2unity-s", "[*j]", "apply/list managed method symbols as flags + comments",
"r2unity-z", "[j]", "list managed string literals",
"r2unity-P", "[*j]", "list P/Invoke (managed -> native)",
"r2unity-R", "[*j]", "list reverse-P/Invoke (native -> managed)",
"r2unity-S", "", "emit CycloneDX SBOM (JSON)", "Variables:", "", "",
"r2unity.metadata", "", "path to global-metadata.dat",
"r2unity.library", "", "path to IL2CPP native library",
NULL
};
// clang-format on
static char *method_attrs_str(unsigned flags) {
enum {