Skip to content

Commit

Permalink
Support Mac SceneManager
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexKnauth committed Dec 9, 2023
1 parent df9a3ad commit eefc280
Showing 1 changed file with 42 additions and 14 deletions.
56 changes: 42 additions & 14 deletions src/game_engine/unity/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use core::{array, iter, mem::MaybeUninit};
use crate::{
file_format::pe, future::retry, signature::Signature, string::ArrayCString, Address, Address32,
Address64, Error, Process,
file_format::macho,
};

const CSTR: usize = 128;
Expand All @@ -30,29 +31,50 @@ pub struct SceneManager {
impl SceneManager {
/// Attaches to the scene manager in the given process.
pub fn attach(process: &Process) -> Option<Self> {
const SIG_64_BIT: Signature<13> = Signature::new("48 83 EC 20 4C 8B ?5 ???????? 33 F6");
const SIG_64_BIT_PE: Signature<13> = Signature::new("48 83 EC 20 4C 8B ?5 ???????? 33 F6");
const SIG_64_BIT_MACHO: Signature<13> = Signature::new("41 54 53 50 4C 8B ?5 ???????? 41 83");
const SIG_32_1: Signature<12> = Signature::new("55 8B EC 51 A1 ???????? 53 33 DB");
const SIG_32_2: Signature<6> = Signature::new("53 8D 41 ?? 33 DB");
const SIG_32_3: Signature<14> = Signature::new("55 8B EC 83 EC 18 A1 ???????? 33 C9 53");

let unity_player = process.get_module_range("UnityPlayer.dll").ok()?;
let (unity_player, format) = [("UnityPlayer.dll", BinaryFormat::PE), ("UnityPlayer.dylib", BinaryFormat::MachO)]
.into_iter()
.find_map(|(name, format)| Some((process.get_module_range(name).ok()?, format)))?;

let is_64_bit = pe::MachineType::read(process, unity_player.0)? == pe::MachineType::X86_64;
let is_64_bit = match format {
BinaryFormat::PE => pe::MachineType::read(process, unity_player.0)? == pe::MachineType::X86_64,
BinaryFormat::MachO => macho::is_64_bit(process, macho::scan_macho_page(process, unity_player)?)?,
};
let is_il2cpp = process.get_module_address("GameAssembly.dll").is_ok();

// There are multiple signatures that can be used, depending on the version of Unity
// used in the target game.
let base_address: Address = if is_64_bit {
let addr = SIG_64_BIT.scan_process_range(process, unity_player)? + 7;
addr + 0x4 + process.read::<i32>(addr).ok()?
} else if let Some(addr) = SIG_32_1.scan_process_range(process, unity_player) {
process.read::<Address32>(addr + 5).ok()?.into()
} else if let Some(addr) = SIG_32_2.scan_process_range(process, unity_player) {
process.read::<Address32>(addr.add_signed(-4)).ok()?.into()
} else if let Some(addr) = SIG_32_3.scan_process_range(process, unity_player) {
process.read::<Address32>(addr + 7).ok()?.into()
} else {
return None;
let base_address: Address = match (is_64_bit, format) {
(true, BinaryFormat::PE) => {
let addr = SIG_64_BIT_PE.scan_process_range(process, unity_player)? + 7;
addr + 0x4 + process.read::<i32>(addr).ok()?
},
(true, BinaryFormat::MachO) => {
// RIP-relative addressing
// 7 is the offset to the ???????? question marks in the signature
let addr = SIG_64_BIT_MACHO.scan_process_range(process, unity_player)? + 7;
// 4 is the offset to the next instruction after the question marks
addr + 0x4 + process.read::<i32>(addr).ok()?
},
(false, BinaryFormat::PE) => {
if let Some(addr) = SIG_32_1.scan_process_range(process, unity_player) {
process.read::<Address32>(addr + 5).ok()?.into()
} else if let Some(addr) = SIG_32_2.scan_process_range(process, unity_player) {
process.read::<Address32>(addr.add_signed(-4)).ok()?.into()
} else if let Some(addr) = SIG_32_3.scan_process_range(process, unity_player) {
process.read::<Address32>(addr + 7).ok()?.into()
} else {
return None;
}
},
(false, BinaryFormat::MachO) => {
return None;
},
};

let offsets = Offsets::new(is_64_bit);
Expand Down Expand Up @@ -426,6 +448,12 @@ impl Transform {
}
}

#[derive(Copy, Clone, PartialEq, Hash, Debug)]
enum BinaryFormat {
PE,
MachO,
}

struct Offsets {
scene_count: u8,
active_scene: u8,
Expand Down

0 comments on commit eefc280

Please sign in to comment.