/// <summary> /// Primitive patching. Inserts a jump to 'target' at 'site'. Works even if both methods' /// callers have already been compiled. /// </summary> /// <param name="site"></param> /// <param name="target"></param> private static RedirectCallsState PatchJumpTo(IntPtr site, IntPtr target) { RedirectCallsState state = new RedirectCallsState(); // R11 is volatile. unsafe { byte* sitePtr = (byte*)site.ToPointer(); state.a = *sitePtr; state.b = *(sitePtr + 1); state.c = *(sitePtr + 10); state.d = *(sitePtr + 11); state.e = *(sitePtr + 12); state.f = *((ulong*)(sitePtr + 2)); *sitePtr = 0x49; // mov r11, target *(sitePtr + 1) = 0xBB; *((ulong*)(sitePtr + 2)) = (ulong)target.ToInt64(); *(sitePtr + 10) = 0x41; // jmp r11 *(sitePtr + 11) = 0xFF; *(sitePtr + 12) = 0xE3; } return state; }
public LoadingExtension() { Debug.Log("LoadingExtension constructor entry."); Debug.Log("Setting ToolMode"); ToolMode = TrafficManagerMode.None; Debug.Log("Init RevertMethods"); RevertMethods = new RedirectCallsState[8]; Debug.Log("Setting Despawn to False"); DespawnEnabled = true; }
private static void RevertJumpTo(IntPtr site, RedirectCallsState state) { unsafe { byte* sitePtr = (byte*)site.ToPointer(); *sitePtr = state.a; // mov r11, target *(sitePtr + 1) = state.b; *((ulong*)(sitePtr + 2)) = state.f; *(sitePtr + 10) = state.c; // jmp r11 *(sitePtr + 11) = state.d; *(sitePtr + 12) = state.e; } }
public static void RevertRedirect(MethodInfo from, RedirectCallsState state) { var fptr1 = from.MethodHandle.GetFunctionPointer(); RevertJumpTo(fptr1, state); }
public override void OnCreated(ILoading loading) { SelfDestruct.DestructOldInstances(this); base.OnCreated(loading); Debug.Log("Setting ToolMode"); ToolMode = TrafficManagerMode.None; Debug.Log("Init RevertMethods"); RevertMethods = new RedirectCallsState[8]; Debug.Log("Setting Despawn to False"); DespawnEnabled = true; Debug.Log("Init DetourInited"); DetourInited = false; Debug.Log("Init Custom PathManager"); CustomPathManager = new CustomPathManager(); }
public Detour(MethodInfo originalMethod, MethodInfo customMethod) { OriginalMethod = originalMethod; CustomMethod = customMethod; Redirect = RedirectionHelper.RedirectCalls(originalMethod, customMethod); }
private void InitDetours() { // TODO realize detouring with annotations if (DetourInited) { return; } Log.Info("Init detours"); bool detourFailed = false; try { Log.Info("Deploying Harmony patches"); #if DEBUG HarmonyInstance.DEBUG = true; #endif Assembly assembly = Assembly.GetExecutingAssembly(); HarmonyMethodStates.Clear(); // Harmony attribute-driven patching Log.Info($"Performing Harmony attribute-driven patching"); HarmonyInst = HarmonyInstance.Create(HARMONY_ID); HarmonyInst.PatchAll(assembly); foreach (Type type in assembly.GetTypes()) { object[] attributes = type.GetCustomAttributes(typeof(HarmonyPatch), true); if (attributes.Length <= 0) { continue; } foreach (object attr in attributes) { HarmonyPatch harmonyPatchAttr = (HarmonyPatch)attr; MethodBase info = HarmonyUtil.GetOriginalMethod(harmonyPatchAttr.info); IntPtr ptr = info.MethodHandle.GetFunctionPointer(); RedirectCallsState state = RedirectionHelper.GetState(ptr); HarmonyMethodStates[info] = state; } } // Harmony manual patching Log.Info($"Performing Harmony manual patching"); foreach (ManualHarmonyPatch manualPatch in ManualHarmonyPatches) { Log.InfoFormat( "Manually patching method {0}.{1}. Prefix: {2}, Postfix: {3}, Transpiler: {4}", manualPatch.method.DeclaringType.FullName, manualPatch.method.Name, manualPatch.prefix?.method, manualPatch.postfix?.method, manualPatch.transpiler?.method); HarmonyInst.Patch( manualPatch.method, manualPatch.prefix, manualPatch.postfix, manualPatch.transpiler); IntPtr ptr = manualPatch.method.MethodHandle.GetFunctionPointer(); RedirectCallsState state = RedirectionHelper.GetState(ptr); HarmonyMethodStates[manualPatch.method] = state; } } catch (Exception e) { Log.Error("Could not deploy Harmony patches"); Log.Info(e.ToString()); Log.Info(e.StackTrace); detourFailed = true; } try { Log.Info("Deploying attribute-driven detours"); DetouredMethodStates = AssemblyRedirector.Deploy(); } catch (Exception e) { Log.Error("Could not deploy attribute-driven detours"); Log.Info(e.ToString()); Log.Info(e.StackTrace); detourFailed = true; } if (detourFailed) { Log.Info("Detours failed"); Singleton <SimulationManager> .instance.m_ThreadingWrapper.QueueMainThread( () => { UIView.library .ShowModal <ExceptionPanel>("ExceptionPanel") .SetMessage( "TM:PE failed to load", "Traffic Manager: President Edition failed to load. You can " + "continue playing but it's NOT recommended. Traffic Manager will " + "not work as expected.", true); }); } else { Log.Info("Detours successful"); } DetourInited = true; }
public override void OnBeforeSimulationFrame() { base.OnBeforeSimulationFrame(); if (firstFrame) { firstFrame = false; Log.Info("ThreadingExtension.OnBeforeSimulationFrame: First frame detected. Checking detours."); List <string> missingDetours = new List <string>(); foreach (Detour detour in Detours) { if (!RedirectionHelper.IsRedirected( detour.OriginalMethod, detour.CustomMethod)) { missingDetours.Add( string.Format( "<Manual> {0}.{1} with {2} parameters ({3})", detour.OriginalMethod.DeclaringType.Name, detour.OriginalMethod.Name, detour.OriginalMethod.GetParameters().Length, detour.OriginalMethod.DeclaringType.AssemblyQualifiedName)); } } foreach (KeyValuePair <MethodBase, RedirectCallsState> entry in HarmonyMethodStates) { MethodBase method = entry.Key; RedirectCallsState oldState = entry.Value; RedirectCallsState newState = RedirectionHelper.GetState(method.MethodHandle.GetFunctionPointer()); if (!oldState.Equals(newState)) { missingDetours.Add( string.Format( "<Harmony> {0}.{1} with {2} parameters ({3})", method.DeclaringType.Name, method.Name, method.GetParameters().Length, method.DeclaringType.AssemblyQualifiedName)); } } Log.Info($"ThreadingExtension.OnBeforeSimulationFrame: First frame detected. " + $"Detours checked. Result: {missingDetours.Count} missing detours"); if (missingDetours.Count > 0) { string error = "Traffic Manager: President Edition detected an incompatibility with another " + "mod! You can continue playing but it's NOT recommended. Traffic Manager will " + "not work as expected. See TMPE.log for technical details."; Log.Error(error); string log = "The following methods were overriden by another mod:"; foreach (string missingDetour in missingDetours) { log += $"\n\t{missingDetour}"; } Log.Info(log); if (GlobalConfig.Instance.Main.ShowCompatibilityCheckErrorMessage) { Prompt.Error("TM:PE Incompatibility Issue", error); } } } if (Options.timedLightsEnabled) { tlsMan.SimulationStep(); } }