From 212085e7fbeb80d7910db0821a2557532a9faf87 Mon Sep 17 00:00:00 2001 From: pancake Date: Thu, 8 Oct 2026 00:34:02 +0200 Subject: [PATCH] Fix method offsets, map native code and silence class imports --- Makefile | 5 ++++- README.md | 7 +++++++ meson.build | 9 ++++++++- src/lib/bin/native.c | 20 +++++++++++------- src/main.c | 4 ++-- src/r2/core_r2unity.c | 47 ++++++++++++++++++++++++++++++++++++++----- 6 files changed, 76 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index c1b7af8..b1eee8c 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,9 @@ all: $(EXEC) r2 plugin: $(PLUGINS) +test: $(EXEC) $(PLUGINS) + python3 test/test_import.py + install-plugin: $(PLUGINS) @[ -n "$(R2_USER_PLUGINS)" ] || (echo "r2 not found; cannot resolve R2_USER_PLUGINS"; exit 1) mkdir -p "$(R2_USER_PLUGINS)" @@ -96,4 +99,4 @@ clean: rm -f $(EXEC) $(OBJS) $(LEGACY_OBJS) $(PLUGINS) $(CONFIG_H) -.PHONY: all clean plugin install-plugin uninstall-plugin fmt bindist +.PHONY: all clean plugin install-plugin uninstall-plugin fmt bindist test diff --git a/README.md b/README.md index 8e3b340..13dcb9b 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,8 @@ 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-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 @@ -104,6 +106,11 @@ r2unity-S emit managed-assembly SBOM text summary r2unity-Sj emit managed-assembly CycloneDX JSON ``` +Use `.r2unity-c*` to import classes and method flags. The script loads the +native companion library automatically, so `s sym.unity..` +followed by `pd` shows the method's native code. Methods without a native +implementation retain address zero and do not get a seekable flag. + 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, diff --git a/meson.build b/meson.build index afc61a2..3409e49 100644 --- a/meson.build +++ b/meson.build @@ -91,7 +91,7 @@ if build_plugins warning('r2 was not found; installing plugins under ' + r2_plugindir) endif - shared_library( + core_plugin = shared_library( 'core_r2unity', files('src/r2/core_r2unity.c') + lib_sources, include_directories: lib_inc, @@ -115,4 +115,11 @@ if build_plugins install_dir: r2_plugindir, ) + python = find_program('python3', required: false) + if python.found() + test('r2unity-class-import', python, + args: [files('test/test_import.py'), core_plugin], + ) + endif + endif diff --git a/src/lib/bin/native.c b/src/lib/bin/native.c index 949f40e..c17c853 100644 --- a/src/lib/bin/native.c +++ b/src/lib/bin/native.c @@ -156,6 +156,7 @@ typedef struct { R2UnityMetadata *meta; Il2CppImageDefinition *images; Il2CppTypeDefinition *types; + Il2CppMethodDefinition *methods; size_t image_count; size_t type_count; size_t method_count; @@ -166,13 +167,14 @@ static bool method_tables_init(R2UnityMethodTables *tables, R2UnityMetadata *met tables->meta = meta; tables->images = r2unity_get_images (meta, &tables->image_count); tables->types = r2unity_get_type_definitions (meta, &tables->type_count); - tables->method_count = (size_t)r2unity_metadata_section_count (meta, R2U_SEC_METHODS); - return tables->images && tables->image_count && tables->types && tables->type_count && tables->method_count; + tables->methods = r2unity_get_method_definitions (meta, &tables->method_count); + return tables->images && tables->image_count && tables->types && tables->type_count && tables->methods && tables->method_count; } static void method_tables_fini(R2UnityMethodTables *tables) { R_FREE (tables->images); R_FREE (tables->types); + R_FREE (tables->methods); } static int image_index_by_name(R2UnityMethodTables *tables, const char *module_name) { @@ -215,23 +217,27 @@ static size_t copy_image_method_table(R2UnityNativeView *view, R2UnityMethodTabl if (image->typeStart < 0) { return 0; } - size_t local = 0; size_t copied = 0; size_t start = (size_t)image->typeStart; size_t end = R_MIN (tables->type_count, start + image->typeCount); - for (size_t ti = start; ti < end && local < table_count; ti++) { + for (size_t ti = start; ti < end; ti++) { const Il2CppTypeDefinition *td = &tables->types[ti]; if (td->methodStart < 0) { - local += td->method_count; continue; } - for (size_t k = 0; k < td->method_count && local < table_count; k++, local++) { + for (size_t k = 0; k < td->method_count; k++) { size_t mi = (size_t)td->methodStart + k; if (mi >= tables->method_count) { continue; } + ut32 token = tables->methods[mi].token; + ut32 rid = token & 0x00ffffff; + if ((token >> 24) != 0x06 || !rid || rid > table_count) { + continue; + } ut64 raw = 0; - if (!read_ptr_at (view, table_va + (ut64)local * view->ptr_size, &raw)) { + // CodeGenModule method pointers are indexed by the MethodDef token RID. + if (!read_ptr_at (view, table_va + (ut64)(rid - 1) * view->ptr_size, &raw)) { continue; } ut64 addr = code_va_from_raw (view, raw); diff --git a/src/main.c b/src/main.c index 5288d3e..cc5fa5e 100644 --- a/src/main.c +++ b/src/main.c @@ -822,7 +822,7 @@ static void emit_class_r2(R2UnityMetadata *meta, } free (base); } - printf ("ic+%s @ 0\n", r2klass); + printf ("'@0x0'ic+ %s\n", r2klass); int mstart = td->methodStart; for (int k = 0; methods && k < td->method_count && mstart >= 0 && (size_t)(mstart + k) < method_count; k++) { @@ -833,7 +833,7 @@ static void emit_class_r2(R2UnityMetadata *meta, } char *mname = method_name_or_fallback (meta, &methods[mi], mi); char *r2meth = r2_ic_name ("method", mname, mi); - printf ("ic+%s.%s @ 0x%" PFMT64x "\n", r2klass, r2meth, addr); + printf ("'@0x%" PFMT64x "'ic+ %s.%s\n", addr, r2klass, r2meth); free (r2meth); free (mname); } diff --git a/src/r2/core_r2unity.c b/src/r2/core_r2unity.c index 51415c2..01806b4 100644 --- a/src/r2/core_r2unity.c +++ b/src/r2/core_r2unity.c @@ -15,6 +15,7 @@ static const char *g_help_msg[] = { "r2unity?", "", "show this help", "r2unity-c", "[*j]", "enumerate classes, inheritance, methods, and fields", "r2unity-D", "", "auto-detect companion files from current binary path", + "r2unity-L", "", "open/select and map the IL2CPP native library", "r2unity-i", "[j]", "summary (metadata version, type/method counts)", "r2unity-s", "[*j]", "apply/list managed/native symbols as flags + comments", "r2unity-z", "[j]", "list managed string literals", @@ -287,6 +288,28 @@ static const char *resolve_library_path(RCore *core) { return R_STR_ISNOTEMPTY (v)? v: NULL; } +static int cmd_load_library(RCore *core) { + const char *path = resolve_library_path (core); + if (!path) { + R_LOG_ERROR ("r2unity.library is not set. Run r2unity-D or set it manually"); + return 1; + } + RBinFile *bf = r_bin_file_find_by_name (core->bin, path); + if (bf) { + if (!r_core_bin_set_by_fd (core, bf->fd) || !r_io_use_fd (core->io, bf->fd)) { + return 1; + } + } else if (!current_binary_matches_path (core, path)) { + RIODesc *desc = r_core_file_open (core, path, R_PERM_R, UT64_MAX); + if (!desc || !r_core_bin_load (core, path, UT64_MAX)) { + R_LOG_ERROR ("cannot load IL2CPP native library: %s", path); + return 1; + } + } + r_core_block_read (core); + return 0; +} + static R2UnityMetadata *open_metadata(RCore *core, RBuffer **out_buf) { const char *path = resolve_metadata_path (core); if (!path) { @@ -667,10 +690,10 @@ static void emit_class_r2(RCore *core, } else { r_cons_printf (core->cons, "# class %s\n", name? name: r2klass); } - r_cons_printf (core->cons, "ic+%s @ 0\n", r2klass); + r_cons_printf (core->cons, "'@0x0'ic+ %s\n", r2klass); if (base) { char *r2base = r2_ic_name ("type", base, 0, false); - r_cons_printf (core->cons, "ic+%s:%s @ 0\n", r2klass, r2base); + r_cons_printf (core->cons, "'@0x0'ic+ %s:%s\n", r2klass, r2base); free (r2base); } free (base); @@ -679,7 +702,7 @@ static void emit_class_r2(RCore *core, char *iname = type_name_from_index (meta, types, type_count, interfaces[td->interfacesStart + k], false); if (iname) { char *r2iface = r2_ic_name ("type", iname, 0, false); - r_cons_printf (core->cons, "ic+%s:%s @ 0\n", r2klass, r2iface); + r_cons_printf (core->cons, "'@0x0'ic+ %s:%s\n", r2klass, r2iface); free (r2iface); } free (iname); @@ -693,7 +716,10 @@ static void emit_class_r2(RCore *core, addr = addr > 0x1000? addr: 0; char *mname = method_name_or_fallback (meta, &methods[mi], mi); char *r2meth = r2_ic_name ("method", mname, mi, true); - r_cons_printf (core->cons, "ic+%s.%s @ 0x%" PFMT64x "\n", r2klass, r2meth, addr); + r_cons_printf (core->cons, "'@0x%" PFMT64x "'ic+ %s.%s\n", addr, r2klass, r2meth); + if (addr) { + r_cons_printf (core->cons, "'@0x%" PFMT64x "'f sym.unity.%s.%s 1\n", addr, r2klass, r2meth); + } free (r2meth); free (mname); } @@ -704,7 +730,7 @@ static void emit_class_r2(RCore *core, char *fname = field_name_or_fallback (meta, &fields[fi], fi); char *r2field = r2_ic_name ("field", fname, fi, false); char *ftype = type_name_from_index (meta, types, type_count, fields[fi].typeIndex, true); - r_cons_printf (core->cons, "ic+%s..%s %s @ 0\n", + r_cons_printf (core->cons, "'@0x0'ic+ %s..%s %s\n", r2klass, r2field, ftype? ftype: "unknown"); @@ -769,6 +795,9 @@ static int cmd_classes(RCore *core, char mode) { r_cons_println (core->cons, "# r2 script generated by r2unity-c"); r_cons_printf (core->cons, "# Input file: %s\n", metadata_path && *metadata_path? metadata_path: "-"); print_native_result (core, &native_result); + if (has_ptrs) { + r_cons_println (core->cons, "r2unity-L"); + } if (!has_ptrs) { r_cons_println (core->cons, "# Method addresses default to 0; run r2unity-D or set r2unity.library to recover native addresses."); } @@ -805,6 +834,9 @@ static int cmd_symbols(RCore *core, char mode) { R_LOG_ERROR ("r2unity.library is not set. Run r2unity-D or set it manually"); return 1; } + if (!mode && cmd_load_library (core)) { + return 1; + } RBuffer *buf = NULL; R2UnityMetadata *meta = open_metadata (core, &buf); if (!meta) { @@ -836,6 +868,9 @@ static int cmd_symbols(RCore *core, char mode) { pj_ka (pj, "methods"); } else if (mode == '*') { print_native_result (core, &native_result); + if (has_ptrs) { + r_cons_println (core->cons, "r2unity-L"); + } } if (mode != 'j') { @@ -1274,6 +1309,8 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) { return cmd_classes (core, mode) == 0; case 'D': return cmd_detect (core, mode == 'j') == 0; + case 'L': + return cmd_load_library (core) == 0; case 'i': return cmd_info (core, mode == 'j') == 0; case 's':