123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- using DotNetDetour;
- using System;
- using System.Diagnostics;
- using System.Reflection;
- using System.Runtime.InteropServices;
- using Unity.Collections.LowLevel.Unsafe;
- #if UNITY_EDITOR
- using UnityEditor;
- #endif
- using UnityEngine;
- using System.Runtime.CompilerServices;
- namespace MonoHook
- {
-
-
-
- public unsafe class MethodHook
- {
- public string tag;
- public bool isHooked { get; private set; }
- public bool isPlayModeHook { get; private set; }
- public MethodBase targetMethod { get; private set; }
- public MethodBase replacementMethod { get; private set; }
- public MethodBase proxyMethod { get; private set; }
- private IntPtr _targetPtr;
- private IntPtr _replacementPtr;
- private IntPtr _proxyPtr;
- private CodePatcher _codePatcher;
- #if UNITY_EDITOR && !UNITY_2020_3_OR_NEWER
-
-
-
-
-
-
- private static FieldInfo s_fi_GUISkin_current;
- #endif
- static MethodHook()
- {
- #if UNITY_EDITOR && !UNITY_2020_3_OR_NEWER
- s_fi_GUISkin_current = typeof(GUISkin).GetField("current", BindingFlags.Static | BindingFlags.NonPublic);
- #endif
- }
-
-
-
-
-
-
- public MethodHook(MethodBase targetMethod, MethodBase replacementMethod, MethodBase proxyMethod, string data = "")
- {
- this.targetMethod = targetMethod;
- this.replacementMethod = replacementMethod;
- this.proxyMethod = proxyMethod;
- this.tag = data;
- CheckMethod();
- }
- public void Install()
- {
- if (LDasm.IsiOS())
- return;
- if (isHooked)
- return;
- #if UNITY_EDITOR && !UNITY_2020_3_OR_NEWER
- if (s_fi_GUISkin_current.GetValue(null) != null)
- DoInstall();
- else
- EditorApplication.update += OnEditorUpdate;
- #else
- DoInstall();
- #endif
- isPlayModeHook = Application.isPlaying;
- }
- public void Uninstall()
- {
- if (!isHooked)
- return;
- _codePatcher.RemovePatch();
- isHooked = false;
- HookPool.RemoveHooker(targetMethod);
- }
- #region private
- private void DoInstall()
- {
- if (targetMethod == null || replacementMethod == null)
- throw new Exception("none of methods targetMethod or replacementMethod can be null");
- HookPool.AddHook(targetMethod, this);
- if (_codePatcher == null)
- {
- if (GetFunctionAddr())
- {
- #if ENABLE_HOOK_DEBUG
- UnityEngine.Debug.Log($"Original [{targetMethod.DeclaringType.Name}.{targetMethod.Name}]: {HookUtils.HexToString(_targetPtr.ToPointer(), 64, -16)}");
- UnityEngine.Debug.Log($"Original [{replacementMethod.DeclaringType.Name}.{replacementMethod.Name}]: {HookUtils.HexToString(_replacementPtr.ToPointer(), 64, -16)}");
- if(proxyMethod != null)
- UnityEngine.Debug.Log($"Original [{proxyMethod.DeclaringType.Name}.{proxyMethod.Name}]: {HookUtils.HexToString(_proxyPtr.ToPointer(), 64, -16)}");
- #endif
- CreateCodePatcher();
- _codePatcher.ApplyPatch();
- #if ENABLE_HOOK_DEBUG
- UnityEngine.Debug.Log($"New [{targetMethod.DeclaringType.Name}.{targetMethod.Name}]: {HookUtils.HexToString(_targetPtr.ToPointer(), 64, -16)}");
- UnityEngine.Debug.Log($"New [{replacementMethod.DeclaringType.Name}.{replacementMethod.Name}]: {HookUtils.HexToString(_replacementPtr.ToPointer(), 64, -16)}");
- if(proxyMethod != null)
- UnityEngine.Debug.Log($"New [{proxyMethod.DeclaringType.Name}.{proxyMethod.Name}]: {HookUtils.HexToString(_proxyPtr.ToPointer(), 64, -16)}");
- #endif
- }
- }
- isHooked = true;
- }
- private void CheckMethod()
- {
- if (targetMethod == null || replacementMethod == null)
- throw new Exception("MethodHook:targetMethod and replacementMethod and proxyMethod can not be null");
- string methodName = $"{targetMethod.DeclaringType.Name}.{targetMethod.Name}";
- if (targetMethod.IsAbstract)
- throw new Exception($"WRANING: you can not hook abstract method [{methodName}]");
- #if UNITY_EDITOR && !UNITY_2020_3_OR_NEWER
- int minMethodBodySize = 10;
- {
- if ((targetMethod.MethodImplementationFlags & MethodImplAttributes.InternalCall) != MethodImplAttributes.InternalCall)
- {
- int codeSize = targetMethod.GetMethodBody().GetILAsByteArray().Length;
- if (codeSize < minMethodBodySize)
- UnityEngine.Debug.LogWarning($"WRANING: you can not hook method [{methodName}], cause its method body is too short({codeSize}), will random crash on IL2CPP release mode");
- }
- }
- if(proxyMethod != null)
- {
- methodName = $"{proxyMethod.DeclaringType.Name}.{proxyMethod.Name}";
- int codeSize = proxyMethod.GetMethodBody().GetILAsByteArray().Length;
- if (codeSize < minMethodBodySize)
- UnityEngine.Debug.LogWarning($"WRANING: size of method body[{methodName}] is too short({codeSize}), will random crash on IL2CPP release mode, please fill some dummy code inside");
- if ((proxyMethod.MethodImplementationFlags & MethodImplAttributes.NoOptimization) != MethodImplAttributes.NoOptimization)
- throw new Exception($"WRANING: method [{methodName}] must has a Attribute `MethodImpl(MethodImplOptions.NoOptimization)` to prevent code call to this optimized by compiler(pass args by shared stack)");
- }
- #endif
- }
- private void CreateCodePatcher()
- {
- long addrOffset = Math.Abs(_targetPtr.ToInt64() - _proxyPtr.ToInt64());
-
- if(_proxyPtr != IntPtr.Zero)
- addrOffset = Math.Max(addrOffset, Math.Abs(_targetPtr.ToInt64() - _proxyPtr.ToInt64()));
- if (LDasm.IsARM())
- {
- if (IntPtr.Size == 8)
- _codePatcher = new CodePatcher_arm64_near(_targetPtr, _replacementPtr, _proxyPtr);
- else if (addrOffset < ((1 << 25) - 1))
- _codePatcher = new CodePatcher_arm32_near(_targetPtr, _replacementPtr, _proxyPtr);
- else if (addrOffset < ((1 << 27) - 1))
- _codePatcher = new CodePatcher_arm32_far(_targetPtr, _replacementPtr, _proxyPtr);
- else
- throw new Exception("address of target method and replacement method are too far, can not hook");
- }
- else
- {
- if (IntPtr.Size == 8)
- {
- if(addrOffset < 0x7fffffff)
- _codePatcher = new CodePatcher_x64_near(_targetPtr, _replacementPtr, _proxyPtr);
- else
- _codePatcher = new CodePatcher_x64_far(_targetPtr, _replacementPtr, _proxyPtr);
- }
- else
- _codePatcher = new CodePatcher_x86(_targetPtr, _replacementPtr, _proxyPtr);
- }
- }
-
-
-
- private bool GetFunctionAddr()
- {
- _targetPtr = GetFunctionAddr(targetMethod);
- _replacementPtr = GetFunctionAddr(replacementMethod);
- _proxyPtr = GetFunctionAddr(proxyMethod);
- if (_targetPtr == IntPtr.Zero || _replacementPtr == IntPtr.Zero)
- return false;
- if (proxyMethod != null && _proxyPtr == null)
- return false;
- if(_replacementPtr == _targetPtr)
- {
- throw new Exception($"the addresses of target method {targetMethod.Name} and replacement method {replacementMethod.Name} can not be same");
- }
- if (LDasm.IsThumb(_targetPtr) || LDasm.IsThumb(_replacementPtr))
- {
- throw new Exception("does not support thumb arch");
- }
- return true;
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct __ForCopy
- {
- public long __dummy;
- public MethodBase method;
- }
-
-
-
-
-
- private IntPtr GetFunctionAddr(MethodBase method)
- {
- if (method == null)
- return IntPtr.Zero;
- if (!LDasm.IsIL2CPP())
- return method.MethodHandle.GetFunctionPointer();
- else
- {
-
- __ForCopy __forCopy = new __ForCopy() { method = method };
- long* ptr = &__forCopy.__dummy;
- ptr++;
- IntPtr methodAddr = IntPtr.Zero;
- if (sizeof(IntPtr) == 8)
- {
- long methodDataAddr = *(long*)ptr;
- byte* ptrData = (byte*)methodDataAddr + sizeof(IntPtr) * 2;
- long methodPtr = 0;
- methodPtr = *(long*)ptrData;
- methodAddr = new IntPtr(*(long*)methodPtr);
- }
- else
- {
- int methodDataAddr = *(int*)ptr;
- byte* ptrData = (byte*)methodDataAddr + sizeof(IntPtr) * 2;
- int methodPtr = 0;
- methodPtr = *(int*)ptrData;
- methodAddr = new IntPtr(*(int*)methodPtr);
- }
- return methodAddr;
- }
- }
- #if UNITY_EDITOR && !UNITY_2020_3_OR_NEWER
- private void OnEditorUpdate()
- {
- if (s_fi_GUISkin_current.GetValue(null) != null)
- {
- try
- {
- DoInstall();
- }
- finally
- {
- EditorApplication.update -= OnEditorUpdate;
- }
- }
- }
- #endif
- #endregion
- }
- }
|