Apply interop with -AAA

This commit is contained in:
pancake committed 2026-10-08 00:47:31 +02:00
1 parent ccaa476cac
commit ae094c666a
2 files changed
+98 -8

No files matched your search

+19 -3
View File
@@ -94,15 +94,18 @@ After installing the plugins, open a Unity binary in r2 and use:
```text
r2unity? show help
r2unity-A import classes and method flags (same as .r2unity-c*)
r2unity-AA also import method comments, strings, native tables and interop
r2unity-AAA also analyze native references (aar)
r2unity-AAAA also perform native analysis (aaa)
r2unity-D detect and cache companion file paths
r2unity-L open/select and map the IL2CPP native library
r2unity-c[*j] list classes, or emit an import script / JSON
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-z[+j] list managed string literals (+ imports them)
r2unity-P[+*j] list P/Invoke entries (+ imports flags and comments)
r2unity-R[+*j] list reverse-P/Invoke entries (+ imports known wrappers)
r2unity-S emit managed-assembly SBOM text summary
r2unity-Sj emit managed-assembly CycloneDX JSON
```
@@ -113,6 +116,19 @@ flags. The script loads the native companion library automatically, so
Methods without a native implementation retain address zero and do not get
a seekable flag.
The analysis levels are cumulative. `r2unity-AA` adds method signatures and
comments, native registration tables, managed strings, P/Invoke method flags
and comments, and known reverse-P/Invoke wrappers. It maps `global-metadata.dat`
read-only at an unused address range named `r2unity.metadata`. Seek to
`str.unity.<index>` to read a literal's actual bytes; these addresses refer to
the mapped metadata, rather than runtime managed string objects. You can also
import strings or interop separately with `r2unity-z+`, `r2unity-P+`, and
`r2unity-R+`.
`r2unity-AAA` performs all of those imports and then runs `aar` to find native
references. `r2unity-AAAA` additionally runs `aaa` for deeper native analysis.
These two levels take longer on large binaries.
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,
+79 -5
View File
@@ -1099,7 +1099,8 @@ static int cmd_strings(RCore *core, char mode) {
ut8 *bytes = NULL;
size_t len = 0;
ut64 data_off = (ut64)meta->stringLiteralDataOffset + lits[i].dataIndex;
if (!r2unity_read_string_literal (meta, &lits[i], &bytes, &len)) {
if (lits[i].dataIndex > (ut64)meta->stringLiteralDataSize
|| !r2unity_read_string_literal (meta, &lits[i], &bytes, &len)) {
if (mode != 'j' && mode != '+') {
r_cons_printf (core->cons, "%zu\t0x%" PFMT64x "\t%u\t<invalid>\n", i, data_off, lits[i].length);
}
@@ -1203,7 +1204,48 @@ static void print_interop_wrapper_flag(RCore *core, const char *name, ut64 wrapp
}
}
static void apply_interop(RCore *core, const R2UnityInterop *it, bool reverse, ut64 addr) {
if (!addr || !it->name) {
return;
}
char *name = interop_flag_name (reverse? "sym.unity.reverse": "sym.unity.pinvoke", it);
if (!name) {
return;
}
if (!reverse) {
char *indexed = r_str_newf ("%s.%d", name, it->method_index);
free (name);
name = indexed;
if (!name) {
return;
}
}
r_flag_set (core->flags, name, addr, 1);
if (reverse && !r_anal_get_function_at (core->anal, addr)) {
r_anal_create_function (core->anal, name, addr, R_ANAL_FCN_TYPE_FCN, NULL);
}
char *comment = reverse
? r_str_newf ("ReversePInvoke %s [%s]", it->name, interop_kind_label (it->kind))
: it->dll_name
? r_str_newf ("PInvoke %s -> %s!%s", it->name, it->dll_name,
it->entry_name? it->entry_name: it->name)
: r_str_newf ("PInvoke %s -> <unresolved>", it->name);
const char *previous = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
if (comment && (!previous || !strstr (previous, comment))) {
char *combined = previous? r_str_newf ("%s\n%s", previous, comment): strdup (comment);
if (combined) {
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, combined);
free (combined);
}
}
free (comment);
free (name);
}
static int cmd_interop(RCore *core, bool reverse, char mode) {
if (mode == '+' && cmd_load_library (core)) {
return 1;
}
RBuffer *buf = NULL;
R2UnityMetadata *meta = open_metadata (core, &buf);
if (!meta) {
@@ -1213,6 +1255,11 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
R2UnityInterop *items = reverse
? r2unity_enumerate_reverse_pinvokes (meta, &n)
: r2unity_enumerate_pinvokes (meta, &n);
R2UnityNativeResult native_result = { 0 };
size_t method_count = r2unity_metadata_section_count (meta, R2U_SEC_METHODS);
if (!reverse && mode == '+' && n) {
find_method_pointers (core, meta, resolve_library_path (core), &native_result);
}
if (reverse) {
const char *lib = resolve_library_path (core);
if (lib) {
@@ -1265,6 +1312,13 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
}
}
pj_end (pj);
} else if (mode == '+') {
ut64 addr = reverse? it->wrapper_va: 0;
if (!reverse && native_result.method_ptrs && it->method_index >= 0
&& (size_t)it->method_index < method_count) {
addr = native_result.method_ptrs[it->method_index];
}
apply_interop (core, it, reverse, addr);
} else if (mode == '*') {
if (it->name) {
const char *prefix = reverse? "sym.unity.reverse": "sym.unity";
@@ -1311,10 +1365,25 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
r_cons_printf (core->cons, "# summary: %s=%zu\n", reverse? "reverse_pinvokes": "pinvokes", n);
}
r2unity_free_interop (items, n);
r2unity_native_result_fini (&native_result);
close_metadata (meta, buf);
return 0;
}
static bool cmd_analyze(RCore *core, unsigned level) {
if (r_core_cmd0 (core, ".r2unity-c*") != 0) {
return false;
}
if (level >= 2 && (cmd_symbols (core, 0) || cmd_strings (core, '+')
|| cmd_interop (core, false, '+') || cmd_interop (core, true, '+'))) {
return false;
}
if (level >= 3 && r_core_cmd0 (core, "aar") != 0) {
return false;
}
return level < 4 || r_core_cmd0 (core, "aaa") == 0;
}
/* ---------- r2unity-S (SBOM) ---------- */
static int cmd_sbom(RCore *core, char mode) {
const char *metadata_path = resolve_metadata_path (core);
@@ -1354,16 +1423,21 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
}
rest++;
/* "r2unity-" followed by the subcommand letter, optionally a modifier
*('j' for JSON, '*' for r2 commands), optionally whitespace + args. */
*('j' for JSON, '*' for r2 commands, '+' to import), or repeated 'A's. */
char sub = *rest;
char mode = 0;
unsigned level = 1;
if (sub) {
rest++;
if (*rest == 'j' || *rest == '*') {
while (sub == 'A' && *rest == 'A') {
level++;
rest++;
}
if (sub != 'A' && (*rest == 'j' || *rest == '*' || *rest == '+')) {
mode = *rest;
rest++;
}
if (*rest && *rest != ' ' && *rest != '\t') {
if (level > 4 || (*rest && *rest != ' ' && *rest != '\t')) {
r_cons_cmd_help (core->cons, g_help_msg);
return true;
}
@@ -1374,7 +1448,7 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
switch (sub) {
case 'A':
return r_core_cmd0 (core, ".r2unity-c*") == 0;
return cmd_analyze (core, level);
case 'c':
return cmd_classes (core, mode) == 0;
case 'D':