1
+ //
2
+ // Following file is essentially copy of Memory.cs file from https://github.com/pardeike/Harmony
3
+ //
4
+
5
+ using System ;
6
+ using System . Collections . Generic ;
7
+ using System . Reflection ;
8
+ using System . Reflection . Emit ;
9
+ using System . Runtime . CompilerServices ;
10
+
11
+ namespace UnityNativeTool . Internal
12
+ {
13
+ /// <summary>A low level memory helper</summary>
14
+ internal static class Detour
15
+ {
16
+ static readonly HashSet < PlatformID > WindowsPlatformIDSet = new HashSet < PlatformID >
17
+ {
18
+ PlatformID . Win32NT , PlatformID . Win32S , PlatformID . Win32Windows , PlatformID . WinCE
19
+ } ;
20
+
21
+ /// <summary>Is current environment Windows?</summary>
22
+ /// <value>True if it is Windows</value>
23
+ ///
24
+ internal static bool IsWindows => WindowsPlatformIDSet . Contains ( Environment . OSVersion . Platform ) ;
25
+
26
+ /// <summary>Unprotect memory pages</summary>
27
+ /// <param name="memory">The starting memory address</param>
28
+ /// <param name="size">The length of memory block</param>
29
+ ///
30
+ internal static void UnprotectMemory ( long memory , ulong size )
31
+ {
32
+ if ( IsWindows )
33
+ {
34
+ var success = PInvokes_Windows . VirtualProtect ( new IntPtr ( memory ) , new UIntPtr ( size ) , PInvokes_Windows . Protection . PAGE_EXECUTE_READWRITE , out var _ ) ;
35
+ if ( ! success )
36
+ throw new System . ComponentModel . Win32Exception ( ) ;
37
+ }
38
+ }
39
+
40
+ internal static void FlushCode ( long memory , ulong size )
41
+ {
42
+ if ( IsWindows )
43
+ {
44
+ var processHandle = PInvokes_Windows . GetCurrentProcess ( ) ;
45
+ var success = PInvokes_Windows . FlushInstructionCache ( processHandle , new IntPtr ( memory ) , new UIntPtr ( size ) ) ;
46
+ if ( ! success )
47
+ throw new System . ComponentModel . Win32Exception ( ) ;
48
+ }
49
+ }
50
+
51
+ /// <summary>Mark method for no inlining</summary>
52
+ /// <param name="method">The method to change</param>
53
+ unsafe public static void MarkForNoInlining ( MethodBase method )
54
+ {
55
+ // TODO for now, this only works on mono
56
+ if ( Type . GetType ( "Mono.Runtime" ) != null )
57
+ {
58
+ var iflags = ( ushort * ) ( method . MethodHandle . Value ) + 1 ;
59
+ * iflags |= ( ushort ) MethodImplOptions . NoInlining ;
60
+ }
61
+ }
62
+
63
+ /// <summary>Detours a method</summary>
64
+ /// <param name="original">The original method</param>
65
+ /// <param name="replacement">The replacement method</param>
66
+ /// <returns>An error string</returns>
67
+ ///
68
+ public unsafe static string DetourMethod ( MethodBase original , MethodBase replacement )
69
+ {
70
+ var originalCodeStart = GetMethodStart ( original , out var exception ) ;
71
+ if ( originalCodeStart == 0 )
72
+ return exception . Message ;
73
+ var patchCodeStart = GetMethodStart ( replacement , out exception ) ;
74
+ if ( patchCodeStart == 0 )
75
+ return exception . Message ;
76
+
77
+ return WriteJump ( originalCodeStart , patchCodeStart ) ;
78
+ }
79
+
80
+ /// <summary>Writes a jump to memory</summary>
81
+ /// <param name="memory">The memory address</param>
82
+ /// <param name="destination">Jump destination</param>
83
+ /// <returns>An error string</returns>
84
+ ///
85
+ public static string WriteJump ( long memory , long destination )
86
+ {
87
+ UnprotectMemory ( memory , IntPtr . Size == sizeof ( long ) ? 12u : 6u ) ;
88
+
89
+ long writtenMemoryAddress ;
90
+ if ( IntPtr . Size == sizeof ( long ) )
91
+ {
92
+ if ( CompareBytes ( memory , new byte [ ] { 0xe9 } ) )
93
+ {
94
+ var offset = ReadInt ( memory + 1 ) ;
95
+ memory += 5 + offset ;
96
+ UnprotectMemory ( memory , 12 ) ;
97
+ }
98
+
99
+ writtenMemoryAddress = memory ;
100
+ memory = WriteBytes ( memory , new byte [ ] { 0x48 , 0xB8 } ) ;
101
+ memory = WriteLong ( memory , destination ) ;
102
+ memory = WriteBytes ( memory , new byte [ ] { 0xFF , 0xE0 } ) ;
103
+ }
104
+ else
105
+ {
106
+ writtenMemoryAddress = memory ;
107
+ memory = WriteByte ( memory , 0x68 ) ;
108
+ memory = WriteInt ( memory , ( int ) destination ) ;
109
+ memory = WriteByte ( memory , 0xc3 ) ;
110
+ }
111
+
112
+ FlushCode ( writtenMemoryAddress , IntPtr . Size == sizeof ( long ) ? 12u : 6u ) ;
113
+ return null ;
114
+ }
115
+
116
+ static RuntimeMethodHandle GetRuntimeMethodHandle ( MethodBase method )
117
+ {
118
+ if ( method is DynamicMethod )
119
+ {
120
+ var noninternalInstance = BindingFlags . NonPublic | BindingFlags . Instance ;
121
+
122
+ // DynamicMethod actually generates its m_methodHandle on-the-fly and therefore
123
+ // we should call GetMethodDescriptor to force it to be created
124
+ //
125
+ var m_GetMethodDescriptor = typeof ( DynamicMethod ) . GetMethod ( "GetMethodDescriptor" , noninternalInstance ) ;
126
+ if ( m_GetMethodDescriptor != null )
127
+ return ( RuntimeMethodHandle ) m_GetMethodDescriptor . Invoke ( method , new object [ 0 ] ) ;
128
+
129
+ // .Net Core
130
+ var f_m_method = typeof ( DynamicMethod ) . GetField ( "m_method" , noninternalInstance ) ;
131
+ if ( f_m_method != null )
132
+ return ( RuntimeMethodHandle ) f_m_method . GetValue ( method ) ;
133
+
134
+ // Mono
135
+ var f_mhandle = typeof ( DynamicMethod ) . GetField ( "mhandle" , noninternalInstance ) ;
136
+ return ( RuntimeMethodHandle ) f_mhandle . GetValue ( method ) ;
137
+ }
138
+
139
+ return method . MethodHandle ;
140
+ }
141
+
142
+ /// <summary>Gets the start of a method in memory</summary>
143
+ /// <param name="method">The method</param>
144
+ /// <param name="exception">[out] Details of the exception</param>
145
+ /// <returns>The method start address</returns>
146
+ ///
147
+ public static long GetMethodStart ( MethodBase method , out Exception exception )
148
+ {
149
+ // required in .NET Core so that the method is JITed and the method start does not change
150
+ //
151
+ var handle = GetRuntimeMethodHandle ( method ) ;
152
+ try
153
+ {
154
+ RuntimeHelpers . PrepareMethod ( handle ) ;
155
+ }
156
+ catch ( Exception )
157
+ {
158
+ }
159
+
160
+ try
161
+ {
162
+ exception = null ;
163
+ return handle . GetFunctionPointer ( ) . ToInt64 ( ) ;
164
+ }
165
+ catch ( Exception ex )
166
+ {
167
+ exception = ex ;
168
+ return 0 ;
169
+ }
170
+ }
171
+
172
+ /// <summary>Compare bytes</summary>
173
+ /// <param name="memory">The memory address</param>
174
+ /// <param name="values">The bytes to compare to</param>
175
+ /// <returns>True if memory address contains the bytes</returns>
176
+ ///
177
+ internal static unsafe bool CompareBytes ( long memory , byte [ ] values )
178
+ {
179
+ var p = ( byte * ) memory ;
180
+ foreach ( var value in values )
181
+ {
182
+ if ( value != * p ) return false ;
183
+ p ++ ;
184
+ }
185
+ return true ;
186
+ }
187
+
188
+ /// <summary>Reads a byte</summary>
189
+ /// <param name="memory">The memory address</param>
190
+ /// <returns>The byte</returns>
191
+ ///
192
+ internal static unsafe byte ReadByte ( long memory )
193
+ {
194
+ var p = ( byte * ) memory ;
195
+ return * p ;
196
+ }
197
+
198
+ /// <summary>Reads an int</summary>
199
+ /// <param name="memory">The memory address</param>
200
+ /// <returns>The int</returns>
201
+ ///
202
+ internal static unsafe int ReadInt ( long memory )
203
+ {
204
+ var p = ( int * ) memory ;
205
+ return * p ;
206
+ }
207
+
208
+ /// <summary>Reads a long</summary>
209
+ /// <param name="memory">The memory address</param>
210
+ /// <returns>The long</returns>
211
+ ///
212
+ internal static unsafe long ReadLong ( long memory )
213
+ {
214
+ var p = ( long * ) memory ;
215
+ return * p ;
216
+ }
217
+
218
+ /// <summary>Writes a byte</summary>
219
+ /// <param name="memory">The memory address</param>
220
+ /// <param name="value">The byte</param>
221
+ /// <returns>Advanced memory address</returns>
222
+ ///
223
+ internal static unsafe long WriteByte ( long memory , byte value )
224
+ {
225
+ var p = ( byte * ) memory ;
226
+ * p = value ;
227
+ return memory + sizeof ( byte ) ;
228
+ }
229
+
230
+ /// <summary>Writes some bytes</summary>
231
+ /// <param name="memory">The memory address</param>
232
+ /// <param name="values">The bytes to write</param>
233
+ /// <returns>Advanced memory address</returns>
234
+ ///
235
+ internal static unsafe long WriteBytes ( long memory , byte [ ] values )
236
+ {
237
+ foreach ( var value in values )
238
+ memory = WriteByte ( memory , value ) ;
239
+ return memory ;
240
+ }
241
+
242
+ /// <summary>Writes an int</summary>
243
+ /// <param name="memory">The memory address</param>
244
+ /// <param name="value">The int</param>
245
+ /// <returns>Advanced memory address</returns>
246
+ ///
247
+ internal static unsafe long WriteInt ( long memory , int value )
248
+ {
249
+ var p = ( int * ) memory ;
250
+ * p = value ;
251
+ return memory + sizeof ( int ) ;
252
+ }
253
+
254
+ /// <summary>Writes a long</summary>
255
+ /// <param name="memory">The memory address</param>
256
+ /// <param name="value"> The long</param>
257
+ /// <returns>Advanced memory address</returns>
258
+ ///
259
+ internal static unsafe long WriteLong ( long memory , long value )
260
+ {
261
+ var p = ( long * ) memory ;
262
+ * p = value ;
263
+ return memory + sizeof ( long ) ;
264
+ }
265
+ }
266
+ }
0 commit comments