Skip to content

Commit 1fab8d6

Browse files
committed
Mac std attach Module
1 parent 472cf54 commit 1fab8d6

File tree

1 file changed

+105
-45
lines changed

1 file changed

+105
-45
lines changed

src/game_engine/unity/mono.rs

Lines changed: 105 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@ use crate::{
99
string::ArrayCString,
1010
Address, Address32, Address64, Error, Process,
1111
};
12+
#[cfg(feature = "std")]
13+
use crate::file_format::macho;
1214
use core::{cell::OnceCell, iter};
1315

1416
#[cfg(all(debug_assertions, feature = "alloc"))]
1517
use alloc::collections::BTreeSet;
18+
#[cfg(feature = "std")]
19+
use alloc::vec::Vec;
1620
use arrayvec::{ArrayString, ArrayVec};
1721
#[cfg(feature = "derive")]
1822
pub use asr_derive::MonoClass as Class;
1923
use bytemuck::CheckedBitPattern;
24+
#[cfg(feature = "std")]
25+
use std::{path::Path, fs::File, io, io::Read};
2026

2127
const CSTR: usize = 128;
2228

@@ -43,39 +49,73 @@ impl Module {
4349
/// correct for this function to work. If you don't know the version in
4450
/// advance, use [`attach_auto_detect`](Self::attach_auto_detect) instead.
4551
pub fn attach(process: &Process, version: Version) -> Option<Self> {
46-
let module = ["mono.dll", "mono-2.0-bdwgc.dll"]
47-
.iter()
48-
.find_map(|&name| process.get_module_address(name).ok())?;
49-
50-
let is_64_bit = pe::MachineType::read(process, module)? == pe::MachineType::X86_64;
51-
let offsets = Offsets::new(version, is_64_bit, BinaryFormat::PE);
52-
53-
let root_domain_function_address = pe::symbols(process, module)
54-
.find(|symbol| {
55-
symbol
56-
.get_name::<25>(process)
57-
.is_ok_and(|name| name.matches("mono_assembly_foreach"))
58-
})?
59-
.address;
52+
#[allow(unused)]
53+
let (module_name, module_range, format) = [
54+
("mono.dll", BinaryFormat::PE),
55+
("mono-2.0-bdwgc.dll", BinaryFormat::PE),
56+
#[cfg(feature = "std")]
57+
("libmonobdwgc-2.0.dylib", BinaryFormat::MachO)
58+
].into_iter()
59+
.find_map(|(name, format)| Some((name, process.get_module_range(name).ok()?, format)))?;
60+
61+
let module = module_range.0;
62+
63+
let is_64_bit = match format {
64+
BinaryFormat::PE => pe::MachineType::read(process, module)? == pe::MachineType::X86_64,
65+
#[cfg(feature = "std")]
66+
BinaryFormat::MachO => macho::is_64_bit(process, macho::scan_macho_page(process, module_range)?)?,
67+
};
68+
let offsets = Offsets::new(version, is_64_bit, format);
69+
70+
let mono_assembly_foreach_address = match format {
71+
BinaryFormat::PE => {
72+
pe::symbols(process, module)
73+
.find(|symbol| {
74+
symbol
75+
.get_name::<25>(process)
76+
.is_ok_and(|name| name.matches("mono_assembly_foreach"))
77+
})?
78+
.address
79+
},
80+
#[cfg(feature = "std")]
81+
BinaryFormat::MachO => {
82+
let mono_module_path = process.get_module_path(module_name).ok()?;
83+
let mono_module_bytes = file_read_all_bytes(mono_module_path).ok()?;
84+
macho::get_function_address(process, module_range, &mono_module_bytes, b"_mono_assembly_foreach")?
85+
}
86+
};
6087

61-
let assemblies_pointer: Address = match is_64_bit {
62-
true => {
63-
const SIG_MONO_64: Signature<3> = Signature::new("48 8B 0D");
64-
let scan_address: Address = SIG_MONO_64
65-
.scan_process_range(process, (root_domain_function_address, 0x100))?
88+
let assemblies_pointer: Address = match (is_64_bit, format) {
89+
(true, BinaryFormat::PE) => {
90+
const SIG_MONO_64_PE: Signature<3> = Signature::new("48 8B 0D");
91+
let scan_address: Address = SIG_MONO_64_PE
92+
.scan_process_range(process, (mono_assembly_foreach_address, 0x100))?
6693
+ 3;
6794
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
68-
}
69-
false => {
95+
},
96+
#[cfg(feature = "std")]
97+
(true, BinaryFormat::MachO) => {
98+
const SIG_MONO_64_MACHO: Signature<3> = Signature::new("48 8B 3D");
99+
// RIP-relative addressing
100+
// 3 is the offset to the next thing after the signature
101+
let scan_address = SIG_MONO_64_MACHO.scan_process_range(process, (mono_assembly_foreach_address, 0x100))? + 3;
102+
// 4 is the offset to the next instruction after relative
103+
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
104+
},
105+
(false, BinaryFormat::PE) => {
70106
const SIG_32_1: Signature<2> = Signature::new("FF 35");
71107
const SIG_32_2: Signature<2> = Signature::new("8B 0D");
72108

73109
let ptr = [SIG_32_1, SIG_32_2].iter().find_map(|sig| {
74-
sig.scan_process_range(process, (root_domain_function_address, 0x100))
110+
sig.scan_process_range(process, (mono_assembly_foreach_address, 0x100))
75111
})? + 2;
76112

77113
process.read::<Address32>(ptr + 2).ok()?.into()
78-
}
114+
},
115+
#[cfg(feature = "std")]
116+
(false, BinaryFormat::MachO) => {
117+
return None;
118+
},
79119
};
80120

81121
let assemblies: Address = match is_64_bit {
@@ -875,44 +915,64 @@ fn detect_version(process: &Process) -> Option<Version> {
875915
return Some(Version::V1);
876916
}
877917

878-
let unity_module = {
879-
let address = process.get_module_address("UnityPlayer.dll").ok()?;
880-
let range = pe::read_size_of_image(process, address)? as u64;
881-
(address, range)
882-
};
918+
let unity_module = [
919+
("UnityPlayer.dll", BinaryFormat::PE),
920+
#[cfg(feature = "std")]
921+
("UnityPlayer.dylib", BinaryFormat::MachO)
922+
].into_iter().find_map(|(name, format)| {
923+
match format {
924+
BinaryFormat::PE => {
925+
let address = process.get_module_address(name).ok()?;
926+
let range = pe::read_size_of_image(process, address)? as u64;
927+
Some((address, range))
928+
},
929+
#[cfg(feature = "std")]
930+
BinaryFormat::MachO => process.get_module_range(name).ok()
931+
}
932+
})?;
883933

934+
// null "202" wildcard "."
884935
const SIG_202X: Signature<6> = Signature::new("00 32 30 32 ?? 2E");
885936

886937
let Some(addr) = SIG_202X.scan_process_range(process, unity_module) else {
887938
return Some(Version::V2);
888939
};
889940

890-
const ZERO: u8 = b'0';
891-
const NINE: u8 = b'9';
892-
893941
let version_string = process.read::<[u8; 6]>(addr + 1).ok()?;
894942

895943
let (before, after) = version_string.split_at(version_string.iter().position(|&x| x == b'.')?);
896944

897-
let mut unity: u32 = 0;
898-
for &val in before {
899-
match val {
900-
ZERO..=NINE => unity = unity * 10 + (val - ZERO) as u32,
901-
_ => break,
902-
}
903-
}
945+
let unity: u32 = ascii_read_u32(before);
904946

905-
let mut unity_minor: u32 = 0;
906-
for &val in &after[1..] {
907-
match val {
908-
ZERO..=NINE => unity_minor = unity_minor * 10 + (val - ZERO) as u32,
909-
_ => break,
910-
}
911-
}
947+
let unity_minor: u32 = ascii_read_u32(&after[1..]);
912948

913949
Some(if (unity == 2021 && unity_minor >= 2) || (unity > 2021) {
914950
Version::V3
915951
} else {
916952
Version::V2
917953
})
918954
}
955+
956+
fn ascii_read_u32(slice: &[u8]) -> u32 {
957+
const ZERO: u8 = b'0';
958+
const NINE: u8 = b'9';
959+
960+
let mut result: u32 = 0;
961+
for &val in slice {
962+
match val {
963+
ZERO..=NINE => result = result * 10 + (val - ZERO) as u32,
964+
_ => break,
965+
}
966+
}
967+
result
968+
}
969+
970+
// --------------------------------------------------------
971+
972+
#[cfg(feature = "std")]
973+
fn file_read_all_bytes<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
974+
let mut f = File::open(path)?;
975+
let mut buffer: Vec<u8> = Vec::new();
976+
f.read_to_end(&mut buffer)?;
977+
Ok(buffer)
978+
}

0 commit comments

Comments
 (0)