2
2
//! backend.
3
3
4
4
use 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 ,
7
10
} ;
8
11
use core:: {
9
12
array,
@@ -40,34 +43,61 @@ impl Module {
40
43
/// correct for this function to work. If you don't know the version in
41
44
/// advance, use [`attach_auto_detect`](Self::attach_auto_detect) instead.
42
45
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) ?,
50
60
} ;
51
61
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
+ } ;
61
84
62
- let assemblies: Address = match pointer_size {
63
- PointerSize :: Bit64 => {
85
+ let assemblies: Address = match ( pointer_size, format ) {
86
+ ( PointerSize :: Bit64 , BinaryFormat :: PE ) => {
64
87
const SIG_MONO_64 : Signature < 3 > = Signature :: new ( "48 8B 0D" ) ;
65
88
let scan_address: Address = SIG_MONO_64
66
89
. scan_process_range ( process, ( root_domain_function_address, 0x100 ) ) ?
67
90
+ 3 ;
68
91
scan_address + 0x4 + process. read :: < i32 > ( scan_address) . ok ( ) ?
69
92
}
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 ) => {
71
101
const SIG_32_1 : Signature < 2 > = Signature :: new ( "FF 35" ) ;
72
102
const SIG_32_2 : Signature < 2 > = Signature :: new ( "8B 0D" ) ;
73
103
@@ -754,9 +784,13 @@ struct Offsets {
754
784
}
755
785
756
786
impl 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 {
760
794
Version :: V1 => Some ( & Self {
761
795
monoassembly_aname : 0x10 ,
762
796
monoassembly_image : 0x58 ,
@@ -799,6 +833,8 @@ impl Offsets {
799
833
monovtable_vtable : 0x48 ,
800
834
monoclassfieldalignment : 0x20 ,
801
835
} ) ,
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
802
838
Version :: V2 => Some ( & Self {
803
839
monoassembly_aname : 0x10 ,
804
840
monoassembly_image : 0x60 ,
@@ -842,7 +878,95 @@ impl Offsets {
842
878
monoclassfieldalignment : 0x20 ,
843
879
} ) ,
844
880
} ,
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 {
846
970
Version :: V1 => Some ( & Self {
847
971
monoassembly_aname : 0x8 ,
848
972
monoassembly_image : 0x40 ,
@@ -885,6 +1009,8 @@ impl Offsets {
885
1009
monovtable_vtable : 0x28 ,
886
1010
monoclassfieldalignment : 0x10 ,
887
1011
} ) ,
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
888
1014
Version :: V2 => Some ( & Self {
889
1015
monoassembly_aname : 0x8 ,
890
1016
monoassembly_image : 0x44 ,
@@ -933,6 +1059,13 @@ impl Offsets {
933
1059
}
934
1060
}
935
1061
1062
+ #[ derive( Copy , Clone , PartialEq , Hash , Debug ) ]
1063
+ #[ non_exhaustive]
1064
+ enum BinaryFormat {
1065
+ PE ,
1066
+ ELF ,
1067
+ }
1068
+
936
1069
/// The version of Mono that was used for the game. These don't correlate to the
937
1070
/// Mono version numbers.
938
1071
#[ derive( Copy , Clone , PartialEq , Hash , Debug ) ]
@@ -948,7 +1081,9 @@ pub enum Version {
948
1081
}
949
1082
950
1083
fn 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
+ {
952
1087
// If the module mono.dll is present, then it's either V1 or V1Cattrs.
953
1088
// In order to distinguish between them, we check the first class listed in the
954
1089
// 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> {
972
1107
} ) ;
973
1108
}
974
1109
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
+ } ) ?;
980
1123
981
1124
const SIG_202X : Signature < 6 > = Signature :: new ( "00 32 30 32 ?? 2E" ) ;
982
1125
0 commit comments