From 7eb038e90ffedc47f75d9dd3e820945a7e8cfcb5 Mon Sep 17 00:00:00 2001 From: AlexKnauth Date: Mon, 16 Oct 2023 17:32:51 -0400 Subject: [PATCH] Support Mac SceneManager --- src/game_engine/unity/scene.rs | 56 +++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/game_engine/unity/scene.rs b/src/game_engine/unity/scene.rs index ec99d8d..e98c8eb 100644 --- a/src/game_engine/unity/scene.rs +++ b/src/game_engine/unity/scene.rs @@ -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; @@ -30,29 +31,50 @@ pub struct SceneManager { impl SceneManager { /// Attaches to the scene manager in the given process. pub fn attach(process: &Process) -> Option { - 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::(addr).ok()? - } else if let Some(addr) = SIG_32_1.scan_process_range(process, unity_player) { - process.read::(addr + 5).ok()?.into() - } else if let Some(addr) = SIG_32_2.scan_process_range(process, unity_player) { - process.read::(addr.add_signed(-4)).ok()?.into() - } else if let Some(addr) = SIG_32_3.scan_process_range(process, unity_player) { - process.read::(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::(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::(addr).ok()? + }, + (false, BinaryFormat::PE) => { + if let Some(addr) = SIG_32_1.scan_process_range(process, unity_player) { + process.read::(addr + 5).ok()?.into() + } else if let Some(addr) = SIG_32_2.scan_process_range(process, unity_player) { + process.read::(addr.add_signed(-4)).ok()?.into() + } else if let Some(addr) = SIG_32_3.scan_process_range(process, unity_player) { + process.read::(addr + 7).ok()?.into() + } else { + return None; + } + }, + (false, BinaryFormat::MachO) => { + return None; + }, }; let offsets = Offsets::new(is_64_bit); @@ -426,6 +448,12 @@ impl Transform { } } +#[derive(Copy, Clone, PartialEq, Hash, Debug)] +enum BinaryFormat { + PE, + MachO, +} + struct Offsets { scene_count: u8, active_scene: u8,