diff --git a/Makefile b/Makefile
index e1f1ec8..7587e94 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/README.md b/README.md
index 1c7c485..936b692 100644
--- a/README.md
+++ b/README.md
@@ -1,132 +1,97 @@
# r2unity
-
+[](https://github.com/radareorg/r2unity/actions/workflows/ci.yml)
-`r2unity` is a commandline tool and radare2 plugin for inspecting Unity apps.
+
-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]
-
-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/`.
diff --git a/doc/future.md b/doc/future.md
index 538f610..462b20e 100644
--- a/doc/future.md
+++ b/doc/future.md
@@ -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).
diff --git a/doc/r2unity.md b/doc/r2unity.md
index 5d692ca..eb33fb4 100644
--- a/doc/r2unity.md
+++ b/doc/r2unity.md
@@ -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:
diff --git a/r2plugin/config.mk b/r2plugin/config.mk
index d91d0a3..611291a 100644
--- a/r2plugin/config.mk
+++ b/r2plugin/config.mk
@@ -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
diff --git a/src/r2/Makefile b/src/r2/Makefile
new file mode 100644
index 0000000..de87cde
--- /dev/null
+++ b/src/r2/Makefile
@@ -0,0 +1,10 @@
+all:
+ $(MAKE) plugin
+
+plugin user-install user-uninstall:
+ $(MAKE) -C ../.. $@
+
+clean:
+ rm -f *.dylib *.so *.dSYM
+
+.PHONY: all clean
diff --git a/src/r2/core_r2unity.c b/src/r2/core_r2unity.c
index 2852f2d..ab8c981 100644
--- a/src/r2/core_r2unity.c
+++ b/src/r2/core_r2unity.c
@@ -4,9 +4,23 @@
#include
#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 {