mirror of
https://github.com/radareorg/r2unity.git
synced 2026-11-07 06:36:00 -10:00
Move elf/macho parsers into separate files
This commit is contained in:
1 parent
2c8352928f
commit
3af512645b
5 files changed
+510
-234
No files matched your search
+246
@@ -0,0 +1,246 @@
|
||||
// Fast ELF parser for r2unity
|
||||
#include "lib.h"
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static inline ut32 RD_LE32 (const ut8 *p) {
|
||||
return ((ut32)p[0]) | ((ut32)p[1] << 8) | ((ut32)p[2] << 16) | ((ut32)p[3] << 24);
|
||||
}
|
||||
static inline ut64 RD_LE64 (const ut8 *p) {
|
||||
return ((ut64)p[0]) | ((ut64)p[1] << 8) | ((ut64)p[2] << 16) | ((ut64)p[3] << 24) |
|
||||
((ut64)p[4] << 32) | ((ut64)p[5] << 40) | ((ut64)p[6] << 48) | ((ut64)p[7] << 56);
|
||||
}
|
||||
static inline ut16 RD_LE16 (const ut8 *p) {
|
||||
return (ut16)((ut16)p[0] | ((ut16)p[1] << 8));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ut64 vaddr;
|
||||
ut64 memsz;
|
||||
ut64 offset;
|
||||
ut64 filesz;
|
||||
ut32 flags;
|
||||
ut32 p_type;
|
||||
} ElfSeg;
|
||||
|
||||
typedef struct {
|
||||
ut8 *file;
|
||||
ut64 filesize;
|
||||
int is64;
|
||||
int le;
|
||||
ElfSeg segs[128];
|
||||
int nsegs;
|
||||
} ElfImg;
|
||||
|
||||
static bool elf_load (const char *path, ElfImg *e) {
|
||||
memset (e, 0, sizeof (*e));
|
||||
int fd = open (path, O_RDONLY);
|
||||
if (fd < 0) return false;
|
||||
struct stat st;
|
||||
if (fstat (fd, &st) != 0 || st.st_size <= 0) { close (fd); return false; }
|
||||
e->filesize = (ut64) st.st_size;
|
||||
e->file = R_NEWS (ut8, e->filesize);
|
||||
if (!e->file) { close (fd); return false; }
|
||||
ssize_t rd = read (fd, e->file, (size_t) e->filesize);
|
||||
close (fd);
|
||||
if (rd < 0 || (ut64) rd != e->filesize) { R_FREE (e->file); return false; }
|
||||
const ut8 *p = e->file;
|
||||
if (!(p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[3] == 'F')) { R_FREE (e->file); return false; }
|
||||
e->is64 = (p[4] == 2);
|
||||
e->le = (p[5] == 1);
|
||||
if (!e->le) { R_FREE (e->file); return false; }
|
||||
if (e->is64) {
|
||||
ut64 phoff = RD_LE64 (p + 0x20);
|
||||
ut16 phentsize = RD_LE16 (p + 0x36);
|
||||
ut16 phnum = RD_LE16 (p + 0x38);
|
||||
for (ut16 i = 0; i < phnum && e->nsegs < (int)(sizeof (e->segs)/sizeof (e->segs[0])); i++) {
|
||||
ut64 off = phoff + (ut64) i * phentsize;
|
||||
if (off + 56 > e->filesize) break;
|
||||
const ut8 *ph = p + off;
|
||||
ut32 p_type = RD_LE32 (ph + 0);
|
||||
ut32 p_flags = RD_LE32 (ph + 4);
|
||||
ut64 p_offset = RD_LE64 (ph + 8);
|
||||
ut64 p_vaddr = RD_LE64 (ph + 16);
|
||||
ut64 p_filesz = RD_LE64 (ph + 32);
|
||||
ut64 p_memsz = RD_LE64 (ph + 40);
|
||||
if (p_type == 1 || p_type == 2) {
|
||||
ElfSeg *s = &e->segs[e->nsegs++];
|
||||
s->vaddr = p_vaddr; s->memsz = p_memsz; s->offset = p_offset; s->filesz = p_filesz; s->flags = p_flags; s->p_type = p_type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ut32 phoff = RD_LE32 (p + 0x1C);
|
||||
ut16 phentsize = RD_LE16 (p + 0x2A);
|
||||
ut16 phnum = RD_LE16 (p + 0x2C);
|
||||
for (ut16 i = 0; i < phnum && e->nsegs < (int)(sizeof (e->segs)/sizeof (e->segs[0])); i++) {
|
||||
ut64 off = (ut64) phoff + (ut64) i * phentsize;
|
||||
if (off + 32 > e->filesize) break;
|
||||
const ut8 *ph = p + off;
|
||||
ut32 p_type = RD_LE32 (ph + 0);
|
||||
ut32 p_offset = RD_LE32 (ph + 4);
|
||||
ut32 p_vaddr = RD_LE32 (ph + 8);
|
||||
ut32 p_filesz = RD_LE32 (ph + 16);
|
||||
ut32 p_memsz = RD_LE32 (ph + 20);
|
||||
ut32 p_flags = RD_LE32 (ph + 24);
|
||||
if (p_type == 1 || p_type == 2) {
|
||||
ElfSeg *s = &e->segs[e->nsegs++];
|
||||
s->vaddr = p_vaddr; s->memsz = p_memsz; s->offset = p_offset; s->filesz = p_filesz; s->flags = p_flags; s->p_type = p_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void elf_free (ElfImg *e) { R_FREE (e->file); }
|
||||
|
||||
static inline const ut8 *elf_vm_to_ptr (ElfImg *e, ut64 vaddr) {
|
||||
for (int i = 0; i < e->nsegs; i++) {
|
||||
const ElfSeg *s = &e->segs[i];
|
||||
if (vaddr >= s->vaddr && vaddr < s->vaddr + s->memsz) {
|
||||
ut64 delta = vaddr - s->vaddr;
|
||||
if (delta < s->filesz && s->offset + delta < e->filesize) {
|
||||
return e->file + s->offset + delta;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
R_API bool r2unity_find_method_pointers_elf (R2UnityMetadata *meta, const char *elf_path, ut64 **out_ptrs) {
|
||||
if (!meta || !elf_path || !out_ptrs) return false;
|
||||
*out_ptrs = NULL;
|
||||
ElfImg e;
|
||||
if (!elf_load (elf_path, &e)) return false;
|
||||
size_t method_count = meta->header.v24.methodsSize / sizeof (Il2CppMethodDefinition);
|
||||
if (!method_count) { elf_free (&e); return false; }
|
||||
ut64 base_vaddr = UT64_MAX, text_lo = UT64_MAX, text_hi = 0;
|
||||
for (int i = 0; i < e.nsegs; i++) {
|
||||
const ElfSeg *s = &e.segs[i];
|
||||
if (s->vaddr < base_vaddr) base_vaddr = s->vaddr;
|
||||
if ((s->flags & 0x1) && (s->flags & 0x4)) {
|
||||
if (s->vaddr < text_lo) text_lo = s->vaddr;
|
||||
if (s->vaddr + s->memsz > text_hi) text_hi = s->vaddr + s->memsz;
|
||||
}
|
||||
}
|
||||
if (text_lo == UT64_MAX || text_hi <= text_lo) { elf_free (&e); return false; }
|
||||
ut64 *candidates = R_NEWS (ut64, method_count);
|
||||
if (!candidates) { elf_free (&e); return false; }
|
||||
bool found = false;
|
||||
int ptrsz = e.is64 ? 8 : 4;
|
||||
// Apply REL(A)/RELR (relative only)
|
||||
ut64 dyn_off = 0, dyn_sz = 0;
|
||||
for (int i = 0; i < e.nsegs; i++) { const ElfSeg *s = &e.segs[i]; if (s->p_type == 2) { dyn_off = s->offset; dyn_sz = s->filesz; break; } }
|
||||
ut64 rela_off = 0, rela_sz = 0, rela_ent = e.is64? 24: 12;
|
||||
ut64 rel_off = 0, rel_sz = 0, rel_ent = e.is64? 16: 8;
|
||||
ut64 relr_off = 0, relr_sz = 0, relr_ent = e.is64? 8: 4;
|
||||
ut64 base = base_vaddr;
|
||||
if (dyn_off && dyn_sz) {
|
||||
const ut8 *d = e.file + dyn_off;
|
||||
for (ut64 off = 0; off + (e.is64? 16: 8) <= dyn_sz; off += (e.is64? 16: 8)) {
|
||||
ut64 tag = e.is64 ? RD_LE64 (d + off) : (ut64) RD_LE32 (d + off);
|
||||
ut64 val = e.is64 ? RD_LE64 (d + off + 8) : (ut64) RD_LE32 (d + off + 4);
|
||||
if (tag == 0) break;
|
||||
if (tag == 7) rela_off = val; else if (tag == 8) rela_sz = val; else if (tag == 9) rela_ent = val;
|
||||
else if (tag == 17) rel_off = val; else if (tag == 18) rel_sz = val; else if (tag == 19) rel_ent = val;
|
||||
else if (tag == 36) relr_off = val; else if (tag == 35) relr_sz = val; else if (tag == 37) relr_ent = val;
|
||||
}
|
||||
}
|
||||
if (rela_off && rela_sz && rela_ent) {
|
||||
for (ut64 i = 0; i + rela_ent <= rela_sz; i += rela_ent) {
|
||||
const ut8 *rp = elf_vm_to_ptr (&e, rela_off + i); if (!rp) break;
|
||||
ut64 r_offset = e.is64 ? RD_LE64 (rp + 0) : (ut64) RD_LE32 (rp + 0);
|
||||
ut64 r_info = e.is64 ? RD_LE64 (rp + 8) : (ut64) RD_LE32 (rp + 4);
|
||||
ut64 r_addend = e.is64 ? RD_LE64 (rp + 16) : (ut64) RD_LE32 (rp + 8);
|
||||
ut64 type = e.is64 ? (r_info & 0xffffffffULL) : (r_info & 0xffULL);
|
||||
bool is_relative = (type == 8) || (type == 23) || (type == 1027) || (type == 8);
|
||||
if (!is_relative) continue;
|
||||
ut8 *loc = (ut8 *) elf_vm_to_ptr (&e, r_offset); if (!loc) continue;
|
||||
ut64 newv = base + r_addend;
|
||||
if (e.is64) { for (int b = 0; b < 8; b++) loc[b] = (ut8)((newv >> (8*b)) & 0xff); }
|
||||
else { ut32 v32 = (ut32) newv; for (int b = 0; b < 4; b++) loc[b] = (ut8)((v32 >> (8*b)) & 0xff); }
|
||||
}
|
||||
}
|
||||
if (rel_off && rel_sz && rel_ent) {
|
||||
for (ut64 i = 0; i + rel_ent <= rel_sz; i += rel_ent) {
|
||||
const ut8 *rp = elf_vm_to_ptr (&e, rel_off + i); if (!rp) break;
|
||||
ut64 r_offset = e.is64 ? RD_LE64 (rp + 0) : (ut64) RD_LE32 (rp + 0);
|
||||
ut64 r_info = e.is64 ? RD_LE64 (rp + 8) : (ut64) RD_LE32 (rp + 4);
|
||||
ut64 type = e.is64 ? (r_info & 0xffffffffULL) : (r_info & 0xffULL);
|
||||
bool is_relative = (type == 8) || (type == 23) || (type == 1027) || (type == 8);
|
||||
if (!is_relative) continue;
|
||||
ut8 *loc = (ut8 *) elf_vm_to_ptr (&e, r_offset); if (!loc) continue;
|
||||
ut64 add = e.is64 ? RD_LE64 (loc) : (ut64) RD_LE32 (loc);
|
||||
ut64 newv = base + add;
|
||||
if (e.is64) { for (int b = 0; b < 8; b++) loc[b] = (ut8)((newv >> (8*b)) & 0xff); }
|
||||
else { ut32 v32 = (ut32) newv; for (int b = 0; b < 4; b++) loc[b] = (ut8)((v32 >> (8*b)) & 0xff); }
|
||||
}
|
||||
}
|
||||
if (e.is64 && relr_off && relr_sz && relr_ent) {
|
||||
ut64 curr = 0;
|
||||
for (ut64 i = 0; i + relr_ent <= relr_sz; i += relr_ent) {
|
||||
const ut8 *rp = elf_vm_to_ptr (&e, relr_off + i); if (!rp) break;
|
||||
ut64 R = RD_LE64 (rp);
|
||||
if ((R & 1ULL) == 0) {
|
||||
ut64 addr = R; ut8 *loc = (ut8 *) elf_vm_to_ptr (&e, addr);
|
||||
if (loc) { ut64 add = RD_LE64 (loc); ut64 newv = base + add; for (int b = 0; b < 8; b++) loc[b] = (ut8)((newv >> (8*b)) & 0xff); }
|
||||
curr = addr + 8;
|
||||
} else {
|
||||
ut64 bitmap = R >> 1;
|
||||
for (int bit = 0; bit < 63; bit++) if (bitmap & (1ULL << bit)) {
|
||||
ut64 addr = curr + (ut64)(bit + 1) * 8ULL;
|
||||
ut8 *loc = (ut8 *) elf_vm_to_ptr (&e, addr); if (!loc) continue;
|
||||
ut64 add = RD_LE64 (loc); ut64 newv = base + add; for (int b = 0; b < 8; b++) loc[b] = (ut8)((newv >> (8*b)) & 0xff);
|
||||
}
|
||||
curr += 8ULL * 63ULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Scan for [count][ptr]
|
||||
ut32 expected = (ut32) (meta->header.v24.methodsSize / 32);
|
||||
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)(8 + ptrsz)) continue;
|
||||
const ut8 *buf = e.file + s->offset;
|
||||
ut64 sz = s->filesz;
|
||||
for (ut64 off = 0; off + (ut64)(8 + ptrsz) <= sz; off += 4) {
|
||||
ut32 cnt32 = RD_LE32 (buf + off);
|
||||
if (cnt32 == 0) continue;
|
||||
if (expected) { if (cnt32 < expected / 4 || cnt32 > expected * 2) continue; } else { if (cnt32 < 64) continue; }
|
||||
ut64 arrptr = (ptrsz == 8) ? RD_LE64 (buf + off + 8) : (ut64) RD_LE32 (buf + off + 4);
|
||||
ut32 good = 0, seen = 0;
|
||||
ut32 sample = R_MIN ((ut32)128, cnt32);
|
||||
for (ut32 k = 0; k < sample; k++) {
|
||||
const ut8 *pp = elf_vm_to_ptr (&e, arrptr + (ut64)k * (ut64)ptrsz);
|
||||
if (!pp && base_vaddr != UT64_MAX) { pp = elf_vm_to_ptr (&e, base_vaddr + arrptr + (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) || (base_vaddr != UT64_MAX && (val + base_vaddr) >= text_lo && (val + base_vaddr) < text_hi)) good++;
|
||||
}
|
||||
if (seen >= 8 && good >= 8) {
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
size_t tocopy = R_MIN ((size_t) cnt32, method_count);
|
||||
size_t in_text = 0;
|
||||
for (size_t m = 0; m < tocopy; m++) {
|
||||
const ut8 *pp = elf_vm_to_ptr (&e, arrptr + (ut64)m * (ut64)ptrsz);
|
||||
if (!pp && base_vaddr != UT64_MAX) { pp = elf_vm_to_ptr (&e, base_vaddr + arrptr + (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: (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
R_FREE (candidates); elf_free (&e); return false;
|
||||
}
|
||||
*out_ptrs = candidates; elf_free (&e); return true;
|
||||
}
|
||||
|
||||
+58
-233
@@ -283,7 +283,7 @@ R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_
|
||||
r_core_free (core);
|
||||
return false;
|
||||
}
|
||||
// Try strict match first: full method_count array
|
||||
// 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);
|
||||
@@ -294,32 +294,47 @@ R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_
|
||||
ut64 start = data_ranges[r].from;
|
||||
ut64 end = data_ranges[r].to;
|
||||
ut64 rsize = end - start;
|
||||
if (rsize < (ut64)method_count * (ut64)ptrsz) {
|
||||
if (rsize < (ut64)ptrsz * 256) {
|
||||
continue;
|
||||
}
|
||||
ut8 *rbuf = R_NEWS (ut8, rsize);
|
||||
if (!rbuf) {
|
||||
break;
|
||||
}
|
||||
if (!rbuf) break;
|
||||
r_io_read_at (core->io, start, rbuf, rsize);
|
||||
for (ut64 off = 0; off + ((ut64)method_count * (ut64)ptrsz) <= rsize; off += ptrsz) {
|
||||
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 (size_t i = 0; i < method_count; i++) {
|
||||
for (ut64 i = 0; i < sampleN; i++) {
|
||||
ut64 val = (ptrsz == 8)
|
||||
? RD_LE64 (rbuf + off + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (rbuf + off + (ut64)i * 4);
|
||||
candidates[i] = val;
|
||||
if (!val) {
|
||||
zero++;
|
||||
continue;
|
||||
}
|
||||
if (val >= text_lo && val < text_hi) {
|
||||
good++;
|
||||
}
|
||||
? 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)(method_count * 9 / 10)) {
|
||||
found = true;
|
||||
break;
|
||||
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_read_at (core->io, addr, tmp, bytes);
|
||||
if (nr > 0) {
|
||||
ut32 g2 = 0, z2 = 0;
|
||||
for (size_t i = 0; i < method_count; 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++; }
|
||||
}
|
||||
if (g2 > 0 && g2 + z2 >= (ut32)(method_count * 9 / 10)) {
|
||||
found = true;
|
||||
R_FREE (tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
R_FREE (tmp);
|
||||
}
|
||||
}
|
||||
R_FREE (rbuf);
|
||||
@@ -352,21 +367,22 @@ R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_
|
||||
}
|
||||
}
|
||||
if (!in_r) continue;
|
||||
// Sample first up to 256 entries
|
||||
// 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_read_at (core->io, arrptr, sbuf, sbytes);
|
||||
if (sread <= 0) { R_FREE (sbuf); continue; }
|
||||
for (ut32 i = 0; i < sample; i++) {
|
||||
ut64 val = 0;
|
||||
if (ptrsz == 8) {
|
||||
r_io_read_at (core->io, arrptr + (ut64)i * 8, (ut8 *)&val, 8);
|
||||
} else {
|
||||
ut32 v32 = 0;
|
||||
r_io_read_at (core->io, arrptr + (ut64)i * 4, (ut8 *)&v32, 4);
|
||||
val = v32;
|
||||
}
|
||||
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 (good >= 32 && seen >= 32) {
|
||||
gmp_ptr = arrptr;
|
||||
gmp_cnt = cnt32;
|
||||
@@ -427,18 +443,20 @@ R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_
|
||||
R_FREE (rbuf);
|
||||
}
|
||||
if (best_len > 0) {
|
||||
// Read best_len pointers into prefix of candidates, rest zeros
|
||||
// Read best_len pointers in one shot into prefix of candidates, rest zeros
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
for (size_t i = 0; i < best_len; i++) {
|
||||
ut64 val = 0;
|
||||
if (ptrsz == 8) {
|
||||
r_io_read_at (core->io, best_start + i * 8, (ut8 *)&val, 8);
|
||||
} else {
|
||||
ut32 v32 = 0;
|
||||
r_io_read_at (core->io, best_start + i * 4, (ut8 *)&v32, 4);
|
||||
val = v32;
|
||||
ut64 bytes = (ut64)best_len * (ut64)ptrsz;
|
||||
ut8 *tmp = R_NEWS (ut8, bytes);
|
||||
if (tmp) {
|
||||
st64 nr = r_io_read_at (core->io, best_start, tmp, bytes);
|
||||
if (nr > 0) {
|
||||
for (size_t i = 0; i < best_len; i++) {
|
||||
candidates[i] = (ptrsz == 8)
|
||||
? RD_LE64 (tmp + (ut64)i * 8)
|
||||
: (ut64) RD_LE32 (tmp + (ut64)i * 4);
|
||||
}
|
||||
}
|
||||
candidates[i] = val;
|
||||
R_FREE (tmp);
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
@@ -546,200 +564,7 @@ R_API bool r2unity_read_method_pointers_at (R2UnityMetadata *meta, const char *e
|
||||
if (!(v >= text_lo && v < text_hi)) ptrs[i] = 0;
|
||||
}
|
||||
}
|
||||
*out_ptrs = ptrs;
|
||||
*out_ptrs = ptrs;
|
||||
r_core_free (core);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Minimal Mach-O 64-bit + FAT parser to locate g_MethodPointers quickly
|
||||
typedef struct {
|
||||
ut64 vmaddr;
|
||||
ut64 vmsize;
|
||||
ut64 fileoff;
|
||||
ut64 filesize;
|
||||
ut32 maxprot;
|
||||
char segname[16];
|
||||
} MachSeg;
|
||||
|
||||
typedef struct {
|
||||
ut8 *file;
|
||||
ut64 filesize;
|
||||
ut64 base; // slice base offset
|
||||
ut32 ncmds;
|
||||
ut64 cmd_off;
|
||||
MachSeg segs[128];
|
||||
int nsegs;
|
||||
} MachO;
|
||||
|
||||
static inline ut32 RD_BE32 (const ut8 *p) {
|
||||
return ((ut32)p[3]) | ((ut32)p[2] << 8) | ((ut32)p[1] << 16) | ((ut32)p[0] << 24);
|
||||
}
|
||||
|
||||
static bool macho_load (const char *path, MachO *mo) {
|
||||
memset (mo, 0, sizeof (*mo));
|
||||
int fd = open (path, O_RDONLY);
|
||||
if (fd < 0) return false;
|
||||
struct stat st;
|
||||
if (fstat (fd, &st) != 0 || st.st_size <= 0) {
|
||||
close (fd);
|
||||
return false;
|
||||
}
|
||||
mo->filesize = (ut64) st.st_size;
|
||||
mo->file = R_NEWS (ut8, mo->filesize);
|
||||
if (!mo->file) {
|
||||
close (fd);
|
||||
return false;
|
||||
}
|
||||
ssize_t rd = read (fd, mo->file, (size_t) mo->filesize);
|
||||
close (fd);
|
||||
if (rd < 0 || (ut64) rd != mo->filesize) {
|
||||
R_FREE (mo->file);
|
||||
return false;
|
||||
}
|
||||
// Handle FAT
|
||||
ut32 magic = RD_BE32 (mo->file);
|
||||
ut64 off = 0;
|
||||
if (magic == 0xcafebabe) {
|
||||
ut32 nfat = RD_BE32 (mo->file + 4);
|
||||
// Choose first arm64 (cputype 0x100000c) or first slice
|
||||
ut32 best = 0;
|
||||
for (ut32 i = 0; i < nfat; i++) {
|
||||
const ut8 *fa = mo->file + 8 + i * 20; // fat_arch (not _64) is 20 bytes, but common on iOS
|
||||
ut32 cputype = RD_BE32 (fa + 0);
|
||||
if (cputype == 0x0100000c /* ARM64 */) { best = i; break; }
|
||||
}
|
||||
const ut8 *fa = mo->file + 8 + best * 20;
|
||||
off = RD_BE32 (fa + 8);
|
||||
// size = RD_BE32 (fa + 12);
|
||||
}
|
||||
mo->base = off;
|
||||
const ut8 *p = mo->file + off;
|
||||
ut32 mh_magic = *(const ut32 *) p;
|
||||
if (mh_magic != 0xFEEDFACF) {
|
||||
// Not 64-bit LE Mach-O
|
||||
R_FREE (mo->file);
|
||||
return false;
|
||||
}
|
||||
// mach_header_64
|
||||
ut32 ncmds = *(const ut32 *)(p + 0x10);
|
||||
mo->ncmds = ncmds;
|
||||
mo->cmd_off = off + 0x20;
|
||||
ut64 co = mo->cmd_off;
|
||||
for (ut32 i = 0; i < ncmds && (co + 8 <= mo->filesize); i++) {
|
||||
ut32 cmd = *(const ut32 *)(mo->file + co);
|
||||
ut32 cmdsize = *(const ut32 *)(mo->file + co + 4);
|
||||
if (cmd == 0x19 /* LC_SEGMENT_64 */ && cmdsize >= 72) {
|
||||
if (mo->nsegs < (int)(sizeof (mo->segs)/sizeof (mo->segs[0]))) {
|
||||
MachSeg *s = &mo->segs[mo->nsegs++];
|
||||
const ut8 *sp = mo->file + co + 8;
|
||||
memcpy (s->segname, sp, 16);
|
||||
s->vmaddr = *(const ut64 *)(sp + 16);
|
||||
s->vmsize = *(const ut64 *)(sp + 24);
|
||||
s->fileoff = *(const ut64 *)(sp + 32);
|
||||
s->filesize = *(const ut64 *)(sp + 40);
|
||||
s->maxprot = *(const ut32 *)(sp + 48);
|
||||
}
|
||||
}
|
||||
co += cmdsize ? cmdsize : 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void macho_free (MachO *mo) {
|
||||
R_FREE (mo->file);
|
||||
}
|
||||
|
||||
static inline bool macho_vm_in_text (MachO *mo, ut64 addr, ut64 *text_lo, ut64 *text_hi) {
|
||||
ut64 lo = UT64_MAX, hi = 0;
|
||||
for (int i = 0; i < mo->nsegs; i++) {
|
||||
MachSeg *s = &mo->segs[i];
|
||||
bool is_text = (s->maxprot & 0x4) || !strncmp (s->segname, "__TEXT", 6);
|
||||
if (is_text) {
|
||||
if (s->vmaddr < lo) lo = s->vmaddr;
|
||||
if (s->vmaddr + s->vmsize > hi) hi = s->vmaddr + s->vmsize;
|
||||
}
|
||||
}
|
||||
if (text_lo) *text_lo = lo;
|
||||
if (text_hi) *text_hi = hi;
|
||||
return (addr >= lo && addr < hi);
|
||||
}
|
||||
|
||||
static inline const ut8 *macho_vm_to_ptr (MachO *mo, ut64 vmaddr) {
|
||||
for (int i = 0; i < mo->nsegs; i++) {
|
||||
MachSeg *s = &mo->segs[i];
|
||||
if (vmaddr >= s->vmaddr && vmaddr < s->vmaddr + s->vmsize) {
|
||||
ut64 delta = vmaddr - s->vmaddr;
|
||||
if (delta < s->filesize) {
|
||||
return mo->file + mo->base + s->fileoff + delta;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
R_API bool r2unity_find_method_pointers_macho (R2UnityMetadata *meta, const char *macho_path, ut64 **out_ptrs) {
|
||||
if (!meta || !macho_path || !out_ptrs) return false;
|
||||
*out_ptrs = NULL;
|
||||
MachO mo;
|
||||
if (!macho_load (macho_path, &mo)) {
|
||||
return false;
|
||||
}
|
||||
size_t method_count = meta->header.v24.methodsSize / sizeof (Il2CppMethodDefinition);
|
||||
if (!method_count) {
|
||||
macho_free (&mo);
|
||||
return false;
|
||||
}
|
||||
ut64 text_lo = 0, text_hi = 0;
|
||||
macho_vm_in_text (&mo, 0, &text_lo, &text_hi);
|
||||
ut64 *candidates = R_NEWS (ut64, method_count);
|
||||
if (!candidates) {
|
||||
macho_free (&mo);
|
||||
return false;
|
||||
}
|
||||
bool found = false;
|
||||
int ptrsz = 8; // 64-bit
|
||||
// Fast CodeRegistration-like scan across readable non-exec segments
|
||||
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 < (8 + 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 cnt32 = RD_LE32 (buf + off);
|
||||
if (cnt32 < 1024 || cnt32 > (ut32)(method_count * 2)) continue;
|
||||
ut64 arrptr = RD_LE64 (buf + off + 8);
|
||||
// Sample within arrptr
|
||||
ut32 good = 0, seen = 0;
|
||||
ut32 sample = R_MIN ((ut32)256, cnt32);
|
||||
for (ut32 k = 0; k < sample; k++) {
|
||||
const ut8 *p = macho_vm_to_ptr (&mo, arrptr + (ut64)k * (ut64)ptrsz);
|
||||
if (!p) break;
|
||||
ut64 val = RD_LE64 (p);
|
||||
if (val) seen++;
|
||||
if (val >= text_lo && val < text_hi) good++;
|
||||
}
|
||||
if (seen >= 32 && good >= 32) {
|
||||
// Read full array
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
for (size_t m = 0; m < method_count && m < cnt32; m++) {
|
||||
const ut8 *p = macho_vm_to_ptr (&mo, arrptr + (ut64)m * (ut64)ptrsz);
|
||||
if (!p) break;
|
||||
ut64 val = RD_LE64 (p);
|
||||
if (val >= text_lo && val < text_hi) candidates[m] = val;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
R_FREE (candidates);
|
||||
macho_free (&mo);
|
||||
return false;
|
||||
}
|
||||
*out_ptrs = candidates;
|
||||
macho_free (&mo);
|
||||
return true;
|
||||
}
|
||||
@@ -276,5 +276,6 @@ R_API Il2CppMethodDefinition *r2unity_get_method_definitions (R2UnityMetadata *m
|
||||
R_API bool r2unity_find_method_pointers (R2UnityMetadata *meta, const char *exe_path, ut64 **out_ptrs);
|
||||
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);
|
||||
|
||||
#endif
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
// Fast Mach-O parser for r2unity
|
||||
#include "lib.h"
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static inline ut32 RD_LE32 (const ut8 *p) {
|
||||
return ((ut32)p[0]) | ((ut32)p[1] << 8) | ((ut32)p[2] << 16) | ((ut32)p[3] << 24);
|
||||
}
|
||||
static inline ut32 RD_BE32 (const ut8 *p) {
|
||||
return ((ut32)p[3]) | ((ut32)p[2] << 8) | ((ut32)p[1] << 16) | ((ut32)p[0] << 24);
|
||||
}
|
||||
static inline ut64 RD_LE64 (const ut8 *p) {
|
||||
return ((ut64)p[0]) | ((ut64)p[1] << 8) | ((ut64)p[2] << 16) | ((ut64)p[3] << 24) |
|
||||
((ut64)p[4] << 32) | ((ut64)p[5] << 40) | ((ut64)p[6] << 48) | ((ut64)p[7] << 56);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char segname[16];
|
||||
ut64 vmaddr;
|
||||
ut64 vmsize;
|
||||
ut64 fileoff;
|
||||
ut64 filesize;
|
||||
ut32 maxprot;
|
||||
} MachSeg;
|
||||
|
||||
typedef struct {
|
||||
ut8 *file;
|
||||
ut64 filesize;
|
||||
ut64 base;
|
||||
ut32 ncmds;
|
||||
ut64 cmd_off;
|
||||
MachSeg segs[128];
|
||||
int nsegs;
|
||||
} MachO;
|
||||
|
||||
static bool macho_load (const char *path, MachO *mo) {
|
||||
memset (mo, 0, sizeof (*mo));
|
||||
int fd = open (path, O_RDONLY);
|
||||
if (fd < 0) return false;
|
||||
struct stat st;
|
||||
if (fstat (fd, &st) != 0 || st.st_size <= 0) {
|
||||
close (fd);
|
||||
return false;
|
||||
}
|
||||
mo->filesize = (ut64) st.st_size;
|
||||
mo->file = R_NEWS (ut8, mo->filesize);
|
||||
if (!mo->file) {
|
||||
close (fd);
|
||||
return false;
|
||||
}
|
||||
ssize_t rd = read (fd, mo->file, (size_t) mo->filesize);
|
||||
close (fd);
|
||||
if (rd < 0 || (ut64) rd != mo->filesize) {
|
||||
R_FREE (mo->file);
|
||||
return false;
|
||||
}
|
||||
ut32 magic = RD_BE32 (mo->file);
|
||||
ut64 off = 0;
|
||||
if (magic == 0xcafebabe) {
|
||||
ut32 nfat = RD_BE32 (mo->file + 4);
|
||||
ut32 best = 0;
|
||||
for (ut32 i = 0; i < nfat; i++) {
|
||||
const ut8 *fa = mo->file + 8 + i * 20;
|
||||
ut32 cputype = RD_BE32 (fa + 0);
|
||||
if (cputype == 0x0100000c) { best = i; break; }
|
||||
}
|
||||
const ut8 *fa = mo->file + 8 + best * 20;
|
||||
off = RD_BE32 (fa + 8);
|
||||
}
|
||||
mo->base = off;
|
||||
const ut8 *p = mo->file + off;
|
||||
ut32 mh_magic = RD_LE32 (p);
|
||||
if (mh_magic != 0xFEEDFACF) {
|
||||
R_FREE (mo->file);
|
||||
return false;
|
||||
}
|
||||
ut32 ncmds = RD_LE32 (p + 0x10);
|
||||
mo->ncmds = ncmds;
|
||||
mo->cmd_off = off + 0x20;
|
||||
ut64 co = mo->cmd_off;
|
||||
for (ut32 i = 0; i < ncmds && (co + 8 <= mo->filesize); i++) {
|
||||
ut32 cmd = RD_LE32 (mo->file + co);
|
||||
ut32 cmdsize = RD_LE32 (mo->file + co + 4);
|
||||
if (cmd == 0x19 && cmdsize >= 72) {
|
||||
if (mo->nsegs < (int)(sizeof (mo->segs)/sizeof (mo->segs[0]))) {
|
||||
MachSeg *s = &mo->segs[mo->nsegs++];
|
||||
const ut8 *sp = mo->file + co + 8;
|
||||
memcpy (s->segname, sp, 16);
|
||||
s->vmaddr = RD_LE64 (sp + 16);
|
||||
s->vmsize = RD_LE64 (sp + 24);
|
||||
s->fileoff = RD_LE64 (sp + 32);
|
||||
s->filesize = RD_LE64 (sp + 40);
|
||||
s->maxprot = RD_LE32 (sp + 48);
|
||||
}
|
||||
}
|
||||
co += cmdsize ? cmdsize : 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void macho_free (MachO *mo) {
|
||||
R_FREE (mo->file);
|
||||
}
|
||||
|
||||
static inline bool macho_vm_in_text (MachO *mo, ut64 addr, ut64 *text_lo, ut64 *text_hi) {
|
||||
ut64 lo = UT64_MAX, hi = 0;
|
||||
for (int i = 0; i < mo->nsegs; i++) {
|
||||
MachSeg *s = &mo->segs[i];
|
||||
bool is_text = (s->maxprot & 0x4) || !strncmp (s->segname, "__TEXT", 6);
|
||||
if (is_text) {
|
||||
if (s->vmaddr < lo) lo = s->vmaddr;
|
||||
if (s->vmaddr + s->vmsize > hi) hi = s->vmaddr + s->vmsize;
|
||||
}
|
||||
}
|
||||
if (text_lo) *text_lo = lo;
|
||||
if (text_hi) *text_hi = hi;
|
||||
return (addr >= lo && addr < hi);
|
||||
}
|
||||
|
||||
static inline const ut8 *macho_vm_to_ptr (MachO *mo, ut64 vmaddr) {
|
||||
for (int i = 0; i < mo->nsegs; i++) {
|
||||
MachSeg *s = &mo->segs[i];
|
||||
if (vmaddr >= s->vmaddr && vmaddr < s->vmaddr + s->vmsize) {
|
||||
ut64 delta = vmaddr - s->vmaddr;
|
||||
if (delta < s->filesize) {
|
||||
return mo->file + mo->base + s->fileoff + delta;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
R_API bool r2unity_find_method_pointers_macho (R2UnityMetadata *meta, const char *macho_path, ut64 **out_ptrs) {
|
||||
if (!meta || !macho_path || !out_ptrs) return false;
|
||||
*out_ptrs = NULL;
|
||||
MachO mo;
|
||||
if (!macho_load (macho_path, &mo)) {
|
||||
return false;
|
||||
}
|
||||
size_t method_count = meta->header.v24.methodsSize / sizeof (Il2CppMethodDefinition);
|
||||
if (!method_count) {
|
||||
macho_free (&mo);
|
||||
return false;
|
||||
}
|
||||
ut64 text_lo = 0, text_hi = 0;
|
||||
macho_vm_in_text (&mo, 0, &text_lo, &text_hi);
|
||||
ut64 *candidates = R_NEWS (ut64, method_count);
|
||||
if (!candidates) {
|
||||
macho_free (&mo);
|
||||
return false;
|
||||
}
|
||||
bool found = false;
|
||||
int ptrsz = 8;
|
||||
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 < (8 + 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 cnt32 = RD_LE32 (buf + off);
|
||||
if (cnt32 < 1024 || cnt32 > (ut32)(method_count * 2)) continue;
|
||||
ut64 arrptr = RD_LE64 (buf + off + 8);
|
||||
ut32 good = 0, seen = 0;
|
||||
ut32 sample = R_MIN ((ut32)128, cnt32);
|
||||
for (ut32 k = 0; k < sample; k++) {
|
||||
const ut8 *p = macho_vm_to_ptr (&mo, arrptr + (ut64)k * (ut64)ptrsz);
|
||||
if (!p) break;
|
||||
ut64 val = RD_LE64 (p);
|
||||
if (val) seen++;
|
||||
if (val >= text_lo && val < text_hi) good++;
|
||||
}
|
||||
if (seen >= 8 && good >= 8) {
|
||||
memset (candidates, 0, method_count * sizeof (ut64));
|
||||
for (size_t m = 0; m < method_count && m < cnt32; m++) {
|
||||
const ut8 *p = macho_vm_to_ptr (&mo, arrptr + (ut64)m * (ut64)ptrsz);
|
||||
if (!p) break;
|
||||
ut64 val = RD_LE64 (p);
|
||||
if (val >= text_lo && val < text_hi) candidates[m] = val;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
R_FREE (candidates);
|
||||
macho_free (&mo);
|
||||
return false;
|
||||
}
|
||||
*out_ptrs = candidates;
|
||||
macho_free (&mo);
|
||||
return true;
|
||||
}
|
||||
+8
-1
@@ -4,7 +4,7 @@
|
||||
#include "lib/lib.h"
|
||||
|
||||
static void print_usage (const char *prog_name) {
|
||||
fprintf (stderr, "Usage: %s [--json-one-line] [--quiet] [-l N] [--macho-fast] [--gmp-addr 0xADDR] [--gmp-count N] <executable> <path/to/global-metadata.dat>\n", prog_name);
|
||||
fprintf (stderr, "Usage: %s [--json-one-line] [--quiet] [-l N] [--macho-fast] [--elf-fast] [--gmp-addr 0xADDR] [--gmp-count N] <executable> <path/to/global-metadata.dat>\n", prog_name);
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
@@ -14,6 +14,7 @@ int main (int argc, char *argv[]) {
|
||||
ut64 gmp_addr = 0;
|
||||
size_t gmp_count = 0;
|
||||
bool macho_fast = false;
|
||||
bool elf_fast = false;
|
||||
int i = 1;
|
||||
for (; i < argc; i++) {
|
||||
if (!strcmp (argv[i], "--json-one-line")) {
|
||||
@@ -28,6 +29,10 @@ int main (int argc, char *argv[]) {
|
||||
macho_fast = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp (argv[i], "--elf-fast")) {
|
||||
elf_fast = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp (argv[i], "-l") && i + 1 < argc) {
|
||||
limit = strtol (argv[++i], NULL, 0);
|
||||
continue;
|
||||
@@ -75,6 +80,8 @@ int main (int argc, char *argv[]) {
|
||||
has_ptrs = r2unity_read_method_pointers_at (meta, exe_path, gmp_addr, gmp_count, &method_ptrs);
|
||||
} else if (macho_fast) {
|
||||
has_ptrs = r2unity_find_method_pointers_macho (meta, exe_path, &method_ptrs);
|
||||
} else if (elf_fast) {
|
||||
has_ptrs = r2unity_find_method_pointers_elf (meta, exe_path, &method_ptrs);
|
||||
} else {
|
||||
has_ptrs = r2unity_find_method_pointers (meta, exe_path, &method_ptrs);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user