forked from Xkein/YRDynamicPatcher
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHook.cs
More file actions
336 lines (297 loc) · 9.01 KB
/
Hook.cs
File metadata and controls
336 lines (297 loc) · 9.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using dword = System.UInt32;
using word = System.UInt16;
namespace DynamicPatcher
{
/// <summary>Specifies the hook behavior.</summary>
public enum HookType
{
/// <summary>Specifies that the hook is a ares style hook.</summary>
AresHook,
/// <summary>Specifies that the hook is just a jump to destination.</summary>
/// <remarks>This type hook can not hook a hooked address.</remarks>
SimpleJumpToRet,
/// <summary>Specifies that the hook is just a jump to hook.</summary>
/// <remarks>This type hook can not hook a hooked address.</remarks>
DirectJumpToHook,
/// <summary>Specifies that the hook is to write bytes to address.</summary>
/// <remarks>This type hook can not hook a hooked address.</remarks>
WriteBytesHook,
/// <summary>Specifies that the hook is to overwrite exported target reference address.</summary>
ExportTableHook,
/// <summary>Specifies that the hook is to overwrite imported target reference address.</summary>
ImportTableHook
};
/// <summary>Controls the hook behavior and data.</summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Delegate,
Inherited = false, AllowMultiple = true)]
public sealed class HookAttribute : Attribute
{
/// <summary>The hook behavior.</summary>
public HookType Type { get; }
/// <summary>The absolute address to hook (write a jump).</summary>
/// <remarks>If Module was set, set RelativeAddress is safer.</remarks>
public int Address
{
get
{
switch (Type)
{
case HookType.ExportTableHook:
return Helpers.GetEATAddress(Module, TargetName);
case HookType.ImportTableHook:
return Helpers.GetIATAddress(Module, TargetName);
}
if (string.IsNullOrEmpty(Module))
{
return _address;
}
return (int)Helpers.GetProcessModule(Module).BaseAddress + RelativeAddress;
}
set
{
switch (Type)
{
case HookType.ExportTableHook:
case HookType.ImportTableHook:
throw new InvalidOperationException($"you can not set address for {Type}.");
}
if (string.IsNullOrEmpty(Module))
{
_address = value;
return;
}
// set RelativeAddress
_address = value - (int)Helpers.GetProcessModule(Module).BaseAddress;
}
}
/// <summary>The relative address to hook.</summary>
/// <remarks>If Module was not set, set Address is safer.</remarks>
public int RelativeAddress
{
get
{
switch (Type)
{
case HookType.ExportTableHook:
case HookType.ImportTableHook:
return Address - (int)Helpers.GetProcessModule(Module).BaseAddress;
}
if (string.IsNullOrEmpty(Module))
{
return Address - (int)Helpers.GetProcessModule().BaseAddress;
}
return _address;
}
set
{
switch (Type)
{
case HookType.ExportTableHook:
case HookType.ImportTableHook:
throw new InvalidOperationException($"you can not set address for {Type}.");
}
if (string.IsNullOrEmpty(Module))
{
// set Address
_address = (int)Helpers.GetProcessModule().BaseAddress + value;
return;
}
// set RelativeAddress
_address = value;
}
}
/// <summary>The number of bytes to store and overwrite.</summary>
public int Size
{
get
{
switch (Type)
{
case HookType.ExportTableHook:
case HookType.ImportTableHook:
return sizeof(uint);
}
return _size;
}
set
{
switch (Type)
{
case HookType.ExportTableHook:
case HookType.ImportTableHook:
throw new InvalidOperationException($"you can not set size for {Type}.");
}
_size = value;
}
}
/// <summary>The module to hook.</summary>
public string Module { get; }
/// <summary>The export or import name of target.</summary>
public string TargetName { get; set; }
// not necessary
// public string Name { get; set; }
/// <summary> Initializes a new instance of the DynamicPatcher.HookAttribute class with the specified HookType.</summary>
public HookAttribute(HookType type, string module = null)
{
Type = type;
Module = module;
}
private int _address;
private int _size;
}
class HookInfo : IComparable
{
public static bool TryCatchCallable { get; set; } = false;
public MemberInfo Member { get; private set; }
public HookAttribute HookAttribute { get; private set; }
public FieldInfo Field { get => Member as FieldInfo; }
public EventInfo Event { get => Member as EventInfo; }
public MethodInfo Method { get => Member as MethodInfo; }
public PropertyInfo Property { get => Member as PropertyInfo; }
public HookTransferStation TransferStation { get; set; } = null;
public HookInfo(MemberInfo member, HookAttribute hookAttribute)
{
Member = member;
HookAttribute = hookAttribute;
}
public HookAttribute GetHookAttribute()
{
return HookAttribute;
}
static public HookAttribute[] GetHookAttributes(MemberInfo member)
{
return member.GetCustomAttributes(typeof(HookAttribute), false) as HookAttribute[];
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe delegate dword AresHookFunction(REGISTERS* R);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate dword AresHookDelegate(IntPtr R);
public object GetReturnValue()
{
switch (Member.MemberType)
{
case MemberTypes.Method:
case MemberTypes.Event:
case MemberTypes.Field when typeof(Delegate).IsAssignableFrom(Field.FieldType):
case MemberTypes.Property when typeof(Delegate).IsAssignableFrom(Property.PropertyType):
var function = GetDelegate();
return function.DynamicInvoke();
case MemberTypes.Field:
return Field.GetValue(null);
case MemberTypes.Property:
return Property.GetValue(null);
}
return null;
}
public Delegate GetDelegate(Type dlgType)
{
return Member.MemberType switch
{
MemberTypes.Method => Method.CreateDelegate(dlgType),
MemberTypes.Event => Event.RaiseMethod.CreateDelegate(dlgType),
MemberTypes.Field when typeof(Delegate).IsAssignableFrom(Field.FieldType) => Field.GetValue(null) as Delegate,
MemberTypes.Property when typeof(Delegate).IsAssignableFrom(Property.PropertyType) => Property.GetValue(null) as Delegate,
_ => null,
};
}
public Delegate GetDelegate()
{
return Member.MemberType switch
{
MemberTypes.Method or MemberTypes.Event => GetDelegate(GetHookFuntionType()),
_ => GetDelegate(null),
};
}
public Type GetHookFuntionType()
{
HookAttribute hook = GetHookAttribute();
return hook.Type switch
{
HookType.AresHook => typeof(AresHookFunction),
HookType.SimpleJumpToRet => typeof(Func<int>),
HookType.WriteBytesHook => typeof(Func<byte[]>),
HookType.DirectJumpToHook or
HookType.ExportTableHook or
HookType.ImportTableHook => Helpers.GetMethodDelegateType(Method ?? Event?.RaiseMethod),
_ => null,
};
}
// avoid GC collect
public Delegate CallableDlg { get; set; }
public Delegate GetCallableDlg()
{
if(CallableDlg != null)
{
//return CallableDlg;
}
Delegate dlg = null;
foreach (HookInfo info in TransferStation.HookInfos)
{
dlg = Delegate.Combine(dlg, info.GetDelegate());
}
if (TryCatchCallable)
{
HookAttribute hook = GetHookAttribute();
switch (hook.Type)
{
case HookType.AresHook:
var aresHook = dlg as AresHookFunction;
AresHookDelegate tryAresCallable = (IntPtr R) =>
{
try
{
unsafe
{
return aresHook.Invoke((REGISTERS*)R);
}
}
catch (Exception e)
{
Logger.LogError("hook exception caught!");
Logger.PrintException(e);
Logger.LogWarning("TransferStation unhook to run on origin code");
TransferStation.UnHook();
return (uint)hook.Address;
}
};
dlg = tryAresCallable;
break;
}
}
CallableDlg = dlg;
return dlg;
}
public IntPtr GetCallable()
{
switch (Member.MemberType)
{
case MemberTypes.Method:
case MemberTypes.Event:
case MemberTypes.Field when typeof(Delegate).IsAssignableFrom(Field.FieldType):
case MemberTypes.Property when typeof(Delegate).IsAssignableFrom(Property.PropertyType):
break;
case MemberTypes.Field:
return (IntPtr)Field.GetValue(null);
case MemberTypes.Property:
return (IntPtr)Property.GetValue(null);
}
var callableDlg = GetCallableDlg();
return Marshal.GetFunctionPointerForDelegate(callableDlg);
}
public int CompareTo(object obj)
{
return CompareTo((HookInfo)obj);
}
public int CompareTo(HookInfo obj)
{
return this.GetHookAttribute().Size.CompareTo(obj.GetHookAttribute().Size);
}
}
}