Skip to content

Commit a673cf6

Browse files
committed
Mono Module attach
1 parent 4d12aaf commit a673cf6

File tree

1 file changed

+115
-31
lines changed

1 file changed

+115
-31
lines changed

src/game_engine/unity/mono.rs

Lines changed: 115 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
//! Support for attaching to Unity games that are using the standard Mono
22
//! backend.
33
4+
#[cfg(feature = "alloc")]
5+
use crate::file_format::macho;
46
use crate::{
5-
deep_pointer::DeepPointer, file_format::pe, future::retry, signature::Signature,
6-
string::ArrayCString, Address, Address32, Address64, Error, PointerSize, Process,
7+
deep_pointer::DeepPointer,
8+
file_format::{elf, pe},
9+
future::retry,
10+
signature::Signature,
11+
string::ArrayCString,
12+
Address, Address32, Address64, Error, PointerSize, Process,
713
};
814
use core::{array, cell::RefCell, iter};
915

@@ -36,34 +42,85 @@ impl Module {
3642
/// correct for this function to work. If you don't know the version in
3743
/// advance, use [`attach_auto_detect`](Self::attach_auto_detect) instead.
3844
pub fn attach(process: &Process, version: Version) -> Option<Self> {
39-
let module = ["mono.dll", "mono-2.0-bdwgc.dll"]
40-
.iter()
41-
.find_map(|&name| process.get_module_address(name).ok())?;
42-
43-
let pointer_size = match pe::MachineType::read(process, module)? {
44-
pe::MachineType::X86_64 => PointerSize::Bit64,
45-
_ => PointerSize::Bit32,
45+
#[allow(unused)]
46+
let (module_range, format) = [
47+
("mono.dll", BinaryFormat::PE),
48+
("libmono.so", BinaryFormat::ELF),
49+
#[cfg(feature = "alloc")]
50+
("libmono.0.dylib", BinaryFormat::MachO),
51+
("mono-2.0-bdwgc.dll", BinaryFormat::PE),
52+
("libmonobdwgc-2.0.so", BinaryFormat::ELF),
53+
#[cfg(feature = "alloc")]
54+
("libmonobdwgc-2.0.dylib", BinaryFormat::MachO),
55+
]
56+
.into_iter()
57+
.find_map(|(name, format)| Some((process.get_module_range(name).ok()?, format)))?;
58+
let module = module_range.0;
59+
60+
let pointer_size = match format {
61+
BinaryFormat::PE => pe::MachineType::read(process, module)?.pointer_size()?,
62+
BinaryFormat::ELF => elf::pointer_size(process, module)?,
63+
#[cfg(feature = "alloc")]
64+
BinaryFormat::MachO => macho::pointer_size(process, module_range)?,
4665
};
4766

48-
let offsets = Offsets::new(version, pointer_size)?;
49-
50-
let root_domain_function_address = pe::symbols(process, module)
51-
.find(|symbol| {
52-
symbol
53-
.get_name::<25>(process)
54-
.is_ok_and(|name| name.matches("mono_assembly_foreach"))
55-
})?
56-
.address;
67+
let offsets = Offsets::new(version, pointer_size, format)?;
68+
69+
let root_domain_function_address = match format {
70+
BinaryFormat::PE => {
71+
pe::symbols(process, module)
72+
.find(|symbol| {
73+
symbol
74+
.get_name::<25>(process)
75+
.is_ok_and(|name| name.matches("mono_assembly_foreach"))
76+
})?
77+
.address
78+
}
79+
BinaryFormat::ELF => {
80+
elf::symbols(process, module)
81+
.find(|symbol| {
82+
symbol
83+
.get_name::<25>(process)
84+
.is_ok_and(|name| name.matches("mono_assembly_foreach"))
85+
})?
86+
.address
87+
}
88+
#[cfg(feature = "alloc")]
89+
BinaryFormat::MachO => {
90+
macho::symbols(process, module_range)?
91+
.find(|symbol| {
92+
symbol
93+
.get_name::<26>(process)
94+
.is_ok_and(|name| name.matches("_mono_assembly_foreach"))
95+
})?
96+
.address
97+
}
98+
};
5799

58-
let assemblies_pointer: Address = match pointer_size {
59-
PointerSize::Bit64 => {
100+
let assemblies_pointer: Address = match (pointer_size, format) {
101+
(PointerSize::Bit64, BinaryFormat::PE) => {
60102
const SIG_MONO_64: Signature<3> = Signature::new("48 8B 0D");
61103
let scan_address: Address = SIG_MONO_64
62104
.scan_process_range(process, (root_domain_function_address, 0x100))?
63105
+ 3;
64106
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
65107
}
66-
PointerSize::Bit32 => {
108+
(PointerSize::Bit64, BinaryFormat::ELF) => {
109+
const SIG_MONO_64_ELF: Signature<3> = Signature::new("48 8B 3D");
110+
let scan_address: Address = SIG_MONO_64_ELF
111+
.scan_process_range(process, (root_domain_function_address, 0x100))?
112+
+ 3;
113+
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
114+
}
115+
#[cfg(feature = "alloc")]
116+
(PointerSize::Bit64, BinaryFormat::MachO) => {
117+
const SIG_MONO_64_MACHO: Signature<3> = Signature::new("48 8B 3D");
118+
let scan_address: Address = SIG_MONO_64_MACHO
119+
.scan_process_range(process, (root_domain_function_address, 0x100))?
120+
+ 3;
121+
scan_address + 0x4 + process.read::<i32>(scan_address).ok()?
122+
}
123+
(PointerSize::Bit32, BinaryFormat::PE) => {
67124
const SIG_32_1: Signature<2> = Signature::new("FF 35");
68125
const SIG_32_2: Signature<2> = Signature::new("8B 0D");
69126

@@ -811,9 +868,13 @@ struct Offsets {
811868
}
812869

813870
impl Offsets {
814-
const fn new(version: Version, pointer_size: PointerSize) -> Option<&'static Self> {
815-
match pointer_size {
816-
PointerSize::Bit64 => match version {
871+
const fn new(
872+
version: Version,
873+
pointer_size: PointerSize,
874+
format: BinaryFormat,
875+
) -> Option<&'static Self> {
876+
match (pointer_size, format) {
877+
(PointerSize::Bit64, BinaryFormat::PE) => match version {
817878
Version::V1 => Some(&Self {
818879
monoassembly_aname: 0x10,
819880
monoassembly_image: 0x58,
@@ -899,7 +960,7 @@ impl Offsets {
899960
monoclassfieldalignment: 0x20,
900961
}),
901962
},
902-
PointerSize::Bit32 => match version {
963+
(PointerSize::Bit32, BinaryFormat::PE) => match version {
903964
Version::V1 => Some(&Self {
904965
monoassembly_aname: 0x8,
905966
monoassembly_image: 0x40,
@@ -990,6 +1051,14 @@ impl Offsets {
9901051
}
9911052
}
9921053

1054+
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
1055+
enum BinaryFormat {
1056+
PE,
1057+
ELF,
1058+
#[cfg(feature = "alloc")]
1059+
MachO,
1060+
}
1061+
9931062
/// The version of Mono that was used for the game. These don't correlate to the
9941063
/// Mono version numbers.
9951064
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
@@ -1005,7 +1074,10 @@ pub enum Version {
10051074
}
10061075

10071076
fn detect_version(process: &Process) -> Option<Version> {
1008-
if process.get_module_address("mono.dll").is_ok() {
1077+
if process.get_module_address("mono.dll").is_ok()
1078+
|| process.get_module_address("libmono.so").is_ok()
1079+
|| process.get_module_address("libmono.0.dylib").is_ok()
1080+
{
10091081
// If the module mono.dll is present, then it's either V1 or V1Cattrs.
10101082
// In order to distinguish between them, we check the first class listed in the
10111083
// default Assembly-CSharp image and check for the pointer to its name, assuming it's using V1.
@@ -1029,11 +1101,23 @@ fn detect_version(process: &Process) -> Option<Version> {
10291101
});
10301102
}
10311103

1032-
let unity_module = {
1033-
let address = process.get_module_address("UnityPlayer.dll").ok()?;
1034-
let range = pe::read_size_of_image(process, address)? as u64;
1035-
(address, range)
1036-
};
1104+
let unity_module = [
1105+
("UnityPlayer.dll", BinaryFormat::PE),
1106+
("UnityPlayer.so", BinaryFormat::ELF),
1107+
#[cfg(feature = "alloc")]
1108+
("UnityPlayer.dylib", BinaryFormat::MachO),
1109+
]
1110+
.into_iter()
1111+
.find_map(|(name, format)| match format {
1112+
BinaryFormat::PE => {
1113+
let address = process.get_module_address(name).ok()?;
1114+
let range = pe::read_size_of_image(process, address)? as u64;
1115+
Some((address, range))
1116+
}
1117+
BinaryFormat::ELF => process.get_module_range(name).ok(),
1118+
#[cfg(feature = "alloc")]
1119+
BinaryFormat::MachO => process.get_module_range(name).ok(),
1120+
})?;
10371121

10381122
const SIG_202X: Signature<6> = Signature::new("00 32 30 32 ?? 2E");
10391123

0 commit comments

Comments
 (0)