diff --git a/src/file_format/macho.rs b/src/file_format/macho.rs index 998ab69..5bb344a 100644 --- a/src/file_format/macho.rs +++ b/src/file_format/macho.rs @@ -1,10 +1,15 @@ //! Support for parsing MachO files -use crate::Process; +use crate::{Process, Address}; use core::mem; -use std::{fs::File, io::Read}; +// Magic mach-o header constants from: +// https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html +const MH_MAGIC_32: u32 = 0xfeedface; +const MH_CIGAM_32: u32 = 0xcefaedfe; +const MH_MAGIC_64: u32 = 0xfeedfacf; +const MH_CIGAM_64: u32 = 0xcffaedfe; struct MachOFormatOffsets { number_of_commands: usize, @@ -35,20 +40,29 @@ impl MachOFormatOffsets { } } -/// Determines whether a MachO process is 64-bit or 32-bit -pub fn is_64_bit(process: &Process) -> Option { - // Magic mach-o header constants from: - // https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html - const MH_MAGIC_32: u32 = 0xfeedface; - const MH_CIGAM_32: u32 = 0xcefaedfe; - const MH_MAGIC_64: u32 = 0xfeedfacf; - const MH_CIGAM_64: u32 = 0xcffaedfe; +/// Scans the range for a page that begins with MachO Magic +pub fn scan_macho_page(process: &Process, range: (Address, u64)) -> Option
{ + const PAGE_SIZE: u64 = 0x1000; + let (addr, len) = range; + // negation mod PAGE_SIZE + let distance_to_page = (PAGE_SIZE - (addr.value() % PAGE_SIZE)) % PAGE_SIZE; + // round up to the next multiple of PAGE_SIZE + let first_page = addr + distance_to_page; + for i in 0..((len - distance_to_page) / PAGE_SIZE) { + let a = first_page + (i * PAGE_SIZE); + match process.read::(a) { + Ok(MH_MAGIC_64 | MH_CIGAM_64 | MH_MAGIC_32 | MH_CIGAM_32) => { + return Some(a); + } + _ => () + } + } + None +} - let process_path = process.get_path().ok()?; - let mut process_file = File::open(process_path).ok()?; - let mut buffer: [u8; 4] = [0; 4]; - process_file.read_exact(&mut buffer).ok(); - let magic: u32 = bytemuck::checked::try_from_bytes(&buffer).ok().cloned()?; +/// Determines whether a MachO header at the address is 64-bit or 32-bit +pub fn is_64_bit(process: &Process, address: Address) -> Option { + let magic: u32 = process.read(address).ok()?; match magic { MH_MAGIC_64 | MH_CIGAM_64 => Some(true), MH_MAGIC_32 | MH_CIGAM_32 => Some(false), diff --git a/src/file_format/mod.rs b/src/file_format/mod.rs index 5a6e905..f26597d 100644 --- a/src/file_format/mod.rs +++ b/src/file_format/mod.rs @@ -2,5 +2,4 @@ pub mod elf; pub mod pe; -#[cfg(feature = "std")] pub mod macho; diff --git a/src/game_engine/unity/mono.rs b/src/game_engine/unity/mono.rs index 3b08f89..1a2e275 100644 --- a/src/game_engine/unity/mono.rs +++ b/src/game_engine/unity/mono.rs @@ -139,7 +139,7 @@ impl Module { #[cfg(feature = "std")] fn attach_dylib(process: &Process, version: Version, module_range: (Address, u64)) -> Option { - let is_64_bit = macho::is_64_bit(process)?; + let is_64_bit = macho::is_64_bit(process, macho::scan_macho_page(process, module_range)?)?; let offsets = Offsets::new(version, is_64_bit, BinaryFormat::MachO); let process_path = process.get_path().ok()?; diff --git a/src/game_engine/unity/scene.rs b/src/game_engine/unity/scene.rs index e6c5d2a..25d8785 100644 --- a/src/game_engine/unity/scene.rs +++ b/src/game_engine/unity/scene.rs @@ -9,11 +9,9 @@ use core::{array, mem::MaybeUninit}; use crate::{ file_format::pe, future::retry, signature::Signature, string::ArrayCString, Address, Address32, Address64, Error, Process, + file_format::macho, }; -#[cfg(feature = "std")] -use crate::file_format::macho; - /// The scene manager allows you to easily identify the current scene loaded in /// the attached Unity game. /// @@ -32,7 +30,6 @@ impl SceneManager { if let Ok(unity_player) = process.get_module_range("UnityPlayer.dll") { return Self::attach_dll(process, unity_player); } - #[cfg(feature = "std")] if let Ok(unity_player) = process.get_module_range("UnityPlayer.dylib") { return Self::attach_dylib(process, unity_player); } @@ -71,11 +68,10 @@ impl SceneManager { }) } - #[cfg(feature = "std")] fn attach_dylib(process: &Process, unity_player: (Address, u64)) -> Option { const SIG_64_BIT_DYLIB: Signature<13> = Signature::new("41 54 53 50 4C 8B ?5 ???????? 41 83"); - let is_64_bit = macho::is_64_bit(process)?; + let is_64_bit = macho::is_64_bit(process, macho::scan_macho_page(process, unity_player)?)?; let is_il2cpp = false; let address = if is_64_bit {