Skip to content

Commit 4a4890e

Browse files
committed
Mac std attach Module
1 parent 3e16aae commit 4a4890e

File tree

1 file changed

+110
-45
lines changed

1 file changed

+110
-45
lines changed

src/game_engine/unity/mono.rs

Lines changed: 110 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,75 @@ 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+
("libmono.0.dylib", BinaryFormat::MachO),
58+
#[cfg(feature = "std")]
59+
("libmonobdwgc-2.0.dylib", BinaryFormat::MachO)
60+
].into_iter()
61+
.find_map(|(name, format)| Some((name, process.get_module_range(name).ok()?, format)))?;
62+
63+
let module = module_range.0;
64+
65+
let is_64_bit = match format {
66+
BinaryFormat::PE => pe::MachineType::read(process, module)? == pe::MachineType::X86_64,
67+
#[cfg(feature = "std")]
68+
BinaryFormat::MachO => macho::is_64_bit(process, macho::scan_macho_page(process, module_range)?)?,
69+
};
70+
let offsets = Offsets::new(version, is_64_bit, format);
71+
72+
let mono_assembly_foreach_address = match format {
73+
BinaryFormat::PE => {
74+
pe::symbols(process, module)
75+
.find(|symbol| {
76+
symbol
77+
.get_name::<25>(process)
78+
.is_ok_and(|name| name.matches("mono_assembly_foreach"))
79+
})?
80+
.address
81+
},
82+
#[cfg(feature = "std")]
83+
BinaryFormat::MachO => {
84+
let mono_module_path = process.get_module_path(module_name).ok()?;
85+
let mono_module_bytes = file_read_all_bytes(mono_module_path).ok()?;
86+
macho::get_function_address(process, module_range, &mono_module_bytes, b"_mono_assembly_foreach")?
87+
}
88+
};
6089

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))?
90+
let assemblies_pointer: Address = match (is_64_bit, format) {
91+
(true, BinaryFormat::PE) => {
92+
const SIG_MONO_64_PE: Signature<3> = Signature::new("48 8B 0D");
93+
let scan_address: Address = SIG_MONO_64_PE
94+
.scan_process_range(process, (mono_assembly_foreach_address, 0x100))?
6695
+ 3;
6796
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
68-
}
69-
false => {
97+
},
98+
#[cfg(feature = "std")]
99+
(true, BinaryFormat::MachO) => {
100+
const SIG_MONO_64_MACHO: Signature<3> = Signature::new("48 8B 3D");
101+
// RIP-relative addressing
102+
// 3 is the offset to the next thing after the signature
103+
let scan_address = SIG_MONO_64_MACHO.scan_process_range(process, (mono_assembly_foreach_address, 0x100))? + 3;
104+
// 4 is the offset to the next instruction after relative
105+
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
106+
},
107+
(false, BinaryFormat::PE) => {
70108
const SIG_32_1: Signature<2> = Signature::new("FF 35");
71109
const SIG_32_2: Signature<2> = Signature::new("8B 0D");
72110

73111
let ptr = [SIG_32_1, SIG_32_2].iter().find_map(|sig| {
74-
sig.scan_process_range(process, (root_domain_function_address, 0x100))
112+
sig.scan_process_range(process, (mono_assembly_foreach_address, 0x100))
75113
})? + 2;
76114

77115
process.read::<Address32>(ptr + 2).ok()?.into()
78-
}
116+
},
117+
#[cfg(feature = "std")]
118+
(false, BinaryFormat::MachO) => {
119+
return None;
120+
},
79121
};
80122

81123
let assemblies: Address = match is_64_bit {
@@ -893,45 +935,68 @@ fn detect_version(process: &Process) -> Option<Version> {
893935
if process.get_module_address("mono.dll").is_ok() {
894936
return Some(Version::V1);
895937
}
938+
if process.get_module_address("libmono.0.dylib").is_ok() {
939+
return Some(Version::V1);
940+
}
896941

897-
let unity_module = {
898-
let address = process.get_module_address("UnityPlayer.dll").ok()?;
899-
let range = pe::read_size_of_image(process, address)? as u64;
900-
(address, range)
901-
};
942+
let unity_module = [
943+
("UnityPlayer.dll", BinaryFormat::PE),
944+
#[cfg(feature = "std")]
945+
("UnityPlayer.dylib", BinaryFormat::MachO)
946+
].into_iter().find_map(|(name, format)| {
947+
match format {
948+
BinaryFormat::PE => {
949+
let address = process.get_module_address(name).ok()?;
950+
let range = pe::read_size_of_image(process, address)? as u64;
951+
Some((address, range))
952+
},
953+
#[cfg(feature = "std")]
954+
BinaryFormat::MachO => process.get_module_range(name).ok()
955+
}
956+
})?;
902957

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

905961
let Some(addr) = SIG_202X.scan_process_range(process, unity_module) else {
906962
return Some(Version::V2);
907963
};
908964

909-
const ZERO: u8 = b'0';
910-
const NINE: u8 = b'9';
911-
912965
let version_string = process.read::<[u8; 6]>(addr + 1).ok()?;
913966

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

916-
let mut unity: u32 = 0;
917-
for &val in before {
918-
match val {
919-
ZERO..=NINE => unity = unity * 10 + (val - ZERO) as u32,
920-
_ => break,
921-
}
922-
}
969+
let unity: u32 = ascii_read_u32(before);
923970

924-
let mut unity_minor: u32 = 0;
925-
for &val in &after[1..] {
926-
match val {
927-
ZERO..=NINE => unity_minor = unity_minor * 10 + (val - ZERO) as u32,
928-
_ => break,
929-
}
930-
}
971+
let unity_minor: u32 = ascii_read_u32(&after[1..]);
931972

932973
Some(if (unity == 2021 && unity_minor >= 2) || (unity > 2021) {
933974
Version::V3
934975
} else {
935976
Version::V2
936977
})
937978
}
979+
980+
fn ascii_read_u32(slice: &[u8]) -> u32 {
981+
const ZERO: u8 = b'0';
982+
const NINE: u8 = b'9';
983+
984+
let mut result: u32 = 0;
985+
for &val in slice {
986+
match val {
987+
ZERO..=NINE => result = result * 10 + (val - ZERO) as u32,
988+
_ => break,
989+
}
990+
}
991+
result
992+
}
993+
994+
// --------------------------------------------------------
995+
996+
#[cfg(feature = "std")]
997+
fn file_read_all_bytes<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
998+
let mut f = File::open(path)?;
999+
let mut buffer: Vec<u8> = Vec::new();
1000+
f.read_to_end(&mut buffer)?;
1001+
Ok(buffer)
1002+
}

0 commit comments

Comments
 (0)