@@ -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;
1214use core:: { cell:: OnceCell , iter} ;
1315
1416#[ cfg( all( debug_assertions, feature = "alloc" ) ) ]
1517use alloc:: collections:: BTreeSet ;
18+ #[ cfg( feature = "std" ) ]
19+ use alloc:: vec:: Vec ;
1620use arrayvec:: { ArrayString , ArrayVec } ;
1721#[ cfg( feature = "derive" ) ]
1822pub use asr_derive:: MonoClass as Class ;
1923use bytemuck:: CheckedBitPattern ;
24+ #[ cfg( feature = "std" ) ]
25+ use std:: { path:: Path , fs:: File , io, io:: Read } ;
2026
2127const 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