/// <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(); state.fptr1 = site; // 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 override void OnLevelLoaded(LoadMode mode) { _mode = mode; if (mode != LoadMode.NewGame && mode != LoadMode.LoadGame) return; base.OnLevelLoaded(mode); if (mode == LoadMode.LoadGame || mode == LoadMode.NewGame) { redirectState = RedirectionHelper.RedirectCalls( typeof(BulldozeTool).GetMethod("TryDeleteBuilding", BindingFlags.Instance | BindingFlags.NonPublic), typeof(CustomBuldozeTool).GetMethod("TryDeleteBuilding", BindingFlags.Instance | BindingFlags.Public)); ModLogger.Debug("Buildoze tool has been detoured"); } }
public static void RevertRedirect(RedirectCallsState state) { //var fptr1 = from.MethodHandle.GetFunctionPointer(); RevertJumpTo(state); }
private static void RevertJumpTo(RedirectCallsState state) { IntPtr site = state.fptr1; 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; } }