22//! backend.
33
44use crate :: {
5- file_format:: pe, future:: retry, signature:: Signature , string:: ArrayCString , Address , Address32 ,
6- Address64 , Error , PointerSize , Process ,
5+ file_format:: { elf, pe} ,
6+ future:: retry,
7+ signature:: Signature ,
8+ string:: ArrayCString ,
9+ Address , Address32 , Address64 , Error , PointerSize , Process ,
710} ;
811use core:: {
912 array,
@@ -40,34 +43,61 @@ impl Module {
4043 /// correct for this function to work. If you don't know the version in
4144 /// advance, use [`attach_auto_detect`](Self::attach_auto_detect) instead.
4245 pub fn attach ( process : & Process , version : Version ) -> Option < Self > {
43- let module = [ "mono.dll" , "mono-2.0-bdwgc.dll" ]
44- . iter ( )
45- . find_map ( |& name| process. get_module_address ( name) . ok ( ) ) ?;
46-
47- let pointer_size = match pe:: MachineType :: read ( process, module) ? {
48- pe:: MachineType :: X86_64 => PointerSize :: Bit64 ,
49- _ => PointerSize :: Bit32 ,
46+ #[ allow( unused) ]
47+ let ( module_range, format) = [
48+ ( "mono.dll" , BinaryFormat :: PE ) ,
49+ ( "libmono.so" , BinaryFormat :: ELF ) ,
50+ ( "mono-2.0-bdwgc.dll" , BinaryFormat :: PE ) ,
51+ ( "libmonobdwgc-2.0.so" , BinaryFormat :: ELF ) ,
52+ ]
53+ . into_iter ( )
54+ . find_map ( |( name, format) | Some ( ( process. get_module_range ( name) . ok ( ) ?, format) ) ) ?;
55+ let module = module_range. 0 ;
56+
57+ let pointer_size = match format {
58+ BinaryFormat :: PE => pe:: MachineType :: read ( process, module) ?. pointer_size ( ) ?,
59+ BinaryFormat :: ELF => elf:: pointer_size ( process, module) ?,
5060 } ;
5161
52- let offsets = Offsets :: new ( version, pointer_size) ?;
53-
54- let root_domain_function_address = pe:: symbols ( process, module)
55- . find ( |symbol| {
56- symbol
57- . get_name :: < 25 > ( process)
58- . is_ok_and ( |name| name. matches ( "mono_assembly_foreach" ) )
59- } ) ?
60- . address ;
62+ let offsets = Offsets :: new ( version, pointer_size, format) ?;
63+
64+ let root_domain_function_address = match format {
65+ BinaryFormat :: PE => {
66+ pe:: symbols ( process, module)
67+ . find ( |symbol| {
68+ symbol
69+ . get_name :: < 25 > ( process)
70+ . is_ok_and ( |name| name. matches ( "mono_assembly_foreach" ) )
71+ } ) ?
72+ . address
73+ }
74+ BinaryFormat :: ELF => {
75+ elf:: symbols ( process, module)
76+ . find ( |symbol| {
77+ symbol
78+ . get_name :: < 25 > ( process)
79+ . is_ok_and ( |name| name. matches ( "mono_assembly_foreach" ) )
80+ } ) ?
81+ . address
82+ }
83+ } ;
6184
62- let assemblies: Address = match pointer_size {
63- PointerSize :: Bit64 => {
85+ let assemblies: Address = match ( pointer_size, format ) {
86+ ( PointerSize :: Bit64 , BinaryFormat :: PE ) => {
6487 const SIG_MONO_64 : Signature < 3 > = Signature :: new ( "48 8B 0D" ) ;
6588 let scan_address: Address = SIG_MONO_64
6689 . scan_process_range ( process, ( root_domain_function_address, 0x100 ) ) ?
6790 + 3 ;
6891 scan_address + 0x4 + process. read :: < i32 > ( scan_address) . ok ( ) ?
6992 }
70- PointerSize :: Bit32 => {
93+ ( PointerSize :: Bit64 , BinaryFormat :: ELF ) => {
94+ const SIG_MONO_64_ELF : Signature < 3 > = Signature :: new ( "48 8B 3D" ) ;
95+ let scan_address: Address = SIG_MONO_64_ELF
96+ . scan_process_range ( process, ( root_domain_function_address, 0x100 ) ) ?
97+ + 3 ;
98+ scan_address + 0x4 + process. read :: < i32 > ( scan_address) . ok ( ) ?
99+ }
100+ ( PointerSize :: Bit32 , BinaryFormat :: PE ) => {
71101 const SIG_32_1 : Signature < 2 > = Signature :: new ( "FF 35" ) ;
72102 const SIG_32_2 : Signature < 2 > = Signature :: new ( "8B 0D" ) ;
73103
@@ -754,9 +784,13 @@ struct Offsets {
754784}
755785
756786impl Offsets {
757- const fn new ( version : Version , pointer_size : PointerSize ) -> Option < & ' static Self > {
758- match pointer_size {
759- PointerSize :: Bit64 => match version {
787+ const fn new (
788+ version : Version ,
789+ pointer_size : PointerSize ,
790+ format : BinaryFormat ,
791+ ) -> Option < & ' static Self > {
792+ match ( pointer_size, format) {
793+ ( PointerSize :: Bit64 , BinaryFormat :: PE ) => match version {
760794 Version :: V1 => Some ( & Self {
761795 monoassembly_aname : 0x10 ,
762796 monoassembly_image : 0x58 ,
@@ -799,6 +833,8 @@ impl Offsets {
799833 monovtable_vtable : 0x48 ,
800834 monoclassfieldalignment : 0x20 ,
801835 } ) ,
836+ // 64-bit PE V2 matches Unity2019_4_2020_3_x64_PE_Offsets from
837+ // https://github.com/hackf5/unityspy/blob/master/src/HackF5.UnitySpy/Offsets/MonoLibraryOffsets.cs#L49
802838 Version :: V2 => Some ( & Self {
803839 monoassembly_aname : 0x10 ,
804840 monoassembly_image : 0x60 ,
@@ -842,7 +878,95 @@ impl Offsets {
842878 monoclassfieldalignment : 0x20 ,
843879 } ) ,
844880 } ,
845- PointerSize :: Bit32 => match version {
881+ ( PointerSize :: Bit64 , BinaryFormat :: ELF ) => match version {
882+ Version :: V1 => Some ( & Self {
883+ monoassembly_aname : 0x10 ,
884+ monoassembly_image : 0x58 ,
885+ monoimage_class_cache : 0x3D0 ,
886+ monointernalhashtable_table : 0x20 ,
887+ monointernalhashtable_size : 0x18 ,
888+ monoclassdef_next_class_cache : 0xF8 ,
889+ monoclassdef_klass : 0x0 ,
890+ monoclass_name : 0x40 ,
891+ monoclass_name_space : 0x48 ,
892+ monoclass_fields : 0xA0 ,
893+ monoclassdef_field_count : 0x8C ,
894+ monoclass_runtime_info : 0xF0 ,
895+ monoclass_vtable_size : 0x18 , // MonoVtable.data
896+ monoclass_parent : 0x28 ,
897+ monoclassfield_name : 0x8 ,
898+ monoclassfield_offset : 0x18 ,
899+ monoclassruntimeinfo_domain_vtables : 0x8 ,
900+ monovtable_vtable : 0x48 ,
901+ monoclassfieldalignment : 0x20 ,
902+ } ) ,
903+ Version :: V1Cattrs => Some ( & Self {
904+ monoassembly_aname : 0x10 ,
905+ monoassembly_image : 0x58 ,
906+ monoimage_class_cache : 0x3D0 ,
907+ monointernalhashtable_table : 0x20 ,
908+ monointernalhashtable_size : 0x18 ,
909+ monoclassdef_next_class_cache : 0x100 ,
910+ monoclassdef_klass : 0x0 ,
911+ monoclass_name : 0x48 ,
912+ monoclass_name_space : 0x50 ,
913+ monoclass_fields : 0xA8 ,
914+ monoclassdef_field_count : 0x94 ,
915+ monoclass_runtime_info : 0xF8 ,
916+ monoclass_vtable_size : 0x18 , // MonoVtable.data
917+ monoclass_parent : 0x28 ,
918+ monoclassfield_name : 0x8 ,
919+ monoclassfield_offset : 0x18 ,
920+ monoclassruntimeinfo_domain_vtables : 0x8 ,
921+ monovtable_vtable : 0x48 ,
922+ monoclassfieldalignment : 0x20 ,
923+ } ) ,
924+ // 64-bit ELF V2 happens to match Unity2019_4_2020_3_x64_MachO_Offsets from
925+ // https://github.com/hackf5/unityspy/blob/master/src/HackF5.UnitySpy/Offsets/MonoLibraryOffsets.cs#L86
926+ Version :: V2 => Some ( & Self {
927+ monoassembly_aname : 0x10 ,
928+ monoassembly_image : 0x60 ,
929+ monoimage_class_cache : 0x4C0 ,
930+ monointernalhashtable_table : 0x20 ,
931+ monointernalhashtable_size : 0x18 ,
932+ monoclassdef_next_class_cache : 0x100 ,
933+ monoclassdef_klass : 0x0 ,
934+ monoclass_name : 0x40 ,
935+ monoclass_name_space : 0x48 ,
936+ monoclass_fields : 0x90 ,
937+ monoclassdef_field_count : 0xF8 ,
938+ monoclass_runtime_info : 0xC8 ,
939+ monoclass_vtable_size : 0x54 ,
940+ monoclass_parent : 0x28 ,
941+ monoclassfield_name : 0x8 ,
942+ monoclassfield_offset : 0x18 ,
943+ monoclassruntimeinfo_domain_vtables : 0x8 ,
944+ monovtable_vtable : 0x40 ,
945+ monoclassfieldalignment : 0x20 ,
946+ } ) ,
947+ Version :: V3 => Some ( & Self {
948+ monoassembly_aname : 0x10 ,
949+ monoassembly_image : 0x60 ,
950+ monoimage_class_cache : 0x4D0 ,
951+ monointernalhashtable_table : 0x20 ,
952+ monointernalhashtable_size : 0x18 ,
953+ monoclassdef_next_class_cache : 0x100 ,
954+ monoclassdef_klass : 0x0 ,
955+ monoclass_name : 0x40 ,
956+ monoclass_name_space : 0x48 ,
957+ monoclass_fields : 0x90 ,
958+ monoclassdef_field_count : 0xF8 ,
959+ monoclass_runtime_info : 0xC8 ,
960+ monoclass_vtable_size : 0x54 ,
961+ monoclass_parent : 0x28 ,
962+ monoclassfield_name : 0x8 ,
963+ monoclassfield_offset : 0x18 ,
964+ monoclassruntimeinfo_domain_vtables : 0x8 ,
965+ monovtable_vtable : 0x48 ,
966+ monoclassfieldalignment : 0x20 ,
967+ } ) ,
968+ } ,
969+ ( PointerSize :: Bit32 , BinaryFormat :: PE ) => match version {
846970 Version :: V1 => Some ( & Self {
847971 monoassembly_aname : 0x8 ,
848972 monoassembly_image : 0x40 ,
@@ -885,6 +1009,8 @@ impl Offsets {
8851009 monovtable_vtable : 0x28 ,
8861010 monoclassfieldalignment : 0x10 ,
8871011 } ) ,
1012+ // 32-bit PE V2 matches Unity2018_4_10_x86_PE_Offsets from
1013+ // https://github.com/hackf5/unityspy/blob/master/src/HackF5.UnitySpy/Offsets/MonoLibraryOffsets.cs#L12
8881014 Version :: V2 => Some ( & Self {
8891015 monoassembly_aname : 0x8 ,
8901016 monoassembly_image : 0x44 ,
@@ -933,6 +1059,13 @@ impl Offsets {
9331059 }
9341060}
9351061
1062+ #[ derive( Copy , Clone , PartialEq , Hash , Debug ) ]
1063+ #[ non_exhaustive]
1064+ enum BinaryFormat {
1065+ PE ,
1066+ ELF ,
1067+ }
1068+
9361069/// The version of Mono that was used for the game. These don't correlate to the
9371070/// Mono version numbers.
9381071#[ derive( Copy , Clone , PartialEq , Hash , Debug ) ]
@@ -948,7 +1081,9 @@ pub enum Version {
9481081}
9491082
9501083fn detect_version ( process : & Process ) -> Option < Version > {
951- if process. get_module_address ( "mono.dll" ) . is_ok ( ) {
1084+ if process. get_module_address ( "mono.dll" ) . is_ok ( )
1085+ || process. get_module_address ( "libmono.so" ) . is_ok ( )
1086+ {
9521087 // If the module mono.dll is present, then it's either V1 or V1Cattrs.
9531088 // In order to distinguish between them, we check the first class listed in the
9541089 // default Assembly-CSharp image and check for the pointer to its name, assuming it's using V1.
@@ -972,11 +1107,19 @@ fn detect_version(process: &Process) -> Option<Version> {
9721107 } ) ;
9731108 }
9741109
975- let unity_module = {
976- let address = process. get_module_address ( "UnityPlayer.dll" ) . ok ( ) ?;
977- let range = pe:: read_size_of_image ( process, address) ? as u64 ;
978- ( address, range)
979- } ;
1110+ let unity_module = [
1111+ ( "UnityPlayer.dll" , BinaryFormat :: PE ) ,
1112+ ( "UnityPlayer.so" , BinaryFormat :: ELF ) ,
1113+ ]
1114+ . into_iter ( )
1115+ . find_map ( |( name, format) | match format {
1116+ BinaryFormat :: PE => {
1117+ let address = process. get_module_address ( name) . ok ( ) ?;
1118+ let range = pe:: read_size_of_image ( process, address) ? as u64 ;
1119+ Some ( ( address, range) )
1120+ }
1121+ BinaryFormat :: ELF => process. get_module_range ( name) . ok ( ) ,
1122+ } ) ?;
9801123
9811124 const SIG_202X : Signature < 6 > = Signature :: new ( "00 32 30 32 ?? 2E" ) ;
9821125
0 commit comments