Register class inheritance and fields using r2 6.1.6 commands

This commit is contained in:
pancake committed 2026-06-02 20:23:21 +02:00
1 parent d9fae9a6f0
commit ca5867eb5c
2 files changed
+415 -18

No files matched your search

+3 -18
View File
@@ -231,9 +231,6 @@ static void section_free(void *p) {
static RBinSection *new_section(const char *name, ut64 off, ut64 size, bool has_strings) {
RBinSection *sec = R_NEW0 (RBinSection);
if (!sec) {
return NULL;
}
init_section (sec, name, off, size, has_strings);
return sec;
}
@@ -511,7 +508,7 @@ static bool symbols_vec(RBinFile *bf) {
#else
static RList *symbols(RBinFile *bf) {
RList *ret = r_list_newf ((RListFree)r_bin_symbol_free);
if (!ret || !symbols_fill (bf, ret)) {
if (!symbols_fill (bf, ret)) {
r_list_free (ret);
return NULL;
}
@@ -606,10 +603,6 @@ static void append_bin_string(R2UnityStringSink *ret, char *text, ut64 paddr, ut
return;
}
RBinString *str = R_NEW0 (RBinString);
if (!str) {
free (text);
return;
}
str->string = text;
str->paddr = paddr;
str->vaddr = paddr;
@@ -681,9 +674,6 @@ static RList *strings(RBinFile *bf) {
R2UnityBinObj *obj = get_obj (bf);
R_RETURN_VAL_IF_FAIL (obj, NULL);
RList *ret = r_list_newf ((RListFree)r_bin_string_free);
if (!ret) {
return NULL;
}
ut32 ordinal = 0;
add_metadata_strings (obj, ret, &ordinal);
add_literal_strings (obj, ret, &ordinal);
@@ -695,9 +685,6 @@ static RList *strings(RBinFile *bf) {
typedef RVecRBinImport R2UnityImportSink;
static void append_import(R2UnityImportSink *ret, RBinImport *imp) {
if (!imp) {
return;
}
RBinImport *dst = RVecRBinImport_emplace_back (ret);
if (!dst) {
r_bin_import_free (imp);
@@ -710,9 +697,7 @@ static void append_import(R2UnityImportSink *ret, RBinImport *imp) {
typedef RList R2UnityImportSink;
static void append_import(R2UnityImportSink *ret, RBinImport *imp) {
if (imp) {
r_list_append (ret, imp);
}
r_list_append (ret, imp);
}
#endif
@@ -745,7 +730,7 @@ static bool imports_vec(RBinFile *bf) {
#else
static RList *imports(RBinFile *bf) {
RList *ret = r_list_newf ((RListFree)r_bin_import_free);
if (!ret || !imports_fill (bf, ret)) {
if (!imports_fill (bf, ret)) {
r_list_free (ret);
return NULL;
}
+412
View File
@@ -11,6 +11,7 @@ static const char *g_help_msg[] = {
"Usage:", "r2unity[-subcmd]", " Unity IL2CPP analyzer",
"r2unity", "", "show this help",
"r2unity?", "", "show this help",
"r2unity-c", "[*j]", "enumerate classes, inheritance, methods, and fields",
"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",
@@ -284,6 +285,415 @@ static bool find_method_pointers(R2UnityMetadata *meta, const char *path, ut64 *
return false;
}
static int type_definition_for_type_index(const Il2CppTypeDefinition *types, size_t type_count, int32_t type_index) {
if (type_index >= 0) {
for (size_t i = 0; i < type_count; i++) {
if (types[i].byvalTypeIndex == type_index) {
return (int)i;
}
}
}
return -1;
}
static char *type_name_from_index(R2UnityMetadata *meta, const Il2CppTypeDefinition *types, size_t type_count, int32_t type_index, bool fallback) {
int idx = type_definition_for_type_index (types, type_count, type_index);
if (idx >= 0) {
return r2unity_type_fullname (meta, &types[idx], (size_t)idx, R2U_NAME_FALLBACK_TYPE);
}
if (fallback && type_index >= 0) {
return r_str_newf ("type_index.%d", type_index);
}
return NULL;
}
static char *field_name_or_fallback(R2UnityMetadata *meta, const Il2CppFieldDefinition *field, size_t index) {
char *name = r2unity_get_string (meta, field->nameIndex);
if (R_STR_ISNOTEMPTY (name)) {
return name;
}
free (name);
return r_str_newf ("field.%zu", index);
}
static char *method_name_or_fallback(R2UnityMetadata *meta, const Il2CppMethodDefinition *method, size_t index) {
char *name = r2unity_get_string (meta, method->nameIndex);
if (R_STR_ISNOTEMPTY (name)) {
return name;
}
free (name);
return r_str_newf ("method.%zu", index);
}
static void sanitize_ic_name(char *s) {
if (!s) {
return;
}
r_name_filter (s, -1);
for (char *p = s; *p; p++) {
if (*p == '.' || *p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
*p = '_';
}
}
}
static char *r2_class_name(const char *name, size_t index) {
char *out = (name && *name)? strdup (name): r_str_newf ("type_%zu", index);
sanitize_ic_name (out);
if (!out || !*out) {
free (out);
out = r_str_newf ("type_%zu", index);
}
return out;
}
static char *r2_method_name(const char *name, size_t index) {
char *out = (name && *name)? r_str_newf ("%s_%zu", name, index): r_str_newf ("method_%zu", index);
sanitize_ic_name (out);
if (!out || !*out) {
free (out);
out = r_str_newf ("method_%zu", index);
}
return out;
}
static char *r2_field_name(const char *name, size_t index) {
char *out = (name && *name)? strdup (name): r_str_newf ("field_%zu", index);
sanitize_ic_name (out);
if (!out || !*out) {
free (out);
out = r_str_newf ("field_%zu", index);
}
return out;
}
static void pj_hex(PJ *pj, const char *key, ut64 value, int width) {
char hex[64];
snprintf (hex, sizeof (hex), "0x%0*" PFMT64x, width, value);
pj_ks (pj, key, hex);
}
static void pj_string_or_null(PJ *pj, const char *key, const char *value) {
if (value) {
pj_ks (pj, key, value);
} else {
pj_knull (pj, key);
}
}
static void emit_class_text(RCore *core,
R2UnityMetadata *meta,
const Il2CppTypeDefinition *types,
size_t type_count,
const Il2CppMethodDefinition *methods,
size_t method_count,
const Il2CppFieldDefinition *fields,
size_t field_count,
const int32_t *interfaces,
size_t interface_count,
size_t type_index) {
const Il2CppTypeDefinition *td = &types[type_index];
char *name = r2unity_type_fullname (meta, td, type_index, R2U_NAME_FALLBACK_TYPE);
char *base = type_name_from_index (meta, types, type_count, td->parentIndex, true);
r_cons_printf (core->cons, "class %zu %s", type_index, name? name: "-");
if (base) {
r_cons_printf (core->cons, " : %s", base);
}
if (interfaces && td->interfaces_count > 0 && td->interfacesStart >= 0) {
bool first = true;
for (size_t k = 0; k < td->interfaces_count && (size_t)(td->interfacesStart + k) < interface_count; k++) {
char *iname = type_name_from_index (meta, types, type_count, interfaces[td->interfacesStart + k], true);
if (iname) {
r_cons_printf (core->cons, "%s%s", first? " implements ": ", ", iname);
first = false;
}
free (iname);
}
}
r_cons_printf (core->cons, " token=0x%08x fields=%u methods=%u\n", td->token, (unsigned)td->field_count, (unsigned)td->method_count);
int fstart = td->fieldStart;
for (int k = 0; fields && k < td->field_count && fstart >= 0 && (size_t)(fstart + k) < field_count; k++) {
size_t fi = (size_t)(fstart + k);
char *fname = field_name_or_fallback (meta, &fields[fi], fi);
char *ftype = type_name_from_index (meta, types, type_count, fields[fi].typeIndex, true);
r_cons_printf (core->cons, " field %zu %s %s token=0x%08x type_index=%d\n",
fi,
ftype? ftype: "-",
fname? fname: "-",
fields[fi].token,
fields[fi].typeIndex);
free (ftype);
free (fname);
}
int mstart = td->methodStart;
for (int k = 0; methods && k < td->method_count && mstart >= 0 && (size_t)(mstart + k) < method_count; k++) {
size_t mi = (size_t)(mstart + k);
char *mname = method_name_or_fallback (meta, &methods[mi], mi);
char *attrs = method_attrs_str (methods[mi].flags);
r_cons_printf (core->cons, " method %zu %s%s%s(%u) token=0x%08x flags=0x%04x\n",
mi,
attrs && *attrs? attrs: "",
attrs && *attrs? " ": "",
mname? mname: "-",
(unsigned)methods[mi].parameterCount,
methods[mi].token,
methods[mi].flags);
free (attrs);
free (mname);
}
free (base);
free (name);
}
static void emit_class_json(PJ *pj,
R2UnityMetadata *meta,
const Il2CppTypeDefinition *types,
size_t type_count,
const Il2CppMethodDefinition *methods,
size_t method_count,
const Il2CppFieldDefinition *fields,
size_t field_count,
const int32_t *interfaces,
size_t interface_count,
const ut64 *method_ptrs,
bool has_ptrs,
size_t type_index) {
const Il2CppTypeDefinition *td = &types[type_index];
char *name = r2unity_type_fullname (meta, td, type_index, R2U_NAME_FALLBACK_TYPE);
char *ns = r2unity_get_string (meta, td->namespaceIndex);
char *short_name = r2unity_get_string (meta, td->nameIndex);
char *base = type_name_from_index (meta, types, type_count, td->parentIndex, true);
pj_o (pj);
pj_kn (pj, "index", (ut64)type_index);
pj_string_or_null (pj, "name", name);
pj_string_or_null (pj, "namespace", ns);
pj_string_or_null (pj, "short_name", short_name);
pj_string_or_null (pj, "base", base);
pj_ki (pj, "base_type_index", td->parentIndex);
pj_hex (pj, "flags", td->flags, 8);
pj_hex (pj, "token", td->token, 8);
pj_ka (pj, "interfaces");
if (interfaces && td->interfaces_count > 0 && td->interfacesStart >= 0) {
for (size_t k = 0; k < td->interfaces_count && (size_t)(td->interfacesStart + k) < interface_count; k++) {
int32_t idx = interfaces[td->interfacesStart + k];
char *iname = type_name_from_index (meta, types, type_count, idx, true);
pj_o (pj);
pj_ki (pj, "type_index", idx);
pj_string_or_null (pj, "name", iname);
pj_end (pj);
free (iname);
}
}
pj_end (pj);
pj_ka (pj, "fields");
int fstart = td->fieldStart;
for (int k = 0; fields && k < td->field_count && fstart >= 0 && (size_t)(fstart + k) < field_count; k++) {
size_t fi = (size_t)(fstart + k);
char *fname = field_name_or_fallback (meta, &fields[fi], fi);
char *ftype = type_name_from_index (meta, types, type_count, fields[fi].typeIndex, true);
pj_o (pj);
pj_kn (pj, "index", (ut64)fi);
pj_string_or_null (pj, "name", fname);
pj_ki (pj, "type_index", fields[fi].typeIndex);
pj_string_or_null (pj, "type", ftype);
pj_hex (pj, "token", fields[fi].token, 8);
pj_end (pj);
free (ftype);
free (fname);
}
pj_end (pj);
pj_ka (pj, "methods");
int mstart = td->methodStart;
for (int k = 0; methods && k < td->method_count && mstart >= 0 && (size_t)(mstart + k) < method_count; k++) {
size_t mi = (size_t)(mstart + k);
char *mname = method_name_or_fallback (meta, &methods[mi], mi);
char *fullname = r2unity_method_fullname (meta, &methods[mi], td, type_index, R2U_NAME_WITH_PARAMS | R2U_NAME_FALLBACK_TYPE);
char *attrs = method_attrs_str (methods[mi].flags);
ut64 addr = (has_ptrs && method_ptrs)? method_ptrs[mi]: 0;
pj_o (pj);
pj_kn (pj, "index", (ut64)mi);
pj_string_or_null (pj, "name", mname);
pj_string_or_null (pj, "full_name", fullname);
pj_kn (pj, "parameter_count", (ut64)methods[mi].parameterCount);
pj_kn (pj, "addr", addr);
pj_hex (pj, "token", methods[mi].token, 8);
pj_hex (pj, "flags", methods[mi].flags, 4);
pj_hex (pj, "iflags", methods[mi].iflags, 4);
pj_string_or_null (pj, "attrs", (attrs && *attrs)? attrs: NULL);
pj_end (pj);
free (attrs);
free (fullname);
free (mname);
}
pj_end (pj);
pj_end (pj);
free (base);
free (short_name);
free (ns);
free (name);
}
static void emit_class_r2(RCore *core,
R2UnityMetadata *meta,
const Il2CppTypeDefinition *types,
size_t type_count,
const Il2CppMethodDefinition *methods,
size_t method_count,
const Il2CppFieldDefinition *fields,
size_t field_count,
const int32_t *interfaces,
size_t interface_count,
const ut64 *method_ptrs,
bool has_ptrs,
size_t type_index) {
const Il2CppTypeDefinition *td = &types[type_index];
char *name = r2unity_type_fullname (meta, td, type_index, R2U_NAME_FALLBACK_TYPE);
char *r2klass = r2_class_name (name, type_index);
char *base = type_name_from_index (meta, types, type_count, td->parentIndex, false);
if (base) {
r_cons_printf (core->cons, "# class %s : %s\n", name? name: r2klass, base);
} else {
r_cons_printf (core->cons, "# class %s\n", name? name: r2klass);
}
r_cons_printf (core->cons, "ic+%s @ 0\n", r2klass);
if (base) {
char *r2base = r2_class_name (base, 0);
r_cons_printf (core->cons, "ic+%s:%s @ 0\n", r2klass, r2base);
free (r2base);
}
free (base);
if (interfaces && td->interfaces_count > 0 && td->interfacesStart >= 0) {
for (size_t k = 0; k < td->interfaces_count && (size_t)(td->interfacesStart + k) < interface_count; k++) {
char *iname = type_name_from_index (meta, types, type_count, interfaces[td->interfacesStart + k], false);
if (iname) {
char *r2iface = r2_class_name (iname, 0);
r_cons_printf (core->cons, "ic+%s:%s @ 0\n", r2klass, r2iface);
free (r2iface);
}
free (iname);
}
}
int mstart = td->methodStart;
for (int k = 0; methods && k < td->method_count && mstart >= 0 && (size_t)(mstart + k) < method_count; k++) {
size_t mi = (size_t)(mstart + k);
ut64 addr = (has_ptrs && method_ptrs)? method_ptrs[mi]: 0;
addr = addr > 0x1000? addr: 0;
char *mname = method_name_or_fallback (meta, &methods[mi], mi);
char *r2meth = r2_method_name (mname, mi);
r_cons_printf (core->cons, "ic+%s.%s @ 0x%" PFMT64x "\n", r2klass, r2meth, addr);
free (r2meth);
free (mname);
}
int fstart = td->fieldStart;
for (int k = 0; fields && k < td->field_count && fstart >= 0 && (size_t)(fstart + k) < field_count; k++) {
size_t fi = (size_t)(fstart + k);
char *fname = field_name_or_fallback (meta, &fields[fi], fi);
char *r2field = r2_field_name (fname, fi);
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",
r2klass,
r2field,
ftype? ftype: "unknown");
free (ftype);
free (r2field);
free (fname);
}
free (r2klass);
free (name);
}
static int cmd_classes(RCore *core, char mode) {
const char *metadata_path = resolve_metadata_path (core);
RBuffer *buf = NULL;
R2UnityMetadata *meta = open_metadata (core, &buf);
if (!meta) {
return 1;
}
size_t type_count = 0;
Il2CppTypeDefinition *types = r2unity_get_type_definitions (meta, &type_count);
if (!types) {
R_LOG_ERROR ("unable to decode type definitions");
close_metadata (meta, buf);
return 1;
}
size_t method_count = 0;
Il2CppMethodDefinition *methods = r2unity_get_method_definitions (meta, &method_count);
size_t field_count = 0;
Il2CppFieldDefinition *fields = r2unity_get_field_definitions (meta, &field_count);
size_t interface_count = 0;
int32_t *interfaces = r2unity_get_type_index_table (meta, R2U_SEC_INTERFACES, &interface_count);
ut64 *method_ptrs = NULL;
bool has_ptrs = false;
const char *lib = resolve_library_path (core);
if (lib && *lib) {
has_ptrs = find_method_pointers (meta, lib, &method_ptrs);
}
if (method_ptrs && !has_ptrs) {
for (size_t k = 0; k < method_count; k++) {
if (method_ptrs[k]) {
has_ptrs = true;
break;
}
}
}
if (mode == 'j') {
PJ *pj = pj_new ();
pj_o (pj);
pj_kb (pj, "ok", true);
pj_ki (pj, "version", meta->version);
pj_ks (pj, "unity_range", r2unity_unity_range_from_wire (meta->version));
pj_kb (pj, "has_ptrs", has_ptrs);
pj_kn (pj, "types", (ut64)type_count);
pj_kn (pj, "methods", (ut64)method_count);
pj_kn (pj, "fields", (ut64)field_count);
pj_ka (pj, "classes");
for (size_t i = 0; i < type_count; i++) {
emit_class_json (pj, meta, types, type_count, methods, method_count, fields, field_count, interfaces, interface_count, method_ptrs, has_ptrs, i);
}
pj_end (pj);
pj_end (pj);
r_cons_println (core->cons, pj_string (pj));
pj_free (pj);
} else if (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: "-");
if (!has_ptrs) {
r_cons_println (core->cons, "# Method addresses default to 0; run r2unity-D or set r2unity.library to recover native addresses.");
}
for (size_t i = 0; i < type_count; i++) {
emit_class_r2 (core, meta, types, type_count, methods, method_count, fields, field_count, interfaces, interface_count, method_ptrs, has_ptrs, i);
}
} else {
r_cons_printf (core->cons, "# classes from %s\n", metadata_path && *metadata_path? metadata_path: "-");
r_cons_printf (core->cons, "# wire_version=%d (%s) types=%zu methods=%zu fields=%zu\n",
meta->version,
r2unity_unity_range_from_wire (meta->version),
type_count,
method_count,
field_count);
for (size_t i = 0; i < type_count; i++) {
emit_class_text (core, meta, types, type_count, methods, method_count, fields, field_count, interfaces, interface_count, i);
}
}
R_FREE (method_ptrs);
R_FREE (interfaces);
R_FREE (fields);
R_FREE (methods);
R_FREE (types);
close_metadata (meta, buf);
return 0;
}
/* ---------- r2unity-s (methods) ---------- */
static int cmd_symbols(RCore *core, char mode) {
/* mode: 0 = apply to r2 session, '*' = print r2 commands, 'j' = JSON */
@@ -678,6 +1088,8 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
}
switch (sub) {
case 'c':
return cmd_classes (core, mode) == 0;
case 'D':
return cmd_detect (core, mode == 'j') == 0;
case 'i':