| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- using DotNetDetour;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using System.Linq;
- namespace MonoHook
- {
- public unsafe abstract class CodePatcher
- {
- public bool isValid { get; protected set; }
- protected void* _pTarget, _pReplace, _pProxy;
- protected int _jmpCodeSize;
- protected byte[] _targetHeaderBackup;
- public CodePatcher(IntPtr target, IntPtr replace, IntPtr proxy, int jmpCodeSize)
- {
- _pTarget = target.ToPointer();
- _pReplace = replace.ToPointer();
- _pProxy = proxy.ToPointer();
- _jmpCodeSize = jmpCodeSize;
- }
- public void ApplyPatch()
- {
- BackupHeader();
- EnableAddrModifiable();
- PatchTargetMethod();
- PatchProxyMethod();
- FlushICache();
- }
- public void RemovePatch()
- {
- if (_targetHeaderBackup == null)
- return;
- EnableAddrModifiable();
- RestoreHeader();
- FlushICache();
- }
- protected void BackupHeader()
- {
- if (_targetHeaderBackup != null)
- return;
- uint requireSize = LDasm.SizeofMinNumByte(_pTarget, _jmpCodeSize);
- _targetHeaderBackup = new byte[requireSize];
- fixed (void* ptr = _targetHeaderBackup)
- HookUtils.MemCpy(ptr, _pTarget, _targetHeaderBackup.Length);
- }
- protected void RestoreHeader()
- {
- if (_targetHeaderBackup == null)
- return;
- HookUtils.MemCpy_Jit(_pTarget, _targetHeaderBackup);
- }
- protected void PatchTargetMethod()
- {
- byte[] buff = GenJmpCode(_pTarget, _pReplace);
- HookUtils.MemCpy_Jit(_pTarget, buff);
- }
- protected void PatchProxyMethod()
- {
- if (_pProxy == null)
- return;
- // copy target's code to proxy
- HookUtils.MemCpy_Jit(_pProxy, _targetHeaderBackup);
- // jmp to target's new position
- long jmpFrom = (long)_pProxy + _targetHeaderBackup.Length;
- long jmpTo = (long)_pTarget + _targetHeaderBackup.Length;
- byte[] buff = GenJmpCode((void*)jmpFrom, (void*)jmpTo);
- HookUtils.MemCpy_Jit((void*)jmpFrom, buff);
- }
- protected void FlushICache()
- {
- HookUtils.FlushICache(_pTarget, _targetHeaderBackup.Length);
- HookUtils.FlushICache(_pProxy, _targetHeaderBackup.Length * 2);
- }
- protected abstract byte[] GenJmpCode(void* jmpFrom, void* jmpTo);
- #if ENABLE_HOOK_DEBUG
- protected string PrintAddrs()
- {
- if (IntPtr.Size == 4)
- return $"target:0x{(uint)_pTarget:x}, replace:0x{(uint)_pReplace:x}, proxy:0x{(uint)_pProxy:x}";
- else
- return $"target:0x{(ulong)_pTarget:x}, replace:0x{(ulong)_pReplace:x}, proxy:0x{(ulong)_pProxy:x}";
- }
- #endif
- private void EnableAddrModifiable()
- {
- HookUtils.SetAddrFlagsToRWX(new IntPtr(_pTarget), _targetHeaderBackup.Length);
- HookUtils.SetAddrFlagsToRWX(new IntPtr(_pProxy), _targetHeaderBackup.Length + _jmpCodeSize);
- }
- }
- public unsafe class CodePatcher_x86 : CodePatcher
- {
- protected static readonly byte[] s_jmpCode = new byte[] // 5 bytes
- {
- 0xE9, 0x00, 0x00, 0x00, 0x00, // jmp $val ; $val = $dst - $src - 5
- };
- public CodePatcher_x86(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length) { }
- protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
- {
- byte[] ret = new byte[s_jmpCode.Length];
- int val = (int)jmpTo - (int)jmpFrom - 5;
- fixed(void * p = &ret[0])
- {
- byte* ptr = (byte*)p;
- *ptr = 0xE9;
- int* pOffset = (int*)(ptr + 1);
- *pOffset = val;
- }
- return ret;
- }
- }
- /// <summary>
- /// x64下2G 内的跳转
- /// </summary>
- public unsafe class CodePatcher_x64_near : CodePatcher_x86 // x64_near pathcer code is same to x86
- {
- public CodePatcher_x64_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy) { }
- }
- /// <summary>
- /// x64下距离超过2G的跳转
- /// </summary>
- public unsafe class CodePatcher_x64_far : CodePatcher
- {
- protected static readonly byte[] s_jmpCode = new byte[] // 12 bytes
- {
- // 由于 rax 会被函数作为返回值修改,并且不会被做为参数使用,因此修改是安全的
- 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, <jmpTo>
- 0x50, // push rax
- 0xC3 // ret
- };
- //protected static readonly byte[] s_jmpCode2 = new byte[] // 14 bytes
- //{
- // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <jmpTo>
- // 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF // jmp [rip - 0xe]
- //};
- public CodePatcher_x64_far(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length) { }
- protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
- {
- byte[] ret = new byte[s_jmpCode.Length];
- fixed (void* p = &ret[0])
- {
- byte* ptr = (byte*)p;
- *ptr++ = 0x48;
- *ptr++ = 0xB8;
- *(long*)ptr = (long)jmpTo;
- ptr += 8;
- *ptr++ = 0x50;
- *ptr++ = 0xC3;
- }
- return ret;
- }
- }
- public unsafe class CodePatcher_arm32_near : CodePatcher
- {
- private static readonly byte[] s_jmpCode = new byte[] // 4 bytes
- {
- 0x00, 0x00, 0x00, 0xEA, // B $val ; $val = (($dst - $src) / 4 - 2) & 0x1FFFFFF
- };
- public CodePatcher_arm32_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
- {
- if (Math.Abs((long)target - (long)replace) >= ((1 << 25) - 1))
- throw new ArgumentException("address offset of target and replace must less than ((1 << 25) - 1)");
- #if ENABLE_HOOK_DEBUG
- Debug.Log($"CodePatcher_arm32_near: {PrintAddrs()}");
- #endif
- }
- protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
- {
- byte[] ret = new byte[s_jmpCode.Length];
- int val = ((int)jmpTo - (int)jmpFrom) / 4 - 2;
- fixed (void* p = &ret[0])
- {
- byte* ptr = (byte*)p;
- *ptr++ = (byte)val;
- *ptr++ = (byte)(val >> 8);
- *ptr++ = (byte)(val >> 16);
- *ptr++ = 0xEA;
- }
- return ret;
- }
- }
- public unsafe class CodePatcher_arm32_far : CodePatcher
- {
- private static readonly byte[] s_jmpCode = new byte[] // 8 bytes
- {
- 0x04, 0xF0, 0x1F, 0xE5, // LDR PC, [PC, #-4]
- 0x00, 0x00, 0x00, 0x00, // $val
- };
- public CodePatcher_arm32_far(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
- {
- if (Math.Abs((long)target - (long)replace) < ((1 << 25) - 1))
- throw new ArgumentException("address offset of target and replace must larger than ((1 << 25) - 1), please use InstructionModifier_arm32_near instead");
- #if ENABLE_HOOK_DEBUG
- Debug.Log($"CodePatcher_arm32_far: {PrintAddrs()}");
- #endif
- }
- protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
- {
- byte[] ret = new byte[s_jmpCode.Length];
- fixed (void* p = &ret[0])
- {
- uint* ptr = (uint*)p;
- *ptr++ = 0xE51FF004;
- *ptr = (uint)jmpTo;
- }
- return ret;
- }
- }
- /// <summary>
- /// arm64 下 ±128MB 范围内的跳转
- /// </summary>
- public unsafe class CodePatcher_arm64_near : CodePatcher
- {
- private static readonly byte[] s_jmpCode = new byte[] // 4 bytes
- {
- /*
- * from 0x14 to 0x17 is B opcode
- * offset bits is 26
- * https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/B--Branch-
- */
- 0x00, 0x00, 0x00, 0x14, // B $val ; $val = (($dst - $src)/4) & 7FFFFFF
- };
- public CodePatcher_arm64_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
- {
- if (Math.Abs((long)target - (long)replace) >= ((1 << 26) - 1) * 4)
- throw new ArgumentException("address offset of target and replace must less than (1 << 26) - 1) * 4");
- #if ENABLE_HOOK_DEBUG
- Debug.Log($"CodePatcher_arm64: {PrintAddrs()}");
- #endif
- }
- protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
- {
- byte[] ret = new byte[s_jmpCode.Length];
- int val = (int)((long)jmpTo - (long)jmpFrom) / 4;
- fixed (void* p = &ret[0])
- {
- byte* ptr = (byte*)p;
- *ptr++ = (byte)val;
- *ptr++ = (byte)(val >> 8);
- *ptr++ = (byte)(val >> 16);
- byte last = (byte)(val >> 24);
- last &= 0b11;
- last |= 0x14;
- *ptr = last;
- }
- return ret;
- }
- }
- /// <summary>
- /// arm64 远距离跳转
- /// </summary>
- public unsafe class CodePatcher_arm64_far : CodePatcher
- {
- private static readonly byte[] s_jmpCode = new byte[] // 20 bytes(字节数过多,太危险了,不建议使用)
- {
- /*
- * ADR: https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/ADR--Form-PC-relative-address-
- * BR: https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/BR--Branch-to-Register-
- */
- 0x6A, 0x00, 0x00, 0x10, // ADR X10, #C
- 0x4A, 0x01, 0x40, 0xF9, // LDR X10, [X10,#0]
- 0x40, 0x01, 0x1F, 0xD6, // BR X10
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // $dst
- };
- public CodePatcher_arm64_far(IntPtr target, IntPtr replace, IntPtr proxy, int jmpCodeSize) : base(target, replace, proxy, jmpCodeSize)
- {
- }
- protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
- {
- throw new NotImplementedException();
- }
- }
- }
|