public static void Install() { if (_applied) { return; } try { var refreshDet = new Detour(AccessTools.Method(AccessTools.Inner(typeof(ILHook), "Context"), "Refresh"), AccessTools.Method(typeof(StackTraceFixes), nameof(OnILChainRefresh))); _origRefresh = refreshDet.GenerateTrampoline <Action <object> >(); var getMethodDet = new Detour(AccessTools.Method(typeof(StackFrame), nameof(StackFrame.GetMethod)), AccessTools.Method(typeof(StackTraceFixes), nameof(GetMethodFix))); _origGetMethod = getMethodDet.GenerateTrampoline <Func <StackFrame, MethodBase> >(); var nat = new NativeDetour(AccessTools.Method(typeof(Assembly), nameof(Assembly.GetExecutingAssembly)), AccessTools.Method(typeof(StackTraceFixes), nameof(GetAssemblyFix))); _realGetAss = nat.GenerateTrampoline <Func <Assembly> >(); } catch (Exception e) { Logger.LogText(Logger.LogChannel.Error, $"Failed to apply stack trace fix: ({e.GetType().FullName}) {e.Message}"); } _applied = true; }
public static void Install() { if (_applied) { return; } var refreshDet = new Detour(AccessTools.Method(AccessTools.Inner(typeof(ILHook), "Context"), "Refresh"), AccessTools.Method(typeof(StackTraceFixes), nameof(OnILChainRefresh))); _origRefresh = refreshDet.GenerateTrampoline <Action <object> >(); var getMethodDet = new Detour(AccessTools.Method(typeof(StackFrame), nameof(StackFrame.GetMethod)), AccessTools.Method(typeof(StackTraceFixes), nameof(GetMethodFix))); _origGetMethod = getMethodDet.GenerateTrampoline <Func <StackFrame, MethodBase> >(); var nat = new NativeDetour(AccessTools.Method(typeof(Assembly), nameof(Assembly.GetExecutingAssembly)), AccessTools.Method(typeof(StackTraceFixes), nameof(GetAssemblyFix))); _realGetAss = nat.GenerateTrampoline <Func <Assembly> >(); _applied = true; }
public static void InstallDispatchHandler(MethodBase method) { var method_token = MethodToken(method); if (_DispatchHandlers.Contains(method_token)) { _Logger.Debug($"Not installing dispatch handler for {method.Name} ({method_token}) - it's already installed"); return; } var parms = method.GetParameters(); int ptypes_offs = 1; if (method.IsStatic) { ptypes_offs = 0; } var ptypes = new Type[parms.Length + ptypes_offs]; if (!method.IsStatic) { ptypes[0] = method.DeclaringType; } for (int i = 0; i < parms.Length; i++) { ptypes[i + ptypes_offs] = parms[i].ParameterType; } var method_returns = false; if (method is MethodInfo) { if (((MethodInfo)method).ReturnType != typeof(void)) { method_returns = true; } } var dm = new DynamicMethod( $"DISPATCH HANDLER FOR METHOD TOKEN {method_token}", method is MethodInfo ? ((MethodInfo)method).ReturnType : typeof(void), ptypes, method.Module, skipVisibility: true ); var il = dm.GetILGenerator(); var loc_args = il.DeclareLocal(typeof(object[])); loc_args.SetLocalSymInfo("args"); var loc_target = il.DeclareLocal(typeof(object)); loc_target.SetLocalSymInfo("target"); if (method.IsStatic) { il.Emit(OpCodes.Ldnull); } else { il.Emit(OpCodes.Ldarg_0); } il.Emit(OpCodes.Stloc, loc_target); int ary_size = ptypes.Length - 1; if (method.IsStatic) { ary_size = ptypes.Length; } il.Emit(OpCodes.Ldc_I4, ary_size); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc, loc_args); for (int i = 0; i < ary_size; i++) { il.Emit(OpCodes.Ldloc, loc_args); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + ptypes_offs); if (ptypes[i + ptypes_offs].IsValueType) { il.Emit(OpCodes.Box, ptypes[i + ptypes_offs]); } il.Emit(OpCodes.Stelem, typeof(object)); } il.Emit(OpCodes.Ldc_I8, method_token); il.Emit(OpCodes.Ldloc, loc_target); il.Emit(OpCodes.Ldloc, loc_args); il.Emit(OpCodes.Call, _HandleDispatchMethod); if (!method_returns) { il.Emit(OpCodes.Pop); } if (method_returns && method is MethodInfo && ((MethodInfo)method).ReturnType.IsValueType) { il.Emit(OpCodes.Unbox_Any, ((MethodInfo)method).ReturnType); } il.Emit(OpCodes.Ret); var detour = new Detour( from: method, to: dm ); _Detours[method_token] = detour; _Trampolines[method_token] = detour.GenerateTrampoline(); _DispatchHandlers.Add(method_token); _Logger.Debug($"Installed dispatch handler for {method.Name} (token {method_token})"); }
public void TestDetours() { Console.WriteLine("Detours: none"); TestObject.TestStep(5, 6, 8); Console.WriteLine(); // Three examples of using Detour. // Note that if you need non-layered, low-level hooks, you can use NativeDetour instead. // This is also why the variable type is IDetour. // System.Linq.Expressions, thanks to leo (HookedMethod) for telling me about how to (ab)use MethodCallExpression! IDetour detourTestMethodA = new Detour( () => default(TestObject).TestMethod(default(int), default(int)), () => TestMethod_A(default(TestObject), default(int), default(int)) ); // Method references as delegates. IDetour detourTestStaticMethodA = new Detour <Func <int, int, int> >( TestObject.TestStaticMethod, TestStaticMethod_A ); // MethodBase, old syntax. IDetour detourTestVoidMethodA = new Detour( typeof(TestObject).GetMethod("TestVoidMethod", BindingFlags.Static | BindingFlags.Public), typeof(DetourTest).GetMethod("TestVoidMethod_A", BindingFlags.Static | BindingFlags.Public) ); Console.WriteLine("Detours: A"); TestObject.TestStep(42, 12, 1); Console.WriteLine("Testing trampoline, should invoke orig, TestVoidMethod(2, 3)"); detourTestVoidMethodA.GenerateTrampoline <Action <int, int> >()(2, 3); Console.WriteLine(); IDetour detourTestMethodB = new Detour( () => default(TestObject).TestMethod(default(int), default(int)), () => TestMethod_B(default(TestObject), default(int), default(int)) ); IDetour detourTestStaticMethodB = new Detour( () => TestObject.TestStaticMethod(default(int), default(int)), () => TestStaticMethod_B(default(int), default(int)) ); IDetour detourTestVoidMethodB = new Detour( () => TestObject.TestVoidMethod(default(int), default(int)), () => TestVoidMethod_B(default(int), default(int)) ); Console.WriteLine("Detours: A + B"); TestObject.TestStep(120, 8, 2); Console.WriteLine("Testing trampoline, should invoke hook A, TestVoidMethod(2, 3)"); Action <int, int> trampolineTestVoidMethodB = detourTestVoidMethodB.GenerateTrampoline <Action <int, int> >(); trampolineTestVoidMethodB(2, 3); Console.WriteLine(); detourTestMethodA.Undo(); detourTestStaticMethodA.Undo(); detourTestVoidMethodA.Undo(); Console.WriteLine("Detours: B"); TestObject.TestStep(120, 8, 2); Console.WriteLine("Testing trampoline, should invoke orig, TestVoidMethod(2, 3)"); trampolineTestVoidMethodB(2, 3); Console.WriteLine(); detourTestMethodB.Undo(); detourTestStaticMethodB.Undo(); detourTestVoidMethodB.Undo(); Console.WriteLine("Detours: none"); TestObject.TestStep(5, 6, 8); Console.WriteLine(); }