mirror of
https://github.com/radareorg/r2unity.git
synced 2026-11-07 06:36:00 -10:00
Add visibility attributes and remove unused code
This commit is contained in:
1 parent
bc0478ba31
commit
0e1506b77b
6 files changed
+154
-409
No files matched your search
@@ -2,8 +2,8 @@
|
||||
|
||||
# Compiler and flags
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -g -I./third_party/radare2/libr/include $(shell pkg-config --cflags r_core r_util 2>/dev/null || echo "")
|
||||
LDFLAGS = $(shell pkg-config --libs r_core r_util 2>/dev/null || echo "")
|
||||
CFLAGS = -Wall -Wextra -g -I./third_party/radare2/libr/include $(shell pkg-config --cflags r_util 2>/dev/null || echo "")
|
||||
LDFLAGS = $(shell pkg-config --libs r_util 2>/dev/null || echo "")
|
||||
|
||||
# Project name
|
||||
EXEC = r2unity
|
||||
|
||||
@@ -205,6 +205,48 @@ R_API bool r2unity_find_method_pointers_elf (R2UnityMetadata *meta, const char *
|
||||
* use method_count computed earlier (methodsSize / sizeof(Il2CppMethodDefinition))
|
||||
*/
|
||||
ut32 expected = (ut32) R_MAX ((ut64)64, (ut64) ((ut64) meta->methodsSize / sizeof (Il2CppMethodDefinition)));
|
||||
// Try to locate CodeRegistration-like layout first: [count, ptr] for multiple arrays
|
||||
for (int i = 0; i < e.nsegs && !found; i++) {
|
||||
const ElfSeg *s = &e.segs[i];
|
||||
bool is_data = (s->flags & 0x1) && !(s->flags & 0x4);
|
||||
if (!is_data || s->filesz < (ut64)(16 + (ut64)ptrsz * 2)) continue;
|
||||
const ut8 *buf = e.file + s->offset;
|
||||
ut64 sz = s->filesz;
|
||||
for (ut64 off = 0; off + (ut64)(8 + ptrsz) * 3 <= sz; off += 4) {
|
||||
ut32 cnt1 = RD_LE32 (buf + off + 0);
|
||||
ut64 p1 = (ptrsz == 8)? RD_LE64 (buf + off + 8): (ut64) RD_LE32 (buf + off + 4);
|
||||
ut32 cnt2 = RD_LE32 (buf + off + (ut64)(8 + ptrsz));
|
||||
ut64 p2 = (ptrsz == 8)? RD_LE64 (buf + off + (ut64)(8 + ptrsz) + 8): (ut64) RD_LE32 (buf + off + (ut64)(8 + ptrsz) + 4);
|
||||
if (cnt1 < 32 || cnt2 < 16) continue;
|
||||
// Sample method array
|
||||
ut32 good = 0, seen = 0;
|
||||
ut32 sample = R_MIN ((ut32)128, cnt1);
|
||||
for (ut32 k = 0; k < sample; k++) {
|
||||
const ut8 *pp = elf_vm_to_ptr (&e, p1 + (ut64)k * (ut64)ptrsz);
|
||||
if (!pp && base_vaddr != UT64_MAX) { pp = elf_vm_to_ptr (&e, base_vaddr + p1 + (ut64)k * (ut64)ptrsz); }
|
||||
if (!pp) break;
|
||||
ut64 val = (ptrsz == 8) ? RD_LE64 (pp) : (ut64) RD_LE32 (pp);
|
||||
if (val) seen++;
|
||||
if ((val >= text_lo && val < text_hi) || (val && base_vaddr != UT64_MAX && (val + base_vaddr) >= text_lo && (val + base_vaddr) < text_hi)) good++;
|
||||
}
|
||||
if (seen >= 8 && good >= 8) {
|
||||
if (r2unity_is_debug ()) fprintf (stderr, "[r2unity/elf] codeReg p1=0x%"PFMT64x" cnt1=%u\n", p1, cnt1);
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
size_t tocopy = R_MIN ((size_t) cnt1, method_count);
|
||||
size_t in_text = 0;
|
||||
for (size_t m = 0; m < tocopy; m++) {
|
||||
const ut8 *pp = elf_vm_to_ptr (&e, p1 + (ut64)m * (ut64)ptrsz);
|
||||
if (!pp && base_vaddr != UT64_MAX) { pp = elf_vm_to_ptr (&e, base_vaddr + p1 + (ut64)m * (ut64)ptrsz); }
|
||||
if (!pp) break;
|
||||
ut64 val = (ptrsz == 8) ? RD_LE64 (pp) : (ut64) RD_LE32 (pp);
|
||||
ut64 abs = (val >= text_lo && val < text_hi)? val: ((val && base_vaddr != UT64_MAX)? val + base_vaddr: val);
|
||||
if (abs >= text_lo && abs < text_hi) { candidates[m] = abs; in_text++; }
|
||||
}
|
||||
if (in_text >= 8) { found = true; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < e.nsegs && !found; i++) {
|
||||
const ElfSeg *s = &e.segs[i];
|
||||
bool is_data = (s->flags & 0x1) && !(s->flags & 0x4);
|
||||
|
||||
+2
-392
@@ -1,8 +1,6 @@
|
||||
#include "lib.h"
|
||||
#include <r_util.h>
|
||||
#include <r_util/r_buf.h>
|
||||
#include <r_core.h>
|
||||
#include <r_util/r_json.h>
|
||||
#undef RD_LE32
|
||||
#undef RD_LE64
|
||||
static inline ut32 RD_LE32 (const ut8 *p) {
|
||||
@@ -312,395 +310,7 @@ R_API Il2CppImageDefinition *r2unity_get_images (R2UnityMetadata *meta, size_t *
|
||||
return imgs;
|
||||
}
|
||||
|
||||
R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_path, ut64 **out_ptrs) {
|
||||
if (!meta || !exe_path || !out_ptrs) {
|
||||
return false;
|
||||
}
|
||||
*out_ptrs = NULL;
|
||||
/* Try specialized fast finders first based on magic, then fall back to r2 scanning */
|
||||
{
|
||||
unsigned char magic[4] = {0};
|
||||
FILE *fp = fopen (exe_path, "rb");
|
||||
if (fp) {
|
||||
(void)fread (magic, 1, 4, fp);
|
||||
fclose (fp);
|
||||
}
|
||||
ut32 m = (ut32)magic[0] | ((ut32)magic[1] << 8) | ((ut32)magic[2] << 16) | ((ut32)magic[3] << 24);
|
||||
if (magic[0] == 0x7f && magic[1] == 'E' && magic[2] == 'L' && magic[3] == 'F') {
|
||||
if (r2unity_find_method_pointers_elf (meta, exe_path, out_ptrs)) {
|
||||
return true;
|
||||
}
|
||||
} else if (m == 0xfeedfacf || m == 0xcffaedfe || m == 0xcafebabe || m == 0xbebafeca) {
|
||||
if (r2unity_find_method_pointers_macho (meta, exe_path, out_ptrs)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
RCore *core = r_core_new ();
|
||||
if (!core) {
|
||||
return false;
|
||||
}
|
||||
// apply relocs/binds/fixups
|
||||
r_core_cmd0 (core, "e bin.cache=true");
|
||||
r_core_cmd0 (core, "e bin.relocs.apply=true");
|
||||
r_core_cmd0 (core, "e io.va=true");
|
||||
if (!r_core_file_open (core, exe_path, 0, 0)) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
if (!r_core_bin_load (core, NULL, 0)) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
// detect pointer size
|
||||
int ptrsz = 8;
|
||||
char *ij = r_core_cmd_str (core, "ij");
|
||||
if (ij) {
|
||||
RJson *pj = r_json_parse (ij);
|
||||
if (pj) {
|
||||
const RJson *pbin = r_json_get (pj, "bin");
|
||||
if (pbin) {
|
||||
st64 bits = r_json_get_num (pbin, "bits");
|
||||
ptrsz = (bits == 32)? 4: 8;
|
||||
}
|
||||
r_json_free (pj);
|
||||
}
|
||||
free (ij);
|
||||
}
|
||||
// compute text range and data ranges from io maps json
|
||||
ut64 text_lo = UT64_MAX;
|
||||
ut64 text_hi = 0;
|
||||
char *omj = r_core_cmd_str (core, "omj");
|
||||
RJson *jom = omj? r_json_parse (omj): NULL;
|
||||
#define MAX_RANGES 128
|
||||
typedef struct { ut64 from, to; } Range;
|
||||
Range data_ranges[MAX_RANGES];
|
||||
int data_n = 0;
|
||||
if (jom && jom->type == R_JSON_ARRAY) {
|
||||
for (size_t i = 0; i < jom->children.count; i++) {
|
||||
const RJson *item = r_json_item (jom, i);
|
||||
if (!item) continue;
|
||||
const char *perm = r_json_get_str (item, "perm");
|
||||
ut64 from = (ut64) r_json_get_num (item, "from");
|
||||
ut64 to = (ut64) r_json_get_num (item, "to");
|
||||
if (!perm) continue;
|
||||
if (strchr (perm, 'x')) {
|
||||
if (from < text_lo) text_lo = from;
|
||||
if (to > text_hi) text_hi = to;
|
||||
} else if (strchr (perm, 'r')) {
|
||||
if (data_n < MAX_RANGES) {
|
||||
data_ranges[data_n].from = from;
|
||||
data_ranges[data_n].to = to;
|
||||
data_n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jom) r_json_free (jom);
|
||||
free (omj);
|
||||
if (text_lo == UT64_MAX || text_hi <= text_lo) {
|
||||
if (g_debug) fprintf (stderr, "[r2unity] No executable text range found.\n");
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
size_t method_count = (ut64) meta->methodsSize / sizeof (Il2CppMethodDefinition);
|
||||
if (!method_count) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
// Try fast sampled window scan first instead of full sliding O(N^2)
|
||||
ut64 *candidates = R_NEWS (ut64, method_count);
|
||||
if (!candidates) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
bool found = false;
|
||||
for (int r = 0; r < data_n && !found; r++) {
|
||||
ut64 start = data_ranges[r].from;
|
||||
ut64 end = data_ranges[r].to;
|
||||
ut64 rsize = end - start;
|
||||
if (rsize < (ut64)ptrsz * 256) {
|
||||
continue;
|
||||
}
|
||||
ut8 *rbuf = R_NEWS (ut8, rsize);
|
||||
if (!rbuf) break;
|
||||
r_io_vread_at (core->io, start, rbuf, rsize);
|
||||
const ut64 sampleN = R_MIN ((ut64)512, (ut64)method_count);
|
||||
const ut64 step = (ut64)ptrsz * 4; // stride to reduce checks
|
||||
for (ut64 off = 0; off + sampleN * (ut64)ptrsz <= rsize; off += step) {
|
||||
ut32 good = 0, zero = 0;
|
||||
for (ut64 i = 0; i < sampleN; i++) {
|
||||
ut64 val = (ptrsz == 8)
|
||||
? RD_LE64 (rbuf + off + i * 8)
|
||||
: (ut64) RD_LE32 (rbuf + off + i * 4);
|
||||
if (!val) { zero++; continue; }
|
||||
if (val >= text_lo && val < text_hi) { good++; }
|
||||
}
|
||||
if (good > 0 && good + zero >= (ut32)(sampleN * 9 / 10)) {
|
||||
// Likely candidate; fetch full array from IO once
|
||||
ut64 addr = start + off;
|
||||
ut64 bytes = (ut64)method_count * (ut64)ptrsz;
|
||||
ut8 *tmp = R_NEWS (ut8, bytes);
|
||||
if (!tmp) { continue; }
|
||||
st64 nr = r_io_vread_at (core->io, addr, tmp, bytes);
|
||||
if (nr > 0) {
|
||||
size_t total = (size_t)((ut64)nr / (ut64)ptrsz);
|
||||
if (total > method_count) total = method_count;
|
||||
ut32 g2 = 0, z2 = 0;
|
||||
for (size_t i = 0; i < total; i++) {
|
||||
ut64 val = (ptrsz == 8)
|
||||
? RD_LE64 (tmp + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (tmp + (ut64)i * 4);
|
||||
candidates[i] = val;
|
||||
if (!val) { z2++; continue; }
|
||||
if (val >= text_lo && val < text_hi) { g2++; }
|
||||
}
|
||||
for (size_t i = total; i < method_count; i++) candidates[i] = 0;
|
||||
if (total > 0 && g2 > 0 && g2 + z2 >= (ut32)(total * 9 / 10)) {
|
||||
found = true;
|
||||
R_FREE (tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
R_FREE (tmp);
|
||||
}
|
||||
}
|
||||
R_FREE (rbuf);
|
||||
}
|
||||
if (!found) {
|
||||
// Try to identify CodeRegistration-like pattern: [count (u32), padding (u32), pointer (ptrsz)]
|
||||
ut64 gmp_ptr = 0;
|
||||
size_t gmp_cnt = 0;
|
||||
for (int r = 0; r < data_n && !found; r++) {
|
||||
ut64 start = data_ranges[r].from;
|
||||
ut64 end = data_ranges[r].to;
|
||||
ut64 rsize = end - start;
|
||||
ut8 *rbuf = R_NEWS (ut8, rsize);
|
||||
if (!rbuf) break;
|
||||
r_io_vread_at (core->io, start, rbuf, rsize);
|
||||
for (ut64 off = 0; off + (ut64)(ptrsz + 8) <= rsize; off += 4) {
|
||||
ut32 cnt32 = RD_LE32 (rbuf + off);
|
||||
if (cnt32 < 32) continue;
|
||||
ut64 arrptr = (ptrsz == 8)
|
||||
? RD_LE64 (rbuf + off + 8)
|
||||
: (ut64) RD_LE32 (rbuf + off + 4);
|
||||
// Check arrptr lies in a readable range
|
||||
bool in_r = false;
|
||||
for (int rr = 0; rr < data_n; rr++) {
|
||||
if (arrptr >= data_ranges[rr].from && arrptr < data_ranges[rr].to) {
|
||||
in_r = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!in_r) continue;
|
||||
// Sample first up to 256 entries with one bulk read
|
||||
ut32 good = 0, seen = 0;
|
||||
ut32 sample = R_MIN ((ut32)256, cnt32);
|
||||
ut64 sbytes = (ut64)sample * (ut64)ptrsz;
|
||||
ut8 *sbuf = R_NEWS (ut8, sbytes);
|
||||
if (!sbuf) continue;
|
||||
st64 sread = r_io_vread_at (core->io, arrptr, sbuf, sbytes);
|
||||
if (sread <= 0) { R_FREE (sbuf); continue; }
|
||||
ut32 sitems = (ut32)((ut64)sread / (ut64)ptrsz);
|
||||
if (sitems > sample) sitems = sample;
|
||||
for (ut32 i = 0; i < sitems; i++) {
|
||||
ut64 val = (ptrsz == 8)
|
||||
? RD_LE64 (sbuf + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (sbuf + (ut64)i * 4);
|
||||
if (val) seen++;
|
||||
if (val >= text_lo && val < text_hi) good++;
|
||||
}
|
||||
R_FREE (sbuf);
|
||||
if (sitems >= 32 && good >= 8 && seen >= 8) {
|
||||
if (g_debug) fprintf (stderr, "[r2unity] gmp-like arrptr=0x%"PFMT64x" cnt=%u sample=%u good=%u seen=%u\n", arrptr, cnt32, sitems, good, seen);
|
||||
gmp_ptr = arrptr;
|
||||
gmp_cnt = cnt32;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
R_FREE (rbuf);
|
||||
}
|
||||
if (found && gmp_ptr) {
|
||||
// Read array at gmp_ptr
|
||||
size_t maxc = method_count;
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
ut64 bytes = (ut64) R_MIN (maxc, gmp_cnt) * (ut64) ptrsz;
|
||||
ut8 *tmp = R_NEWS (ut8, bytes);
|
||||
if (tmp) {
|
||||
st64 nr = r_io_vread_at (core->io, gmp_ptr, tmp, bytes);
|
||||
size_t total = (nr > 0)? (size_t)((ut64)nr / (ut64)ptrsz): 0;
|
||||
if (total > R_MIN (maxc, gmp_cnt)) total = R_MIN (maxc, gmp_cnt);
|
||||
for (size_t i = 0; i < total; i++) {
|
||||
candidates[i] = (ptrsz == 8)
|
||||
? RD_LE64 (tmp + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (tmp + (ut64)i * 4);
|
||||
}
|
||||
R_FREE (tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// Fallback: find the longest run of pointers into text (or zero) – use as prefix
|
||||
ut64 best_start = 0;
|
||||
size_t best_len = 0;
|
||||
size_t max_probe = R_MIN ((size_t)65536, (size_t)method_count);
|
||||
for (int r = 0; r < data_n; r++) {
|
||||
ut64 start = data_ranges[r].from;
|
||||
ut64 end = data_ranges[r].to;
|
||||
ut64 rsize = end - start;
|
||||
ut8 *rbuf = R_NEWS (ut8, rsize);
|
||||
if (!rbuf) break;
|
||||
r_io_vread_at (core->io, start, rbuf, rsize);
|
||||
for (ut64 off = 0; off + (ut64)(ptrsz * 16) <= rsize; off += ptrsz) {
|
||||
size_t run = 0;
|
||||
for (; run < max_probe; run++) {
|
||||
ut64 val = (ptrsz == 8)
|
||||
? RD_LE64 (rbuf + off + (ut64)run * 8)
|
||||
: (ut64) RD_LE32 (rbuf + off + (ut64)run * 4);
|
||||
if (!val) {
|
||||
// allow zeros in the run
|
||||
continue;
|
||||
}
|
||||
if (!(val >= text_lo && val < text_hi)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (run > best_len) {
|
||||
best_len = run;
|
||||
best_start = start + off;
|
||||
}
|
||||
}
|
||||
R_FREE (rbuf);
|
||||
}
|
||||
if (best_len > 0) {
|
||||
if (g_debug) fprintf (stderr, "[r2unity] fallback run best_start=0x%"PFMT64x" best_len=%u\n", best_start, (unsigned)best_len);
|
||||
// Read best_len pointers in one shot into prefix of candidates, rest zeros
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
ut64 bytes = (ut64)best_len * (ut64)ptrsz;
|
||||
ut8 *tmp = R_NEWS (ut8, bytes);
|
||||
if (tmp) {
|
||||
st64 nr = r_io_vread_at (core->io, best_start, tmp, bytes);
|
||||
if (nr > 0) {
|
||||
size_t total = (size_t)((ut64)nr / (ut64)ptrsz);
|
||||
if (total > best_len) total = best_len;
|
||||
for (size_t i = 0; i < total; i++) {
|
||||
candidates[i] = (ptrsz == 8)
|
||||
? RD_LE64 (tmp + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (tmp + (ut64)i * 4);
|
||||
}
|
||||
}
|
||||
R_FREE (tmp);
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
R_FREE (candidates);
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
// Sanitize: zero out any pointer not pointing into text
|
||||
for (size_t i = 0; i < method_count; i++) {
|
||||
ut64 v = candidates[i];
|
||||
if (!v) continue;
|
||||
if (!(v >= text_lo && v < text_hi)) {
|
||||
candidates[i] = 0;
|
||||
}
|
||||
}
|
||||
*out_ptrs = candidates;
|
||||
r_core_free (core);
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API bool r2unity_read_method_pointers_at (R2UnityMetadata *meta, const char *exe_path, ut64 addr, size_t count, ut64 **out_ptrs) {
|
||||
if (!meta || !exe_path || !out_ptrs || !addr) {
|
||||
return false;
|
||||
}
|
||||
*out_ptrs = NULL;
|
||||
RCore *core = r_core_new ();
|
||||
if (!core) {
|
||||
return false;
|
||||
}
|
||||
r_core_cmd0 (core, "e bin.cache=true");
|
||||
r_core_cmd0 (core, "e bin.relocs.apply=true");
|
||||
if (!r_core_file_open (core, exe_path, 0, 0)) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
if (!r_core_bin_load (core, NULL, 0)) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
int ptrsz = 8;
|
||||
char *ij = r_core_cmd_str (core, "ij");
|
||||
if (ij) {
|
||||
RJson *pj = r_json_parse (ij);
|
||||
if (pj) {
|
||||
const RJson *pbin = r_json_get (pj, "bin");
|
||||
if (pbin) {
|
||||
st64 bits = r_json_get_num (pbin, "bits");
|
||||
ptrsz = (bits == 32)? 4: 8;
|
||||
}
|
||||
r_json_free (pj);
|
||||
}
|
||||
free (ij);
|
||||
}
|
||||
size_t method_count = (ut64) meta->methodsSize / sizeof (Il2CppMethodDefinition);
|
||||
if (!method_count) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
if (!count || count > method_count) {
|
||||
count = method_count;
|
||||
}
|
||||
ut64 *ptrs = R_NEWS (ut64, method_count);
|
||||
if (!ptrs) {
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
memset (ptrs, 0, method_count * sizeof (ut64));
|
||||
ut64 bytes = (ut64) count * (ut64) ptrsz;
|
||||
ut8 *tmp = R_NEWS (ut8, bytes);
|
||||
if (tmp) {
|
||||
st64 nr = r_io_vread_at (core->io, addr, tmp, bytes);
|
||||
size_t total = (nr > 0)? (size_t)((ut64)nr / (ut64)ptrsz): 0;
|
||||
if (total > count) total = count;
|
||||
for (size_t i = 0; i < total; i++) {
|
||||
ptrs[i] = (ptrsz == 8)
|
||||
? RD_LE64 (tmp + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (tmp + (ut64)i * 4);
|
||||
}
|
||||
R_FREE (tmp);
|
||||
}
|
||||
// Sanitize out-of-text pointers
|
||||
ut64 text_lo = UT64_MAX, text_hi = 0;
|
||||
char *omj = r_core_cmd_str (core, "omj");
|
||||
RJson *jom = omj? r_json_parse (omj): NULL;
|
||||
if (jom && jom->type == R_JSON_ARRAY) {
|
||||
for (size_t i = 0; i < jom->children.count; i++) {
|
||||
const RJson *item = r_json_item (jom, i);
|
||||
if (!item) continue;
|
||||
const char *perm = r_json_get_str (item, "perm");
|
||||
ut64 from = (ut64) r_json_get_num (item, "from");
|
||||
ut64 to = (ut64) r_json_get_num (item, "to");
|
||||
if (!perm) continue;
|
||||
if (strchr (perm, 'x')) {
|
||||
if (from < text_lo) text_lo = from;
|
||||
if (to > text_hi) text_hi = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jom) r_json_free (jom);
|
||||
free (omj);
|
||||
if (text_lo != UT64_MAX && text_hi > text_lo) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ut64 v = ptrs[i];
|
||||
if (!v) continue;
|
||||
if (!(v >= text_lo && v < text_hi)) ptrs[i] = 0;
|
||||
}
|
||||
}
|
||||
*out_ptrs = ptrs;
|
||||
r_core_free (core);
|
||||
return true;
|
||||
(void) meta; (void) exe_path; (void) addr; (void) count; (void) out_ptrs;
|
||||
return false;
|
||||
}
|
||||
+1
-1
@@ -296,7 +296,7 @@ R_API const char *r2unity_get_string (R2UnityMetadata *meta, uint32_t index);
|
||||
R_API Il2CppTypeDefinition *r2unity_get_type_definitions (R2UnityMetadata *meta, size_t *count);
|
||||
R_API Il2CppMethodDefinition *r2unity_get_method_definitions (R2UnityMetadata *meta, size_t *count);
|
||||
R_API Il2CppImageDefinition *r2unity_get_images (R2UnityMetadata *meta, size_t *count);
|
||||
R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_path, ut64 **out_ptrs);
|
||||
/* Simplified API: use format-specific finders, or manual read stub. */
|
||||
R_API bool r2unity_read_method_pointers_at (R2UnityMetadata *meta, const char *exe_path, ut64 addr, size_t count, ut64 **out_ptrs);
|
||||
R_API bool r2unity_find_method_pointers_macho (R2UnityMetadata *meta, const char *macho_path, ut64 **out_ptrs);
|
||||
R_API bool r2unity_find_method_pointers_elf (R2UnityMetadata *meta, const char *elf_path, ut64 **out_ptrs);
|
||||
|
||||
@@ -168,6 +168,44 @@ R_API bool r2unity_find_method_pointers_macho (R2UnityMetadata *meta, const char
|
||||
bool found = false;
|
||||
int ptrsz = 8;
|
||||
ut32 expected = (ut32) R_MAX ((ut64)64, (ut64) ((ut64) meta->methodsSize / sizeof (Il2CppMethodDefinition)));
|
||||
// Try CodeRegistration-like pairs first
|
||||
for (int i = 0; i < mo.nsegs && !found; i++) {
|
||||
MachSeg *s = &mo.segs[i];
|
||||
bool is_data = (s->maxprot & 0x1) && !(s->maxprot & 0x4);
|
||||
if (!is_data || s->filesize < (16 + 8)) continue;
|
||||
const ut8 *buf = mo.file + mo.base + s->fileoff;
|
||||
ut64 sz = s->filesize;
|
||||
for (ut64 off = 0; off + 16 <= sz; off += 4) {
|
||||
ut32 cnt1 = RD_LE32 (buf + off);
|
||||
ut64 p1 = RD_LE64 (buf + off + 8);
|
||||
if (cnt1 < 32) continue;
|
||||
ut32 good = 0, seen = 0;
|
||||
ut32 sample = R_MIN ((ut32)128, cnt1);
|
||||
for (ut32 k = 0; k < sample; k++) {
|
||||
const ut8 *p = macho_vm_to_ptr (&mo, p1 + (ut64)k * 8);
|
||||
if (!p && mo.vm_base) { p = macho_vm_to_ptr (&mo, mo.vm_base + p1 + (ut64)k * 8); }
|
||||
if (!p) break;
|
||||
ut64 val = RD_LE64 (p);
|
||||
if (val) seen++;
|
||||
if (val < text_lo && mo.vm_base) val += mo.vm_base;
|
||||
if (val >= text_lo && val < text_hi) good++;
|
||||
}
|
||||
if (seen >= 8 && good >= 8) {
|
||||
if (r2unity_is_debug ()) fprintf (stderr, "[r2unity/macho] codeReg p1=0x%"PFMT64x" cnt1=%u\n", p1, cnt1);
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
for (size_t m = 0; m < method_count && m < cnt1; m++) {
|
||||
const ut8 *p = macho_vm_to_ptr (&mo, p1 + (ut64)m * 8);
|
||||
if (!p && mo.vm_base) { p = macho_vm_to_ptr (&mo, mo.vm_base + p1 + (ut64)m * 8); }
|
||||
if (!p) break;
|
||||
ut64 val = RD_LE64 (p);
|
||||
if (val < text_lo && mo.vm_base) val += mo.vm_base;
|
||||
if (val >= text_lo && val < text_hi) candidates[m] = val;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < mo.nsegs && !found; i++) {
|
||||
MachSeg *s = &mo.segs[i];
|
||||
bool is_data = (s->maxprot & 0x1) && !(s->maxprot & 0x4);
|
||||
|
||||
+69
-14
@@ -3,6 +3,57 @@
|
||||
#include <string.h>
|
||||
#include "lib/lib.h"
|
||||
|
||||
static void build_method_attrs_string (char *o, size_t osz, unsigned int flags) {
|
||||
/* System.Reflection.MethodAttributes subset */
|
||||
const unsigned int MemberAccessMask = 0x0007;
|
||||
const unsigned int MdPrivate = 0x0001;
|
||||
const unsigned int MdFamANDAssem = 0x0002; /* private protected */
|
||||
const unsigned int MdAssembly = 0x0003; /* internal */
|
||||
const unsigned int MdFamily = 0x0004; /* protected */
|
||||
const unsigned int MdFamORAssem = 0x0005; /* protected internal */
|
||||
const unsigned int MdPublic = 0x0006;
|
||||
const unsigned int MdStatic = 0x0010;
|
||||
const unsigned int MdFinal = 0x0020;
|
||||
const unsigned int MdVirtual = 0x0040;
|
||||
const unsigned int MdAbstract = 0x0400;
|
||||
const char *vis = "";
|
||||
switch (flags & MemberAccessMask) {
|
||||
case MdPublic: vis = "public"; break;
|
||||
case MdFamily: vis = "protected"; break;
|
||||
case MdAssembly: vis = "internal"; break;
|
||||
case MdFamORAssem: vis = "protected internal"; break;
|
||||
case MdFamANDAssem: vis = "private protected"; break;
|
||||
case MdPrivate: vis = "private"; break;
|
||||
default: vis = ""; break;
|
||||
}
|
||||
char tmp[128]; tmp[0] = 0;
|
||||
if (*vis) {
|
||||
strncat (tmp, vis, sizeof (tmp) - 1);
|
||||
}
|
||||
if (flags & MdStatic) {
|
||||
if (*tmp) strncat (tmp, " ", sizeof (tmp) - 1);
|
||||
strncat (tmp, "static", sizeof (tmp) - 1);
|
||||
}
|
||||
if (flags & MdAbstract) {
|
||||
if (*tmp) strncat (tmp, " ", sizeof (tmp) - 1);
|
||||
strncat (tmp, "abstract", sizeof (tmp) - 1);
|
||||
}
|
||||
if (flags & MdVirtual) {
|
||||
if (*tmp) strncat (tmp, " ", sizeof (tmp) - 1);
|
||||
strncat (tmp, "virtual", sizeof (tmp) - 1);
|
||||
}
|
||||
if (flags & MdFinal) {
|
||||
if (*tmp) strncat (tmp, " ", sizeof (tmp) - 1);
|
||||
strncat (tmp, "final", sizeof (tmp) - 1);
|
||||
}
|
||||
if (!*tmp) {
|
||||
strncpy (o, "", osz);
|
||||
} else {
|
||||
strncpy (o, tmp, osz);
|
||||
o[osz? osz - 1: 0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usage (const char *prog_name) {
|
||||
fprintf (stderr, "Usage: %s [--json-one-line] [--quiet] [-l N] [--fast] [--gmp-addr 0xADDR] [--gmp-count N] [-v|--debug] <executable> <path/to/global-metadata.dat>\n", prog_name);
|
||||
}
|
||||
@@ -91,22 +142,13 @@ int main (int argc, char *argv[]) {
|
||||
ut32 m = (ut32)magic[0] | ((ut32)magic[1] << 8) | ((ut32)magic[2] << 16) | ((ut32)magic[3] << 24);
|
||||
if (magic[0] == 0x7f && magic[1] == 'E' && magic[2] == 'L' && magic[3] == 'F') {
|
||||
has_ptrs = r2unity_find_method_pointers_elf (meta, exe_path, &method_ptrs);
|
||||
if (!has_ptrs) {
|
||||
/* Fallback to r2-based */
|
||||
has_ptrs = r2unity_find_method_pointers (meta, exe_path, &method_ptrs);
|
||||
}
|
||||
} else if (m == 0xfeedfacf || m == 0xcffaedfe || m == 0xcafebabe || m == 0xbebafeca) {
|
||||
has_ptrs = r2unity_find_method_pointers_macho (meta, exe_path, &method_ptrs);
|
||||
if (!has_ptrs) {
|
||||
/* Fallback to r2-based */
|
||||
has_ptrs = r2unity_find_method_pointers (meta, exe_path, &method_ptrs);
|
||||
}
|
||||
} else {
|
||||
/* Fallback to r2-based */
|
||||
has_ptrs = r2unity_find_method_pointers (meta, exe_path, &method_ptrs);
|
||||
has_ptrs = false;
|
||||
}
|
||||
} else {
|
||||
has_ptrs = r2unity_find_method_pointers (meta, exe_path, &method_ptrs);
|
||||
has_ptrs = false;
|
||||
}
|
||||
// Recompute has_ptrs by checking any non-zero entry
|
||||
if (method_ptrs) {
|
||||
@@ -192,15 +234,28 @@ int main (int argc, char *argv[]) {
|
||||
char *im = (char *) r2unity_get_string (meta, images[ii].nameIndex);
|
||||
if (im && *im) {
|
||||
printf ("'@0x%"PFMT64x"'f sym.unity.%s.%s\n", addr, im, fullname);
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: [%s] %s\n", addr, im, fullname);
|
||||
char attrs[128]; attrs[0] = 0; build_method_attrs_string (attrs, sizeof (attrs), m->flags);
|
||||
if (*attrs) {
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: [%s] %s %s\n", addr, im, attrs, fullname);
|
||||
} else {
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: [%s] %s\n", addr, im, fullname);
|
||||
}
|
||||
free (im);
|
||||
} else {
|
||||
printf ("'@0x%"PFMT64x"'f sym.unity.%s\n", addr, fullname);
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: %s\n", addr, fullname);
|
||||
char attrs2[128]; attrs2[0] = 0; build_method_attrs_string (attrs2, sizeof (attrs2), m->flags);
|
||||
if (*attrs2) {
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: %s %s\n", addr, attrs2, fullname);
|
||||
} else {
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: %s\n", addr, fullname);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf ("'@0x%"PFMT64x"'f sym.unity.%s\n", addr, fullname);
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: %s\n", addr, fullname);
|
||||
char attrs3[128]; attrs3[0] = 0; build_method_attrs_string (attrs3, sizeof (attrs3), m->flags);
|
||||
if (*attrs3) {
|
||||
printf ("'@0x%"PFMT64x"'CCu Method: %s\n", addr, attrs3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf ("# %s\n", fullname);
|
||||
|
||||
Reference in new issue
Block a user