mirror of
https://github.com/radareorg/r2unity.git
synced 2026-11-07 06:36:00 -10:00
Update readme and minor clang-format fixup
This commit is contained in:
1 parent
bc49ade2ee
commit
ac757ccd8f
7 files changed
+109
-121
No files matched your search
@@ -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)
|
||||
|
||||
|
||||
@@ -1,132 +1,97 @@
|
||||
# r2unity
|
||||
|
||||
<img src="r2unity.png" alt="logo" width="150px" height="150px" align="left">
|
||||
[](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/`.
|
||||
@@ -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).
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user