@@ -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,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