1
1
//! Support for attaching to Unity games that are using the standard Mono
2
2
//! backend.
3
3
4
+ #[ cfg( feature = "alloc" ) ]
5
+ use crate :: file_format:: macho;
4
6
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 ,
7
13
} ;
8
14
use core:: { array, cell:: RefCell , iter} ;
9
15
@@ -36,34 +42,85 @@ impl Module {
36
42
/// correct for this function to work. If you don't know the version in
37
43
/// advance, use [`attach_auto_detect`](Self::attach_auto_detect) instead.
38
44
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) ?,
46
65
} ;
47
66
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
+ } ;
57
99
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 ) => {
60
102
const SIG_MONO_64 : Signature < 3 > = Signature :: new ( "48 8B 0D" ) ;
61
103
let scan_address: Address = SIG_MONO_64
62
104
. scan_process_range ( process, ( root_domain_function_address, 0x100 ) ) ?
63
105
+ 3 ;
64
106
scan_address + 0x4 + process. read :: < i32 > ( scan_address) . ok ( ) ?
65
107
}
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 ) => {
67
124
const SIG_32_1 : Signature < 2 > = Signature :: new ( "FF 35" ) ;
68
125
const SIG_32_2 : Signature < 2 > = Signature :: new ( "8B 0D" ) ;
69
126
@@ -811,9 +868,13 @@ struct Offsets {
811
868
}
812
869
813
870
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 {
817
878
Version :: V1 => Some ( & Self {
818
879
monoassembly_aname : 0x10 ,
819
880
monoassembly_image : 0x58 ,
@@ -899,7 +960,7 @@ impl Offsets {
899
960
monoclassfieldalignment : 0x20 ,
900
961
} ) ,
901
962
} ,
902
- PointerSize :: Bit32 => match version {
963
+ ( PointerSize :: Bit32 , BinaryFormat :: PE ) => match version {
903
964
Version :: V1 => Some ( & Self {
904
965
monoassembly_aname : 0x8 ,
905
966
monoassembly_image : 0x40 ,
@@ -990,6 +1051,14 @@ impl Offsets {
990
1051
}
991
1052
}
992
1053
1054
+ #[ derive( Copy , Clone , PartialEq , Hash , Debug ) ]
1055
+ enum BinaryFormat {
1056
+ PE ,
1057
+ ELF ,
1058
+ #[ cfg( feature = "alloc" ) ]
1059
+ MachO ,
1060
+ }
1061
+
993
1062
/// The version of Mono that was used for the game. These don't correlate to the
994
1063
/// Mono version numbers.
995
1064
#[ derive( Copy , Clone , PartialEq , Hash , Debug ) ]
@@ -1005,7 +1074,10 @@ pub enum Version {
1005
1074
}
1006
1075
1007
1076
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
+ {
1009
1081
// If the module mono.dll is present, then it's either V1 or V1Cattrs.
1010
1082
// In order to distinguish between them, we check the first class listed in the
1011
1083
// 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> {
1029
1101
} ) ;
1030
1102
}
1031
1103
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
+ } ) ?;
1037
1121
1038
1122
const SIG_202X : Signature < 6 > = Signature :: new ( "00 32 30 32 ?? 2E" ) ;
1039
1123
0 commit comments