use super::super::binary::find_code_bounds;
use super::super::source::MemorySource;
use anyhow::{bail, Result};
use byteorder::{ByteOrder, LE};
pub fn discover_class_uclass(source: &dyn MemorySource) -> Result<usize> {
let code_bounds = find_code_bounds(source)?;
eprintln!("Scanning for Class UClass (self-referential pattern)...");
let mut candidates: Vec<usize> = Vec::new();
for region in source.regions() {
if !region.is_readable() || !region.is_writable() {
continue;
}
if region.start < 0x151000000 || region.start > 0x175000000 {
continue;
}
eprintln!(
" Scanning region {:#x}-{:#x} for Class UClass...",
region.start, region.end
);
let data = match source.read_bytes(region.start, region.size()) {
Ok(d) => d,
Err(_) => continue,
};
for i in (0..data.len().saturating_sub(0x28)).step_by(8) {
let obj_addr = region.start + i;
let vtable_ptr = LE::read_u64(&data[i..i + 8]) as usize;
if vtable_ptr < 0x140000000 || vtable_ptr > 0x160000000 {
continue;
}
let class_private = LE::read_u64(&data[i + 0x10..i + 0x18]) as usize;
if class_private != obj_addr {
continue; }
if let Ok(vtable_data) = source.read_bytes(vtable_ptr, 8) {
let first_func = LE::read_u64(&vtable_data) as usize;
if !code_bounds.contains(first_func) {
continue;
}
} else {
continue;
}
eprintln!(
" Found self-referential object at {:#x} (vtable={:#x})",
obj_addr, vtable_ptr
);
candidates.push(obj_addr);
}
}
if candidates.is_empty() {
eprintln!(
" No self-referential UClass found at offset 0x10, trying alternative offsets..."
);
for class_offset in [0x08, 0x18, 0x20, 0x28] {
for region in source.regions() {
if !region.is_readable() || !region.is_writable() {
continue;
}
if region.start < 0x151000000 || region.start > 0x175000000 {
continue;
}
let data = match source.read_bytes(region.start, region.size()) {
Ok(d) => d,
Err(_) => continue,
};
for i in (0..data.len().saturating_sub(0x30)).step_by(8) {
let obj_addr = region.start + i;
let vtable_ptr = LE::read_u64(&data[i..i + 8]) as usize;
if vtable_ptr < 0x140000000 || vtable_ptr > 0x160000000 {
continue;
}
if i + class_offset + 8 > data.len() {
continue;
}
let class_private =
LE::read_u64(&data[i + class_offset..i + class_offset + 8]) as usize;
if class_private != obj_addr {
continue;
}
if let Ok(vtable_data) = source.read_bytes(vtable_ptr, 8) {
let first_func = LE::read_u64(&vtable_data) as usize;
if !code_bounds.contains(first_func) {
continue;
}
} else {
continue;
}
eprintln!(
" Found self-referential at {:#x} with class_offset={:#x}",
obj_addr, class_offset
);
candidates.push(obj_addr);
if candidates.len() >= 3 {
break;
}
}
if candidates.len() >= 3 {
break;
}
}
if !candidates.is_empty() {
eprintln!(" Class UClass likely at offset {:#x}", class_offset);
break;
}
}
}
if candidates.is_empty() {
bail!("Could not find Class UClass (self-referential pattern not found)");
}
Ok(candidates[0])
}