diff --git a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes b/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes deleted file mode 100644 index 0eb3f38..0000000 Binary files a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes and /dev/null differ diff --git a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes b/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes deleted file mode 100644 index 926857d..0000000 Binary files a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes and /dev/null differ diff --git a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes.meta b/Editor/3rds/UnityHook.meta similarity index 57% rename from Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes.meta rename to Editor/3rds/UnityHook.meta index 625dd7a..4fab614 100644 --- a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes.meta +++ b/Editor/3rds/UnityHook.meta @@ -1,6 +1,7 @@ fileFormatVersion: 2 -guid: a96a2c46326d2a84984c2820bd4bfc91 -TextScriptImporter: +guid: 13fe0cab0b357464d889de45c8d98850 +folderAsset: yes +DefaultImporter: externalObjects: {} userData: assetBundleName: diff --git a/Editor/3rds/UnityHook/CodePatcher.cs b/Editor/3rds/UnityHook/CodePatcher.cs new file mode 100644 index 0000000..6ec2fa7 --- /dev/null +++ b/Editor/3rds/UnityHook/CodePatcher.cs @@ -0,0 +1,320 @@ +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; + } + } + + /// + /// x64下2G 内的跳转 + /// + 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) { } + } + + /// + /// x64下距离超过2G的跳转 + /// + 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, + 0x50, // push rax + 0xC3 // ret + }; + + //protected static readonly byte[] s_jmpCode2 = new byte[] // 14 bytes + //{ + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + // 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; + } + } + + /// + /// arm64 下 ±128MB 范围内的跳转 + /// + 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; + } + } + + /// + /// arm64 远距离跳转 + /// + 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(); + } + } +} \ No newline at end of file diff --git a/Editor/3rds/UnityHook/CodePatcher.cs.meta b/Editor/3rds/UnityHook/CodePatcher.cs.meta new file mode 100644 index 0000000..f745350 --- /dev/null +++ b/Editor/3rds/UnityHook/CodePatcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97cc0d26f72fc4148b8370b2252d1585 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/HookPool.cs b/Editor/3rds/UnityHook/HookPool.cs new file mode 100644 index 0000000..34a0599 --- /dev/null +++ b/Editor/3rds/UnityHook/HookPool.cs @@ -0,0 +1,74 @@ +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using System.Linq; +using System.IO; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace MonoHook +{ + /// + /// Hook 池,防止重复 Hook + /// + public static class HookPool + { + private static Dictionary _hooks = new Dictionary(); + + public static void AddHook(MethodBase method, MethodHook hook) + { + MethodHook preHook; + if (_hooks.TryGetValue(method, out preHook)) + { + preHook.Uninstall(); + _hooks[method] = hook; + } + else + _hooks.Add(method, hook); + } + + public static MethodHook GetHook(MethodBase method) + { + if (method == null) return null; + + MethodHook hook; + if (_hooks.TryGetValue(method, out hook)) + return hook; + return null; + } + + public static void RemoveHooker(MethodBase method) + { + if (method == null) return; + + _hooks.Remove(method); + } + + public static void UninstallAll() + { + var list = _hooks.Values.ToList(); + foreach (var hook in list) + hook.Uninstall(); + + _hooks.Clear(); + } + + public static void UninstallByTag(string tag) + { + var list = _hooks.Values.ToList(); + foreach (var hook in list) + { + if(hook.tag == tag) + hook.Uninstall(); + } + } + + public static List GetAllHooks() + { + return _hooks.Values.ToList(); + } + } + +} diff --git a/Editor/3rds/UnityHook/HookPool.cs.meta b/Editor/3rds/UnityHook/HookPool.cs.meta new file mode 100644 index 0000000..7503859 --- /dev/null +++ b/Editor/3rds/UnityHook/HookPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b7421e47f0ae1e4ebb72bf18d1d7d48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/HookUtils.cs b/Editor/3rds/UnityHook/HookUtils.cs new file mode 100644 index 0000000..7acd8c9 --- /dev/null +++ b/Editor/3rds/UnityHook/HookUtils.cs @@ -0,0 +1,272 @@ +#if !(UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX) +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using UnityEngine; + +namespace MonoHook +{ + public static unsafe class HookUtils + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void DelegateFlushICache(void* code, int size); // delegate * unmanaged[Cdecl] native_flush_cache_fun_ptr; // unsupported at C# 8.0 + + static DelegateFlushICache flush_icache; + private static readonly long _Pagesize; + + static HookUtils() + { + PropertyInfo p_SystemPageSize = typeof(Environment).GetProperty("SystemPageSize"); + if (p_SystemPageSize == null) + throw new NotSupportedException("Unsupported runtime"); + _Pagesize = (int)p_SystemPageSize.GetValue(null, new object[0]); + SetupFlushICacheFunc(); + } + + public static void MemCpy(void* pDst, void* pSrc, int len) + { + byte* pDst_ = (byte*)pDst; + byte* pSrc_ = (byte*)pSrc; + + for (int i = 0; i < len; i++) + *pDst_++ = *pSrc_++; + } + + public static void MemCpy_Jit(void* pDst, byte[] src) + { + fixed (void* p = &src[0]) + { + MemCpy(pDst, p, src.Length); + } + } + + /// + /// set flags of address to `read write execute` + /// + public static void SetAddrFlagsToRWX(IntPtr ptr, int size) + { + if (ptr == IntPtr.Zero) + return; + +#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN + uint oldProtect; + bool ret = VirtualProtect(ptr, (uint)size, Protection.PAGE_EXECUTE_READWRITE, out oldProtect); + UnityEngine.Debug.Assert(ret); +#else + SetMemPerms(ptr,(ulong)size,MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); +#endif + } + + public static void FlushICache(void* code, int size) + { + if (code == null) + return; + + flush_icache?.Invoke(code, size); + +#if ENABLE_HOOK_DEBUG + Debug.Log($"flush icache at 0x{(IntPtr.Size == 4 ? (uint)code : (ulong)code):x}, size:{size}"); +#endif + } + + public static KeyValuePair GetPageAlignedAddr(long code, int size) + { + long pagesize = _Pagesize; + long startPage = (code) & ~(pagesize - 1); + long endPage = (code + size + pagesize - 1) & ~(pagesize - 1); + return new KeyValuePair(startPage, endPage); + } + + + const int PRINT_SPLIT = 4; + const int PRINT_COL_SIZE = PRINT_SPLIT * 4; + public static string HexToString(void* ptr, int size, int offset = 0) + { + Func formatAddr = (IntPtr addr__) => IntPtr.Size == 4 ? $"0x{(uint)addr__:x}" : $"0x{(ulong)addr__:x}"; + + byte* addr = (byte*)ptr; + + StringBuilder sb = new StringBuilder(1024); + sb.AppendLine($"addr:{formatAddr(new IntPtr(addr))}"); + + addr += offset; + size += Math.Abs(offset); + + int count = 0; + while (true) + { + sb.Append($"\r\n{formatAddr(new IntPtr(addr + count))}: "); + for (int i = 1; i < PRINT_COL_SIZE + 1; i++) + { + if (count >= size) + goto END; + + sb.Append($"{*(addr + count):x2}"); + if (i % PRINT_SPLIT == 0) + sb.Append(" "); + + count++; + } + } + END:; + return sb.ToString(); + } + + static void SetupFlushICacheFunc() + { + string processorType = SystemInfo.processorType; + if (processorType.Contains("Intel") || processorType.Contains("AMD")) + return; + + if (IntPtr.Size == 4) + { + // never release, so save GCHandle is unnecessary + s_ptr_flush_icache_arm32 = GCHandle.Alloc(s_flush_icache_arm32, GCHandleType.Pinned).AddrOfPinnedObject().ToPointer(); + SetAddrFlagsToRWX(new IntPtr(s_ptr_flush_icache_arm32), s_flush_icache_arm32.Length); + flush_icache = Marshal.GetDelegateForFunctionPointer(new IntPtr(s_ptr_flush_icache_arm32)); + } + else + { + s_ptr_flush_icache_arm64 = GCHandle.Alloc(s_flush_icache_arm64, GCHandleType.Pinned).AddrOfPinnedObject().ToPointer(); + SetAddrFlagsToRWX(new IntPtr(s_ptr_flush_icache_arm64), s_flush_icache_arm64.Length); + flush_icache = Marshal.GetDelegateForFunctionPointer(new IntPtr(s_ptr_flush_icache_arm64)); + } + +#if ENABLE_HOOK_DEBUG + Debug.Log($"flush_icache delegate is {((flush_icache != null) ? "not " : "")}null"); +#endif + } + + + static void* s_ptr_flush_icache_arm32, s_ptr_flush_icache_arm64; + private static byte[] s_flush_icache_arm32 = new byte[] + { + // void cdecl mono_arch_flush_icache (guint8 *code, gint size) + 0x00, 0x48, 0x2D, 0xE9, // PUSH {R11,LR} + 0x0D, 0xB0, 0xA0, 0xE1, // MOV R11, SP + 0x08, 0xD0, 0x4D, 0xE2, // SUB SP, SP, #8 + 0x04, 0x00, 0x8D, 0xE5, // STR R0, [SP,#8+var_4] + 0x00, 0x10, 0x8D, 0xE5, // STR R1, [SP,#8+var_8] + 0x04, 0x00, 0x9D, 0xE5, // LDR R0, [SP,#8+var_4] + 0x04, 0x10, 0x9D, 0xE5, // LDR R1, [SP,#8+var_4] + 0x00, 0x20, 0x9D, 0xE5, // LDR R2, [SP,#8+var_8] + 0x02, 0x10, 0x81, 0xE0, // ADD R1, R1, R2 + 0x01, 0x00, 0x00, 0xEB, // BL __clear_cache + 0x0B, 0xD0, 0xA0, 0xE1, // MOV SP, R11 + 0x00, 0x88, 0xBD, 0xE8, // POP {R11,PC} + + // __clear_cache ; CODE XREF: j___clear_cache+8↑j + 0x80, 0x00, 0x2D, 0xE9, // PUSH { R7 } + 0x02, 0x70, 0x00, 0xE3, 0x0F, 0x70, 0x40, 0xE3, // MOV R7, #0xF0002 + 0x00, 0x20, 0xA0, 0xE3, // MOV R2, #0 + 0x00, 0x00, 0x00, 0xEF, // SVC 0 + 0x80, 0x00, 0xBD, 0xE8, // POP {R7} + 0x1E, 0xFF, 0x2F, 0xE1, // BX LR + }; + + private static byte[] s_flush_icache_arm64 = new byte[] // X0: code, W1: size + { + // void cdecl mono_arch_flush_icache (guint8 *code, gint size) + 0xFF, 0xC3, 0x00, 0xD1, // SUB SP, SP, #0x30 + 0xE8, 0x03, 0x7E, 0xB2, // MOV X8, #4 + 0xE0, 0x17, 0x00, 0xF9, // STR X0, [SP,#0x30+var_8] + 0xE1, 0x27, 0x00, 0xB9, // STR W1, [SP,#0x30+var_C] + 0xE0, 0x17, 0x40, 0xF9, // LDR X0, [SP,#0x30+var_8] + 0xE9, 0x27, 0x80, 0xB9, // LDRSW X9, [SP,#0x30+var_C] + 0x09, 0x00, 0x09, 0x8B, // ADD X9, X0, X9 + 0xE9, 0x0F, 0x00, 0xF9, // STR X9, [SP,#0x30+var_18] + 0xE8, 0x07, 0x00, 0xF9, // STR X8, [SP,#0x30+var_28] + 0xE8, 0x03, 0x00, 0xF9, // STR X8, [SP,#0x30+var_30] + 0xE8, 0x17, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_8] + 0x08, 0xF5, 0x7E, 0x92, // AND X8, X8, #0xFFFFFFFFFFFFFFFC + 0xE8, 0x0B, 0x00, 0xF9, // STR X8, [SP,#0x30+var_20] + + // loc_590 ; CODE XREF: mono_arch_flush_icache(uchar*, int)+58↓j + 0xE8, 0x0B, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_20] + 0xE9, 0x0F, 0x40, 0xF9, // LDR X9, [SP,#0x30+var_18] + 0x1F, 0x01, 0x09, 0xEB, // CMP X8, X9 + 0xE2, 0x00, 0x00, 0x54, // B.CS loc_5B8 + 0xE8, 0x0B, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_20] + 0x28, 0x7E, 0x0B, 0xD5, // SYS #3, c7, c14, #1, X8 + 0xE8, 0x0B, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_20] + 0x08, 0x11, 0x00, 0x91, // ADD X8, X8, #4 + 0xE8, 0x0B, 0x00, 0xF9, // STR X8, [SP,#0x30+var_20] + 0xF7, 0xFF, 0xFF, 0x17, // B loc_590 + // ; --------------------------------------------------------------------------- + + // loc_5B8 ; CODE XREF: mono_arch_flush_icache(uchar *, int)+40↑j + 0x9F, 0x3B, 0x03, 0xD5, // DSB ISH + 0xE8, 0x17, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_8] + 0x08, 0xF5, 0x7E, 0x92, // AND X8, X8, #0xFFFFFFFFFFFFFFFC + 0xE8, 0x0B, 0x00, 0xF9, // STR X8, [SP,#0x30+var_20] + + // loc_5C8 ; CODE XREF: mono_arch_flush_icache(uchar *, int)+90↓j + 0xE8, 0x0B, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_20] + 0xE9, 0x0F, 0x40, 0xF9, // LDR X9, [SP,#0x30+var_18] + 0x1F, 0x01, 0x09, 0xEB, // CMP X8, X9 + 0xE2, 0x00, 0x00, 0x54, // B.CS loc_5F0 + 0xE8, 0x0B, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_20] + 0x28, 0x75, 0x0B, 0xD5, // SYS #3, c7, c5, #1, X8 + 0xE8, 0x0B, 0x40, 0xF9, // LDR X8, [SP,#0x30+var_20] + 0x08, 0x11, 0x00, 0x91, // ADD X8, X8, #4 + 0xE8, 0x0B, 0x00, 0xF9, // STR X8, [SP,#0x30+var_20] + 0xF7, 0xFF, 0xFF, 0x17, // B loc_5C8 + // ; --------------------------------------------------------------------------- + + // loc_5F0 ; CODE XREF: mono_arch_flush_icache(uchar *, int)+78↑j + 0x9F, 0x3B, 0x03, 0xD5, // DSB ISH + 0xDF, 0x3F, 0x03, 0xD5, // ISB + 0xFF, 0xC3, 0x00, 0x91, // ADD SP, SP, #0x30 ; '0' + 0xC0, 0x03, 0x5F, 0xD6, // RET + }; + + +#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN + [Flags] + public enum Protection + { + PAGE_NOACCESS = 0x01, + PAGE_READONLY = 0x02, + PAGE_READWRITE = 0x04, + PAGE_WRITECOPY = 0x08, + PAGE_EXECUTE = 0x10, + PAGE_EXECUTE_READ = 0x20, + PAGE_EXECUTE_READWRITE = 0x40, + PAGE_EXECUTE_WRITECOPY = 0x80, + PAGE_GUARD = 0x100, + PAGE_NOCACHE = 0x200, + PAGE_WRITECOMBINE = 0x400 + } + + [DllImport("kernel32")] + public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, Protection flNewProtect, out uint lpflOldProtect); + +#else + [Flags] + public enum MmapProts : int { + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + PROT_NONE = 0x0, + PROT_GROWSDOWN = 0x01000000, + PROT_GROWSUP = 0x02000000, + } + + [DllImport("libc", SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern int mprotect(IntPtr start, IntPtr len, MmapProts prot); + + public static unsafe void SetMemPerms(IntPtr start, ulong len, MmapProts prot) { + var requiredAddr = GetPageAlignedAddr(start.ToInt64(), (int)len); + long startPage = requiredAddr.Key; + long endPage = requiredAddr.Value; + + if (mprotect((IntPtr) startPage, (IntPtr) (endPage - startPage), prot) != 0) + throw new Exception($"mprotect with prot:{prot} fail!"); + } +#endif + } +} + +#endif \ No newline at end of file diff --git a/Editor/3rds/UnityHook/HookUtils.cs.meta b/Editor/3rds/UnityHook/HookUtils.cs.meta new file mode 100644 index 0000000..8ec4cb3 --- /dev/null +++ b/Editor/3rds/UnityHook/HookUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e84139b42a6164e4c93ce4df1be6dcfb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/HookUtils_OSX.cs b/Editor/3rds/UnityHook/HookUtils_OSX.cs new file mode 100644 index 0000000..8e4a126 --- /dev/null +++ b/Editor/3rds/UnityHook/HookUtils_OSX.cs @@ -0,0 +1,102 @@ +#if (UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX) +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using UnityEngine; + +namespace MonoHook +{ + public static unsafe class HookUtils + { + static bool jit_write_protect_supported; + private static readonly long _Pagesize; + + + static HookUtils() + { + jit_write_protect_supported = pthread_jit_write_protect_supported_np() != 0; + + PropertyInfo p_SystemPageSize = typeof(Environment).GetProperty("SystemPageSize"); + if (p_SystemPageSize == null) + throw new NotSupportedException("Unsupported runtime"); + _Pagesize = (int)p_SystemPageSize.GetValue(null, new object[0]); + } + + public static void MemCpy(void* pDst, void* pSrc, int len) + { + byte* pDst_ = (byte*)pDst; + byte* pSrc_ = (byte*)pSrc; + + for (int i = 0; i < len; i++) + *pDst_++ = *pSrc_++; + } + + public static void MemCpy_Jit(void* pDst, byte[] src) + { + fixed(void * p = &src[0]) + { + memcpy_jit(new IntPtr(pDst), new IntPtr(p), src.Length); + } + } + + /// + /// set flags of address to `read write execute` + /// + public static void SetAddrFlagsToRWX(IntPtr ptr, int size) { } + + public static void FlushICache(void* code, int size) { } + + public static KeyValuePair GetPageAlignedAddr(long code, int size) + { + long pagesize = _Pagesize; + long startPage = (code) & ~(pagesize - 1); + long endPage = (code + size + pagesize - 1) & ~(pagesize - 1); + return new KeyValuePair(startPage, endPage); + } + + + const int PRINT_SPLIT = 4; + const int PRINT_COL_SIZE = PRINT_SPLIT * 4; + public static string HexToString(void* ptr, int size, int offset = 0) + { + Func formatAddr = (IntPtr addr__) => IntPtr.Size == 4 ? $"0x{(uint)addr__:x}" : $"0x{(ulong)addr__:x}"; + + byte* addr = (byte*)ptr; + + StringBuilder sb = new StringBuilder(1024); + sb.AppendLine($"addr:{formatAddr(new IntPtr(addr))}"); + + addr += offset; + size += Math.Abs(offset); + + int count = 0; + while (true) + { + sb.Append($"\r\n{formatAddr(new IntPtr(addr + count))}: "); + for (int i = 1; i < PRINT_COL_SIZE + 1; i++) + { + if (count >= size) + goto END; + + sb.Append($"{*(addr + count):x2}"); + if (i % PRINT_SPLIT == 0) + sb.Append(" "); + + count++; + } + } + END:; + return sb.ToString(); + } + + [DllImport("pthread", SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern int pthread_jit_write_protect_supported_np(); + + [DllImport("libMonoHookUtils_OSX", SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr memcpy_jit(IntPtr dst, IntPtr src, int len); + } +} + +#endif \ No newline at end of file diff --git a/Editor/3rds/UnityHook/HookUtils_OSX.cs.meta b/Editor/3rds/UnityHook/HookUtils_OSX.cs.meta new file mode 100644 index 0000000..6ecee49 --- /dev/null +++ b/Editor/3rds/UnityHook/HookUtils_OSX.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: efda6e010e5c6594081c4a62861d469f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes.meta b/Editor/3rds/UnityHook/HybridCLRHooks.meta similarity index 57% rename from Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes.meta rename to Editor/3rds/UnityHook/HybridCLRHooks.meta index 939b185..8169f8d 100644 --- a/Data~/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes.meta +++ b/Editor/3rds/UnityHook/HybridCLRHooks.meta @@ -1,6 +1,7 @@ fileFormatVersion: 2 -guid: 33c064b00b452284f93cd6e89de8a06d -TextScriptImporter: +guid: d796fc01daee1964586621890988a5ae +folderAsset: yes +DefaultImporter: externalObjects: {} userData: assetBundleName: diff --git a/Editor/3rds/UnityHook/HybridCLRHooks/CopyStrippedAOTAssembliesHook.cs b/Editor/3rds/UnityHook/HybridCLRHooks/CopyStrippedAOTAssembliesHook.cs new file mode 100644 index 0000000..d78848f --- /dev/null +++ b/Editor/3rds/UnityHook/HybridCLRHooks/CopyStrippedAOTAssembliesHook.cs @@ -0,0 +1,67 @@ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using System.Runtime.CompilerServices; +using MonoHook; +using HybridCLR.Editor.BuildProcessors; +using System.IO; + +namespace HybridCLR.MonoHook +{ +#if UNITY_2021_1_OR_NEWER && UNITY_IOS + [InitializeOnLoad] + public class CopyStrippedAOTAssembliesHook + { + private static MethodHook _hook; + + static CopyStrippedAOTAssembliesHook() + { + if (_hook == null) + { + Type type = typeof(UnityEditor.EditorApplication).Assembly.GetType("UnityEditorInternal.AssemblyStripper"); + MethodInfo miTarget = type.GetMethod("StripAssembliesTo", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + + MethodInfo miReplacement = new StripAssembliesDel(OverrideStripAssembliesTo).Method; + MethodInfo miProxy = new StripAssembliesDel(StripAssembliesToProxy).Method; + + _hook = new MethodHook(miTarget, miReplacement, miProxy); + _hook.Install(); + } + } + + private delegate bool StripAssembliesDel(string outputFolder, out string output, out string error, IEnumerable linkXmlFiles, object runInformation); + + private static bool OverrideStripAssembliesTo(string outputFolder, out string output, out string error, IEnumerable linkXmlFiles, object runInformation) + { + bool result = StripAssembliesToProxy(outputFolder, out output, out error, linkXmlFiles, runInformation); + if (!result) + { + return false; + } + UnityEngine.Debug.Log($"== StripAssembliesTo outputDir:{outputFolder}"); + string outputStrippedDir = HybridCLR.Editor.SettingsUtil.GetAssembliesPostIl2CppStripDir(EditorUserBuildSettings.activeBuildTarget); + Directory.CreateDirectory(outputStrippedDir); + foreach (var aotDll in Directory.GetFiles(outputFolder, "*.dll")) + { + string dstFile = $"{outputStrippedDir}/{Path.GetFileName(aotDll)}"; + Debug.Log($"[RunAssemblyStripper] copy aot dll {aotDll} -> {dstFile}"); + File.Copy(aotDll, dstFile, true); + } + return result; + } + + [MethodImpl(MethodImplOptions.NoOptimization)] + private static bool StripAssembliesToProxy(string outputFolder, out string output, out string error, IEnumerable linkXmlFiles, object runInformation) + { + Debug.LogError("== StripAssembliesToProxy =="); + output = ""; + error = ""; + return true; + } + } +#endif +} diff --git a/Editor/3rds/UnityHook/HybridCLRHooks/CopyStrippedAOTAssembliesHook.cs.meta b/Editor/3rds/UnityHook/HybridCLRHooks/CopyStrippedAOTAssembliesHook.cs.meta new file mode 100644 index 0000000..18542c3 --- /dev/null +++ b/Editor/3rds/UnityHook/HybridCLRHooks/CopyStrippedAOTAssembliesHook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf42c4f20b8a1b94baa04a1a5c6b8beb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/LDasm.cs b/Editor/3rds/UnityHook/LDasm.cs new file mode 100644 index 0000000..d87ab91 --- /dev/null +++ b/Editor/3rds/UnityHook/LDasm.cs @@ -0,0 +1,903 @@ +using System; +using System.Runtime.InteropServices; + +namespace DotNetDetour +{ + /// + /// 用于计算汇编指令长度,使用的是BlackBone的LDasm.c中的算法,我把他翻译成C#了 + /// + public unsafe class LDasm + { + const int F_INVALID = 0x01; + const int F_PREFIX = 0x02; + const int F_REX = 0x04; + const int F_MODRM = 0x08; + const int F_SIB = 0x10; + const int F_DISP = 0x20; + const int F_IMM = 0x40; + const int F_RELATIVE = 0x80; + + const int OP_NONE = 0x00; + const int OP_INVALID = 0x80; + + const int OP_DATA_I8 = 0x01; + const int OP_DATA_I16 = 0x02; + const int OP_DATA_I16_I32 = 0x04; + const int OP_DATA_I16_I32_I64 = 0x08; + const int OP_EXTENDED = 0x10; + const int OP_RELATIVE = 0x20; + const int OP_MODRM = 0x40; + const int OP_PREFIX = 0x80; + + struct ldasm_data + { + public byte flags; + public byte rex; + public byte modrm; + public byte sib; + public byte opcd_offset; + public byte opcd_size; + public byte disp_offset; + public byte disp_size; + public byte imm_offset; + public byte imm_size; + } + + static byte[] flags_table = + { + /* 00 */ OP_MODRM, + /* 01 */ OP_MODRM, + /* 02 */ OP_MODRM, + /* 03 */ OP_MODRM, + /* 04 */ OP_DATA_I8, + /* 05 */ OP_DATA_I16_I32, + /* 06 */ OP_NONE, + /* 07 */ OP_NONE, + /* 08 */ OP_MODRM, + /* 09 */ OP_MODRM, + /* 0A */ OP_MODRM, + /* 0B */ OP_MODRM, + /* 0C */ OP_DATA_I8, + /* 0D */ OP_DATA_I16_I32, + /* 0E */ OP_NONE, + /* 0F */ OP_NONE, + + /* 10 */ OP_MODRM, + /* 11 */ OP_MODRM, + /* 12 */ OP_MODRM, + /* 13 */ OP_MODRM, + /* 14 */ OP_DATA_I8, + /* 15 */ OP_DATA_I16_I32, + /* 16 */ OP_NONE, + /* 17 */ OP_NONE, + /* 18 */ OP_MODRM, + /* 19 */ OP_MODRM, + /* 1A */ OP_MODRM, + /* 1B */ OP_MODRM, + /* 1C */ OP_DATA_I8, + /* 1D */ OP_DATA_I16_I32, + /* 1E */ OP_NONE, + /* 1F */ OP_NONE, + + /* 20 */ OP_MODRM, + /* 21 */ OP_MODRM, + /* 22 */ OP_MODRM, + /* 23 */ OP_MODRM, + /* 24 */ OP_DATA_I8, + /* 25 */ OP_DATA_I16_I32, + /* 26 */ OP_PREFIX, + /* 27 */ OP_NONE, + /* 28 */ OP_MODRM, + /* 29 */ OP_MODRM, + /* 2A */ OP_MODRM, + /* 2B */ OP_MODRM, + /* 2C */ OP_DATA_I8, + /* 2D */ OP_DATA_I16_I32, + /* 2E */ OP_PREFIX, + /* 2F */ OP_NONE, + + /* 30 */ OP_MODRM, + /* 31 */ OP_MODRM, + /* 32 */ OP_MODRM, + /* 33 */ OP_MODRM, + /* 34 */ OP_DATA_I8, + /* 35 */ OP_DATA_I16_I32, + /* 36 */ OP_PREFIX, + /* 37 */ OP_NONE, + /* 38 */ OP_MODRM, + /* 39 */ OP_MODRM, + /* 3A */ OP_MODRM, + /* 3B */ OP_MODRM, + /* 3C */ OP_DATA_I8, + /* 3D */ OP_DATA_I16_I32, + /* 3E */ OP_PREFIX, + /* 3F */ OP_NONE, + + /* 40 */ OP_NONE, + /* 41 */ OP_NONE, + /* 42 */ OP_NONE, + /* 43 */ OP_NONE, + /* 44 */ OP_NONE, + /* 45 */ OP_NONE, + /* 46 */ OP_NONE, + /* 47 */ OP_NONE, + /* 48 */ OP_NONE, + /* 49 */ OP_NONE, + /* 4A */ OP_NONE, + /* 4B */ OP_NONE, + /* 4C */ OP_NONE, + /* 4D */ OP_NONE, + /* 4E */ OP_NONE, + /* 4F */ OP_NONE, + + /* 50 */ OP_NONE, + /* 51 */ OP_NONE, + /* 52 */ OP_NONE, + /* 53 */ OP_NONE, + /* 54 */ OP_NONE, + /* 55 */ OP_NONE, + /* 56 */ OP_NONE, + /* 57 */ OP_NONE, + /* 58 */ OP_NONE, + /* 59 */ OP_NONE, + /* 5A */ OP_NONE, + /* 5B */ OP_NONE, + /* 5C */ OP_NONE, + /* 5D */ OP_NONE, + /* 5E */ OP_NONE, + /* 5F */ OP_NONE, + /* 60 */ OP_NONE, + + /* 61 */ OP_NONE, + /* 62 */ OP_MODRM, + /* 63 */ OP_MODRM, + /* 64 */ OP_PREFIX, + /* 65 */ OP_PREFIX, + /* 66 */ OP_PREFIX, + /* 67 */ OP_PREFIX, + /* 68 */ OP_DATA_I16_I32, + /* 69 */ OP_MODRM | OP_DATA_I16_I32, + /* 6A */ OP_DATA_I8, + /* 6B */ OP_MODRM | OP_DATA_I8, + /* 6C */ OP_NONE, + /* 6D */ OP_NONE, + /* 6E */ OP_NONE, + /* 6F */ OP_NONE, + + /* 70 */ OP_RELATIVE | OP_DATA_I8, + /* 71 */ OP_RELATIVE | OP_DATA_I8, + /* 72 */ OP_RELATIVE | OP_DATA_I8, + /* 73 */ OP_RELATIVE | OP_DATA_I8, + /* 74 */ OP_RELATIVE | OP_DATA_I8, + /* 75 */ OP_RELATIVE | OP_DATA_I8, + /* 76 */ OP_RELATIVE | OP_DATA_I8, + /* 77 */ OP_RELATIVE | OP_DATA_I8, + /* 78 */ OP_RELATIVE | OP_DATA_I8, + /* 79 */ OP_RELATIVE | OP_DATA_I8, + /* 7A */ OP_RELATIVE | OP_DATA_I8, + /* 7B */ OP_RELATIVE | OP_DATA_I8, + /* 7C */ OP_RELATIVE | OP_DATA_I8, + /* 7D */ OP_RELATIVE | OP_DATA_I8, + /* 7E */ OP_RELATIVE | OP_DATA_I8, + /* 7F */ OP_RELATIVE | OP_DATA_I8, + + /* 80 */ OP_MODRM | OP_DATA_I8, + /* 81 */ OP_MODRM | OP_DATA_I16_I32, + /* 82 */ OP_MODRM | OP_DATA_I8, + /* 83 */ OP_MODRM | OP_DATA_I8, + /* 84 */ OP_MODRM, + /* 85 */ OP_MODRM, + /* 86 */ OP_MODRM, + /* 87 */ OP_MODRM, + /* 88 */ OP_MODRM, + /* 89 */ OP_MODRM, + /* 8A */ OP_MODRM, + /* 8B */ OP_MODRM, + /* 8C */ OP_MODRM, + /* 8D */ OP_MODRM, + /* 8E */ OP_MODRM, + /* 8F */ OP_MODRM, + + /* 90 */ OP_NONE, + /* 91 */ OP_NONE, + /* 92 */ OP_NONE, + /* 93 */ OP_NONE, + /* 94 */ OP_NONE, + /* 95 */ OP_NONE, + /* 96 */ OP_NONE, + /* 97 */ OP_NONE, + /* 98 */ OP_NONE, + /* 99 */ OP_NONE, + /* 9A */ OP_DATA_I16 | OP_DATA_I16_I32, + /* 9B */ OP_NONE, + /* 9C */ OP_NONE, + /* 9D */ OP_NONE, + /* 9E */ OP_NONE, + /* 9F */ OP_NONE, + + /* A0 */ OP_DATA_I8, + /* A1 */ OP_DATA_I16_I32_I64, + /* A2 */ OP_DATA_I8, + /* A3 */ OP_DATA_I16_I32_I64, + /* A4 */ OP_NONE, + /* A5 */ OP_NONE, + /* A6 */ OP_NONE, + /* A7 */ OP_NONE, + /* A8 */ OP_DATA_I8, + /* A9 */ OP_DATA_I16_I32, + /* AA */ OP_NONE, + /* AB */ OP_NONE, + /* AC */ OP_NONE, + /* AD */ OP_NONE, + /* AE */ OP_NONE, + /* AF */ OP_NONE, + + /* B0 */ OP_DATA_I8, + /* B1 */ OP_DATA_I8, + /* B2 */ OP_DATA_I8, + /* B3 */ OP_DATA_I8, + /* B4 */ OP_DATA_I8, + /* B5 */ OP_DATA_I8, + /* B6 */ OP_DATA_I8, + /* B7 */ OP_DATA_I8, + /* B8 */ OP_DATA_I16_I32_I64, + /* B9 */ OP_DATA_I16_I32_I64, + /* BA */ OP_DATA_I16_I32_I64, + /* BB */ OP_DATA_I16_I32_I64, + /* BC */ OP_DATA_I16_I32_I64, + /* BD */ OP_DATA_I16_I32_I64, + /* BE */ OP_DATA_I16_I32_I64, + /* BF */ OP_DATA_I16_I32_I64, + + /* C0 */ OP_MODRM | OP_DATA_I8, + /* C1 */ OP_MODRM | OP_DATA_I8, + /* C2 */ OP_DATA_I16, + /* C3 */ OP_NONE, + /* C4 */ OP_MODRM, + /* C5 */ OP_MODRM, + /* C6 */ OP_MODRM | OP_DATA_I8, + /* C7 */ OP_MODRM | OP_DATA_I16_I32, + /* C8 */ OP_DATA_I8 | OP_DATA_I16, + /* C9 */ OP_NONE, + /* CA */ OP_DATA_I16, + /* CB */ OP_NONE, + /* CC */ OP_NONE, + /* CD */ OP_DATA_I8, + /* CE */ OP_NONE, + /* CF */ OP_NONE, + + /* D0 */ OP_MODRM, + /* D1 */ OP_MODRM, + /* D2 */ OP_MODRM, + /* D3 */ OP_MODRM, + /* D4 */ OP_DATA_I8, + /* D5 */ OP_DATA_I8, + /* D6 */ OP_NONE, + /* D7 */ OP_NONE, + /* D8 */ OP_MODRM, + /* D9 */ OP_MODRM, + /* DA */ OP_MODRM, + /* DB */ OP_MODRM, + /* DC */ OP_MODRM, + /* DD */ OP_MODRM, + /* DE */ OP_MODRM, + /* DF */ OP_MODRM, + + /* E0 */ OP_RELATIVE | OP_DATA_I8, + /* E1 */ OP_RELATIVE | OP_DATA_I8, + /* E2 */ OP_RELATIVE | OP_DATA_I8, + /* E3 */ OP_RELATIVE | OP_DATA_I8, + /* E4 */ OP_DATA_I8, + /* E5 */ OP_DATA_I8, + /* E6 */ OP_DATA_I8, + /* E7 */ OP_DATA_I8, + /* E8 */ OP_RELATIVE | OP_DATA_I16_I32, + /* E9 */ OP_RELATIVE | OP_DATA_I16_I32, + /* EA */ OP_DATA_I16 | OP_DATA_I16_I32, + /* EB */ OP_RELATIVE | OP_DATA_I8, + /* EC */ OP_NONE, + /* ED */ OP_NONE, + /* EE */ OP_NONE, + /* EF */ OP_NONE, + + /* F0 */ OP_PREFIX, + /* F1 */ OP_NONE, + /* F2 */ OP_PREFIX, + /* F3 */ OP_PREFIX, + /* F4 */ OP_NONE, + /* F5 */ OP_NONE, + /* F6 */ OP_MODRM, + /* F7 */ OP_MODRM, + /* F8 */ OP_NONE, + /* F9 */ OP_NONE, + /* FA */ OP_NONE, + /* FB */ OP_NONE, + /* FC */ OP_NONE, + /* FD */ OP_NONE, + /* FE */ OP_MODRM, + /* FF */ OP_MODRM + }; + + static byte[] flags_table_ex = + { + /* 0F00 */ OP_MODRM, + /* 0F01 */ OP_MODRM, + /* 0F02 */ OP_MODRM, + /* 0F03 */ OP_MODRM, + /* 0F04 */ OP_INVALID, + /* 0F05 */ OP_NONE, + /* 0F06 */ OP_NONE, + /* 0F07 */ OP_NONE, + /* 0F08 */ OP_NONE, + /* 0F09 */ OP_NONE, + /* 0F0A */ OP_INVALID, + /* 0F0B */ OP_NONE, + /* 0F0C */ OP_INVALID, + /* 0F0D */ OP_MODRM, + /* 0F0E */ OP_INVALID, + /* 0F0F */ OP_MODRM | OP_DATA_I8, //3Dnow + + /* 0F10 */ OP_MODRM, + /* 0F11 */ OP_MODRM, + /* 0F12 */ OP_MODRM, + /* 0F13 */ OP_MODRM, + /* 0F14 */ OP_MODRM, + /* 0F15 */ OP_MODRM, + /* 0F16 */ OP_MODRM, + /* 0F17 */ OP_MODRM, + /* 0F18 */ OP_MODRM, + /* 0F19 */ OP_INVALID, + /* 0F1A */ OP_INVALID, + /* 0F1B */ OP_INVALID, + /* 0F1C */ OP_INVALID, + /* 0F1D */ OP_INVALID, + /* 0F1E */ OP_INVALID, + /* 0F1F */ OP_NONE, + + /* 0F20 */ OP_MODRM, + /* 0F21 */ OP_MODRM, + /* 0F22 */ OP_MODRM, + /* 0F23 */ OP_MODRM, + /* 0F24 */ OP_MODRM | OP_EXTENDED, //SSE5 + /* 0F25 */ OP_INVALID, + /* 0F26 */ OP_MODRM, + /* 0F27 */ OP_INVALID, + /* 0F28 */ OP_MODRM, + /* 0F29 */ OP_MODRM, + /* 0F2A */ OP_MODRM, + /* 0F2B */ OP_MODRM, + /* 0F2C */ OP_MODRM, + /* 0F2D */ OP_MODRM, + /* 0F2E */ OP_MODRM, + /* 0F2F */ OP_MODRM, + + /* 0F30 */ OP_NONE, + /* 0F31 */ OP_NONE, + /* 0F32 */ OP_NONE, + /* 0F33 */ OP_NONE, + /* 0F34 */ OP_NONE, + /* 0F35 */ OP_NONE, + /* 0F36 */ OP_INVALID, + /* 0F37 */ OP_NONE, + /* 0F38 */ OP_MODRM | OP_EXTENDED, + /* 0F39 */ OP_INVALID, + /* 0F3A */ OP_MODRM | OP_EXTENDED | OP_DATA_I8, + /* 0F3B */ OP_INVALID, + /* 0F3C */ OP_INVALID, + /* 0F3D */ OP_INVALID, + /* 0F3E */ OP_INVALID, + /* 0F3F */ OP_INVALID, + + /* 0F40 */ OP_MODRM, + /* 0F41 */ OP_MODRM, + /* 0F42 */ OP_MODRM, + /* 0F43 */ OP_MODRM, + /* 0F44 */ OP_MODRM, + /* 0F45 */ OP_MODRM, + /* 0F46 */ OP_MODRM, + /* 0F47 */ OP_MODRM, + /* 0F48 */ OP_MODRM, + /* 0F49 */ OP_MODRM, + /* 0F4A */ OP_MODRM, + /* 0F4B */ OP_MODRM, + /* 0F4C */ OP_MODRM, + /* 0F4D */ OP_MODRM, + /* 0F4E */ OP_MODRM, + /* 0F4F */ OP_MODRM, + + /* 0F50 */ OP_MODRM, + /* 0F51 */ OP_MODRM, + /* 0F52 */ OP_MODRM, + /* 0F53 */ OP_MODRM, + /* 0F54 */ OP_MODRM, + /* 0F55 */ OP_MODRM, + /* 0F56 */ OP_MODRM, + /* 0F57 */ OP_MODRM, + /* 0F58 */ OP_MODRM, + /* 0F59 */ OP_MODRM, + /* 0F5A */ OP_MODRM, + /* 0F5B */ OP_MODRM, + /* 0F5C */ OP_MODRM, + /* 0F5D */ OP_MODRM, + /* 0F5E */ OP_MODRM, + /* 0F5F */ OP_MODRM, + + /* 0F60 */ OP_MODRM, + /* 0F61 */ OP_MODRM, + /* 0F62 */ OP_MODRM, + /* 0F63 */ OP_MODRM, + /* 0F64 */ OP_MODRM, + /* 0F65 */ OP_MODRM, + /* 0F66 */ OP_MODRM, + /* 0F67 */ OP_MODRM, + /* 0F68 */ OP_MODRM, + /* 0F69 */ OP_MODRM, + /* 0F6A */ OP_MODRM, + /* 0F6B */ OP_MODRM, + /* 0F6C */ OP_MODRM, + /* 0F6D */ OP_MODRM, + /* 0F6E */ OP_MODRM, + /* 0F6F */ OP_MODRM, + + /* 0F70 */ OP_MODRM | OP_DATA_I8, + /* 0F71 */ OP_MODRM | OP_DATA_I8, + /* 0F72 */ OP_MODRM | OP_DATA_I8, + /* 0F73 */ OP_MODRM | OP_DATA_I8, + /* 0F74 */ OP_MODRM, + /* 0F75 */ OP_MODRM, + /* 0F76 */ OP_MODRM, + /* 0F77 */ OP_NONE, + /* 0F78 */ OP_MODRM, + /* 0F79 */ OP_MODRM, + /* 0F7A */ OP_INVALID, + /* 0F7B */ OP_INVALID, + /* 0F7C */ OP_MODRM, + /* 0F7D */ OP_MODRM, + /* 0F7E */ OP_MODRM, + /* 0F7F */ OP_MODRM, + + /* 0F80 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F81 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F82 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F83 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F84 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F85 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F86 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F87 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F88 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F89 */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F8A */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F8B */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F8C */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F8D */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F8E */ OP_RELATIVE | OP_DATA_I16_I32, + /* 0F8F */ OP_RELATIVE | OP_DATA_I16_I32, + + /* 0F90 */ OP_MODRM, + /* 0F91 */ OP_MODRM, + /* 0F92 */ OP_MODRM, + /* 0F93 */ OP_MODRM, + /* 0F94 */ OP_MODRM, + /* 0F95 */ OP_MODRM, + /* 0F96 */ OP_MODRM, + /* 0F97 */ OP_MODRM, + /* 0F98 */ OP_MODRM, + /* 0F99 */ OP_MODRM, + /* 0F9A */ OP_MODRM, + /* 0F9B */ OP_MODRM, + /* 0F9C */ OP_MODRM, + /* 0F9D */ OP_MODRM, + /* 0F9E */ OP_MODRM, + /* 0F9F */ OP_MODRM, + + /* 0FA0 */ OP_NONE, + /* 0FA1 */ OP_NONE, + /* 0FA2 */ OP_NONE, + /* 0FA3 */ OP_MODRM, + /* 0FA4 */ OP_MODRM | OP_DATA_I8, + /* 0FA5 */ OP_MODRM, + /* 0FA6 */ OP_INVALID, + /* 0FA7 */ OP_INVALID, + /* 0FA8 */ OP_NONE, + /* 0FA9 */ OP_NONE, + /* 0FAA */ OP_NONE, + /* 0FAB */ OP_MODRM, + /* 0FAC */ OP_MODRM | OP_DATA_I8, + /* 0FAD */ OP_MODRM, + /* 0FAE */ OP_MODRM, + /* 0FAF */ OP_MODRM, + + /* 0FB0 */ OP_MODRM, + /* 0FB1 */ OP_MODRM, + /* 0FB2 */ OP_MODRM, + /* 0FB3 */ OP_MODRM, + /* 0FB4 */ OP_MODRM, + /* 0FB5 */ OP_MODRM, + /* 0FB6 */ OP_MODRM, + /* 0FB7 */ OP_MODRM, + /* 0FB8 */ OP_MODRM, + /* 0FB9 */ OP_MODRM, + /* 0FBA */ OP_MODRM | OP_DATA_I8, + /* 0FBB */ OP_MODRM, + /* 0FBC */ OP_MODRM, + /* 0FBD */ OP_MODRM, + /* 0FBE */ OP_MODRM, + /* 0FBF */ OP_MODRM, + + /* 0FC0 */ OP_MODRM, + /* 0FC1 */ OP_MODRM, + /* 0FC2 */ OP_MODRM | OP_DATA_I8, + /* 0FC3 */ OP_MODRM, + /* 0FC4 */ OP_MODRM | OP_DATA_I8, + /* 0FC5 */ OP_MODRM | OP_DATA_I8, + /* 0FC6 */ OP_MODRM | OP_DATA_I8, + /* 0FC7 */ OP_MODRM, + /* 0FC8 */ OP_NONE, + /* 0FC9 */ OP_NONE, + /* 0FCA */ OP_NONE, + /* 0FCB */ OP_NONE, + /* 0FCC */ OP_NONE, + /* 0FCD */ OP_NONE, + /* 0FCE */ OP_NONE, + /* 0FCF */ OP_NONE, + + /* 0FD0 */ OP_MODRM, + /* 0FD1 */ OP_MODRM, + /* 0FD2 */ OP_MODRM, + /* 0FD3 */ OP_MODRM, + /* 0FD4 */ OP_MODRM, + /* 0FD5 */ OP_MODRM, + /* 0FD6 */ OP_MODRM, + /* 0FD7 */ OP_MODRM, + /* 0FD8 */ OP_MODRM, + /* 0FD9 */ OP_MODRM, + /* 0FDA */ OP_MODRM, + /* 0FDB */ OP_MODRM, + /* 0FDC */ OP_MODRM, + /* 0FDD */ OP_MODRM, + /* 0FDE */ OP_MODRM, + /* 0FDF */ OP_MODRM, + + /* 0FE0 */ OP_MODRM, + /* 0FE1 */ OP_MODRM, + /* 0FE2 */ OP_MODRM, + /* 0FE3 */ OP_MODRM, + /* 0FE4 */ OP_MODRM, + /* 0FE5 */ OP_MODRM, + /* 0FE6 */ OP_MODRM, + /* 0FE7 */ OP_MODRM, + /* 0FE8 */ OP_MODRM, + /* 0FE9 */ OP_MODRM, + /* 0FEA */ OP_MODRM, + /* 0FEB */ OP_MODRM, + /* 0FEC */ OP_MODRM, + /* 0FED */ OP_MODRM, + /* 0FEE */ OP_MODRM, + /* 0FEF */ OP_MODRM, + + /* 0FF0 */ OP_MODRM, + /* 0FF1 */ OP_MODRM, + /* 0FF2 */ OP_MODRM, + /* 0FF3 */ OP_MODRM, + /* 0FF4 */ OP_MODRM, + /* 0FF5 */ OP_MODRM, + /* 0FF6 */ OP_MODRM, + /* 0FF7 */ OP_MODRM, + /* 0FF8 */ OP_MODRM, + /* 0FF9 */ OP_MODRM, + /* 0FFA */ OP_MODRM, + /* 0FFB */ OP_MODRM, + /* 0FFC */ OP_MODRM, + /* 0FFD */ OP_MODRM, + /* 0FFE */ OP_MODRM, + /* 0FFF */ OP_INVALID, + }; + + static byte cflags(byte op) + { + return flags_table[op]; + } + + static byte cflags_ex(byte op) + { + return flags_table_ex[op]; + } + + /// + /// 计算大于等于 size 字节的最少指令的长度 + /// + /// + /// + public static uint SizeofMinNumByte(void* code, int size) + { + if (IsARM()) + return (uint)((size + 3) / 4) * 4; // 此为 jit 模式下的长度,不再支持 thumb + + uint Length; + byte* pOpcode; + uint Result = 0; + ldasm_data data = new ldasm_data(); + bool is64 = IntPtr.Size == 8; + do + { + Length = ldasm(code, data, is64); + + pOpcode = (byte*)code + data.opcd_offset; + Result += Length; + if (Result >= size) + break; + if ((Length == 1) && (*pOpcode == 0xCC)) + break; + + code = (void*)((ulong)code + Length); + + } while (Length>0); + + return Result; + } + + static bool? s_isArm; + public static bool IsARM() + { + if(s_isArm.HasValue) + return s_isArm.Value; + + var arch = RuntimeInformation.ProcessArchitecture; + s_isArm = arch == Architecture.Arm || arch == Architecture.Arm64; + + return s_isArm.Value; + } + + public static bool IsArm32() + { + return IsARM() && IntPtr.Size == 4; + } + + public static bool IsArm64() + { + return IsARM() && IntPtr.Size == 8; + } + + static bool? s_isiOS; + public static bool IsiOS() + { + if(s_isiOS.HasValue) + return s_isiOS.Value; + + s_isiOS = UnityEngine.SystemInfo.operatingSystem.ToLower().Contains("ios"); + return s_isiOS.Value; + } + + static bool? s_isIL2CPP; + public static bool IsIL2CPP() + { + if (s_isIL2CPP.HasValue) + return s_isIL2CPP.Value; + + try + { + byte[] ilBody = typeof(LDasm).GetMethod("IsIL2CPP").GetMethodBody().GetILAsByteArray(); + if (ilBody == null || ilBody.Length == 0) + s_isIL2CPP = true; + else + s_isIL2CPP = false; + } + catch + { + s_isIL2CPP = true; + } + + return s_isIL2CPP.Value; + } + + public static bool IsThumb(IntPtr code) + { + return IsArm32() && ((long)code & 0x1) == 0x1; + } + + /// + /// 计算 thumb 指令长度 + /// + /// + /// + /// + public static uint CalcARMThumbMinLen(void* code, int size) + { + uint len = 0; + + ushort* ins = (ushort*)code; + while (true) + { + if (len >= size) + return len; + + if (((*ins >> 13) & 3) == 3) + { + ins += 2; + len += 4; + } + else + { + ins++; + len += 2; + } + } + } + + static uint ldasm(void* code, ldasm_data ld, bool is64) + { + byte* p = (byte*)code; + byte s, op, f; + byte rexw, pr_66, pr_67; + + s = rexw = pr_66 = pr_67 = 0; + + /* dummy check */ + if ((int)code==0) + return 0; + + /* init output data */ + //memset(ld, 0, sizeof(ldasm_data)); + + /* phase 1: parse prefixies */ + while ((cflags(*p) & OP_PREFIX)!=0) + { + if (*p == 0x66) + pr_66 = 1; + if (*p == 0x67) + pr_67 = 1; + p++; s++; + ld.flags |= F_PREFIX; + if (s == 15) + { + ld.flags |= F_INVALID; + return s; + } + } + + /* parse REX prefix */ + if (is64 && *p >> 4 == 4) + { + ld.rex = *p; + rexw = (byte)((ld.rex >> 3) & 1); + ld.flags |= F_REX; + p++; s++; + } + + /* can be only one REX prefix */ + if (is64 && *p >> 4 == 4) + { + ld.flags |= F_INVALID; + s++; + return s; + } + + /* phase 2: parse opcode */ + ld.opcd_offset = (byte)(p - (byte*)code); + ld.opcd_size = 1; + op = *p++; s++; + + /* is 2 byte opcode? */ + if (op == 0x0F) + { + op = *p++; s++; + ld.opcd_size++; + f = cflags_ex(op); + if ((f & OP_INVALID)!=0) + { + ld.flags |= F_INVALID; + return s; + } + /* for SSE instructions */ + if ((f & OP_EXTENDED)!=0) + { + op = *p++; s++; + ld.opcd_size++; + } + } + else { + f = cflags(op); + /* pr_66 = pr_67 for opcodes A0-A3 */ + if (op >= 0xA0 && op <= 0xA3) + pr_66 = pr_67; + } + + /* phase 3: parse ModR/M, SIB and DISP */ + if ((f & OP_MODRM)!=0) + { + byte mod = (byte)(*p >> 6); + byte ro = (byte)((*p & 0x38) >> 3); + byte rm = (byte)(*p & 7); + + ld.modrm = *p++; s++; + ld.flags |= F_MODRM; + + /* in F6,F7 opcodes immediate data present if R/O == 0 */ + if (op == 0xF6 && (ro == 0 || ro == 1)) + f |= OP_DATA_I8; + if (op == 0xF7 && (ro == 0 || ro == 1)) + f |= OP_DATA_I16_I32_I64; + + /* is SIB byte exist? */ + if (mod != 3 && rm == 4 && !(!is64 && pr_67!=0)) + { + ld.sib = *p++; s++; + ld.flags |= F_SIB; + + /* if base == 5 and mod == 0 */ + if ((ld.sib & 7) == 5 && mod == 0) + { + ld.disp_size = 4; + } + } + + switch (mod) + { + case 0: + if (is64) + { + if (rm == 5) + { + ld.disp_size = 4; + if (is64) + ld.flags |= F_RELATIVE; + } + } + else if (pr_67!=0) + { + if (rm == 6) + ld.disp_size = 2; + } + else { + if (rm == 5) + ld.disp_size = 4; + } + break; + case 1: + ld.disp_size = 1; + break; + case 2: + if (is64) + ld.disp_size = 4; + else if (pr_67!=0) + ld.disp_size = 2; + else + ld.disp_size = 4; + break; + } + + if (ld.disp_size>0) + { + ld.disp_offset = (byte)(p - (byte*)code); + p += ld.disp_size; + s += ld.disp_size; + ld.flags |= F_DISP; + } + } + + /* phase 4: parse immediate data */ + if (rexw!=0 && (f & OP_DATA_I16_I32_I64)!=0) + ld.imm_size = 8; + else if ((f & OP_DATA_I16_I32)!=0 || (f & OP_DATA_I16_I32_I64)!=0) + ld.imm_size = (byte)(4 - (pr_66 << 1)); + + /* if exist, add OP_DATA_I16 and OP_DATA_I8 size */ + ld.imm_size += (byte)(f & 3); + + if ((ld.imm_size)!=0) + { + s += ld.imm_size; + ld.imm_offset = (byte)(p - (byte*)code); + ld.flags |= F_IMM; + if ((f & OP_RELATIVE)!=0) + ld.flags |= F_RELATIVE; + } + + /* instruction is too long */ + if (s > 15) + ld.flags |= F_INVALID; + + return s; + } + } +} diff --git a/Editor/3rds/UnityHook/LDasm.cs.meta b/Editor/3rds/UnityHook/LDasm.cs.meta new file mode 100644 index 0000000..969a5a1 --- /dev/null +++ b/Editor/3rds/UnityHook/LDasm.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c561c9729c367e4fbef63f4ec56f268 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/MethodHook.cs b/Editor/3rds/UnityHook/MethodHook.cs new file mode 100644 index 0000000..e7acd0a --- /dev/null +++ b/Editor/3rds/UnityHook/MethodHook.cs @@ -0,0 +1,381 @@ +/* + Desc: 一个可以运行时 Hook Mono 方法的工具,让你可以无需修改 UnityEditor.dll 等文件就可以重写其函数功能 + Author: Misaka Mikoto + Github: https://github.com/Misaka-Mikoto-Tech/MonoHook + */ + +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; + + +/* +>>>>>>> 原始 UnityEditor.LogEntries.Clear 一型(.net 4.x) +0000000000403A00 < | 55 | push rbp | +0000000000403A01 | 48 8B EC | mov rbp,rsp | +0000000000403A04 | 48 81 EC 80 00 00 00 | sub rsp,80 | +0000000000403A0B | 48 89 65 B0 | mov qword ptr ss:[rbp-50],rsp | +0000000000403A0F | 48 89 6D A8 | mov qword ptr ss:[rbp-58],rbp | +0000000000403A13 | 48 89 5D C8 | mov qword ptr ss:[rbp-38],rbx | << +0000000000403A17 | 48 89 75 D0 | mov qword ptr ss:[rbp-30],rsi | +0000000000403A1B | 48 89 7D D8 | mov qword ptr ss:[rbp-28],rdi | +0000000000403A1F | 4C 89 65 E0 | mov qword ptr ss:[rbp-20],r12 | +0000000000403A23 | 4C 89 6D E8 | mov qword ptr ss:[rbp-18],r13 | +0000000000403A27 | 4C 89 75 F0 | mov qword ptr ss:[rbp-10],r14 | +0000000000403A2B | 4C 89 7D F8 | mov qword ptr ss:[rbp-8],r15 | +0000000000403A2F | 49 BB 00 2D 1E 1A FE 7F 00 00 | mov r11,7FFE1A1E2D00 | +0000000000403A39 | 4C 89 5D B8 | mov qword ptr ss:[rbp-48],r11 | +0000000000403A3D | 49 BB 08 2D 1E 1A FE 7F 00 00 | mov r11,7FFE1A1E2D08 | + + +>>>>>>> 二型(.net 2.x) +0000000000403E8F | 55 | push rbp | +0000000000403E90 | 48 8B EC | mov rbp,rsp | +0000000000403E93 | 48 83 EC 70 | sub rsp,70 | +0000000000403E97 | 48 89 65 C8 | mov qword ptr ss:[rbp-38],rsp | +0000000000403E9B | 48 89 5D B8 | mov qword ptr ss:[rbp-48],rbx | +0000000000403E9F | 48 89 6D C0 | mov qword ptr ss:[rbp-40],rbp | <<(16) +0000000000403EA3 | 48 89 75 F8 | mov qword ptr ss:[rbp-8],rsi | +0000000000403EA7 | 48 89 7D F0 | mov qword ptr ss:[rbp-10],rdi | +0000000000403EAB | 4C 89 65 D0 | mov qword ptr ss:[rbp-30],r12 | +0000000000403EAF | 4C 89 6D D8 | mov qword ptr ss:[rbp-28],r13 | +0000000000403EB3 | 4C 89 75 E0 | mov qword ptr ss:[rbp-20],r14 | +0000000000403EB7 | 4C 89 7D E8 | mov qword ptr ss:[rbp-18],r15 | +0000000000403EBB | 48 83 EC 20 | sub rsp,20 | +0000000000403EBF | 49 BB 18 3F 15 13 FE 7F 00 00 | mov r11,7FFE13153F18 | +0000000000403EC9 | 41 FF D3 | call r11 | +0000000000403ECC | 48 83 C4 20 | add rsp,20 | + +>>>>>>>>> arm64 +il2cpp:00000000003DE714 F5 0F 1D F8 STR X21, [SP,#-0x10+var_20]! | << absolute safe +il2cpp:00000000003DE718 F4 4F 01 A9 STP X20, X19, [SP,#0x20+var_10] | << may be safe +il2cpp:00000000003DE71C FD 7B 02 A9 STP X29, X30, [SP,#0x20+var_s0] | +il2cpp:00000000003DE720 FD 83 00 91 ADD X29, SP, #0x20 | +il2cpp:00000000003DE724 B5 30 00 B0 ADRP X21, #_ZZ62GameObject_SetActive_mCF1EEF2A314F3AE | << dangerous: relative instruction, can not be overwritten +il2cpp:00000000003DE728 A2 56 47 F9 LDR method, [X21,#_ZZ62GameObject_SetActive_mCF] ; | +il2cpp:00000000003DE72C F3 03 01 2A MOV W19, W1 | + */ + +namespace MonoHook +{ + /// + /// Hook 类,用来 Hook 某个 C# 方法 + /// + 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; } // 需要被hook的目标方法 + public MethodBase replacementMethod { get; private set; } // 被hook后的替代方法 + public MethodBase proxyMethod { get; private set; } // 目标方法的代理方法(可以通过此方法调用被hook后的原方法) + + private IntPtr _targetPtr; // 目标方法被 jit 后的地址指针 + private IntPtr _replacementPtr; + private IntPtr _proxyPtr; + + private CodePatcher _codePatcher; + +#if UNITY_EDITOR + /// + /// call `MethodInfo.MethodHandle.GetFunctionPointer()` + /// will visit static class `UnityEditor.IMGUI.Controls.TreeViewGUI.Styles` and invoke its static constructor, + /// and init static filed `foldout`, but `GUISKin.current` is null now, + /// so we should wait until `GUISKin.current` has a valid value + /// + private static FieldInfo s_fi_GUISkin_current; +#endif + + static MethodHook() + { +#if UNITY_EDITOR + s_fi_GUISkin_current = typeof(GUISkin).GetField("current", BindingFlags.Static | BindingFlags.NonPublic); +#endif + } + + /// + /// 创建一个 Hook + /// + /// 需要替换的目标方法 + /// 准备好的替换方法 + /// 如果还需要调用原始目标方法,可以通过此参数的方法调用,如果不需要可以填 null + 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()) // iOS 不支持修改 code 所在区域 page + return; + + if (isHooked) + return; + +#if UNITY_EDITOR + 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 + int minMethodBodySize = 10; + + { + if ((targetMethod.MethodImplementationFlags & MethodImplAttributes.InternalCall) != MethodImplAttributes.InternalCall) + { + int codeSize = targetMethod.GetMethodBody().GetILAsByteArray().Length; // GetMethodBody can not call on il2cpp + 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) // 2G + _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); + } + } + + /// + /// 获取对应函数jit后的native code的地址 + /// + 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)] // 好像在 IL2CPP 里无效 + 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 + { + /* + // System.Reflection.MonoMethod + typedef struct Il2CppReflectionMethod + { + Il2CppObject object; + const MethodInfo *method; + Il2CppString *name; + Il2CppReflectionType *reftype; + } Il2CppReflectionMethod; + + typedef Il2CppClass Il2CppVTable; + typedef struct Il2CppObject + { + union + { + Il2CppClass *klass; + Il2CppVTable *vtable; + }; + MonitorData *monitor; + } Il2CppObject; + + typedef struct MethodInfo + { + Il2CppMethodPointer methodPointer; // this is the pointer to native code of method + InvokerMethod invoker_method; + const char* name; + Il2CppClass *klass; + const Il2CppType *return_type; + const ParameterInfo* parameters; + // ... + } + */ + + __ForCopy __forCopy = new __ForCopy() { method = method }; + + long* ptr = &__forCopy.__dummy; + ptr++; // addr of _forCopy.method + + IntPtr methodAddr = IntPtr.Zero; + if (sizeof(IntPtr) == 8) + { + long methodDataAddr = *(long*)ptr; + byte* ptrData = (byte*)methodDataAddr + sizeof(IntPtr) * 2; // offset of Il2CppReflectionMethod::const MethodInfo *method; + + long methodPtr = 0; + methodPtr = *(long*)ptrData; + methodAddr = new IntPtr(*(long*)methodPtr); // MethodInfo::Il2CppMethodPointer methodPointer; + } + else + { + int methodDataAddr = *(int*)ptr; + byte* ptrData = (byte*)methodDataAddr + sizeof(IntPtr) * 2; // offset of Il2CppReflectionMethod::const MethodInfo *method; + + int methodPtr = 0; + methodPtr = *(int*)ptrData; + methodAddr = new IntPtr(*(int*)methodPtr); + } + return methodAddr; + } + } + +#if UNITY_EDITOR + private void OnEditorUpdate() + { + if (s_fi_GUISkin_current.GetValue(null) != null) + { + try + { + DoInstall(); + } + finally + { + EditorApplication.update -= OnEditorUpdate; + } + } + } +#endif + + #endregion + } + +} diff --git a/Editor/3rds/UnityHook/MethodHook.cs.meta b/Editor/3rds/UnityHook/MethodHook.cs.meta new file mode 100644 index 0000000..007e62c --- /dev/null +++ b/Editor/3rds/UnityHook/MethodHook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd0b8071cf434d6498160259e3829980 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/Plugins.meta b/Editor/3rds/UnityHook/Plugins.meta new file mode 100644 index 0000000..f6f9520 --- /dev/null +++ b/Editor/3rds/UnityHook/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 82346623158bae349a8347ae74662b12 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/Plugins/Utils.cpp b/Editor/3rds/UnityHook/Plugins/Utils.cpp new file mode 100644 index 0000000..106d990 --- /dev/null +++ b/Editor/3rds/UnityHook/Plugins/Utils.cpp @@ -0,0 +1,23 @@ +// +// Utils.cpp +// MonoHookUtils_OSX +// +// Created by Misaka-Mikoto on 2022/8/31. +// +#include +#include +#include +#include +#include + +extern "C"{ + +void* memcpy_jit(void* dst, void* src, int32_t size) +{ + pthread_jit_write_protect_np(0); + void* ret = memcpy(dst, src, size); + pthread_jit_write_protect_np(1); + sys_icache_invalidate (dst, size); + return ret; +} +} diff --git a/Editor/3rds/UnityHook/Plugins/Utils.cpp.meta b/Editor/3rds/UnityHook/Plugins/Utils.cpp.meta new file mode 100644 index 0000000..447c3db --- /dev/null +++ b/Editor/3rds/UnityHook/Plugins/Utils.cpp.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: 56b28b5583a184c669dcb968d175544c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: ARM64 + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/3rds/UnityHook/Plugins/libMonoHookUtils_OSX.dylib b/Editor/3rds/UnityHook/Plugins/libMonoHookUtils_OSX.dylib new file mode 100644 index 0000000..07f9063 Binary files /dev/null and b/Editor/3rds/UnityHook/Plugins/libMonoHookUtils_OSX.dylib differ diff --git a/Editor/3rds/UnityHook/Plugins/libMonoHookUtils_OSX.dylib.meta b/Editor/3rds/UnityHook/Plugins/libMonoHookUtils_OSX.dylib.meta new file mode 100644 index 0000000..48e28e3 --- /dev/null +++ b/Editor/3rds/UnityHook/Plugins/libMonoHookUtils_OSX.dylib.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: 4adb23596911347faa69537b900c9f5e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Installer/InstallerController.cs b/Editor/Installer/InstallerController.cs index a163e88..f867357 100644 --- a/Editor/Installer/InstallerController.cs +++ b/Editor/Installer/InstallerController.cs @@ -266,10 +266,6 @@ namespace HybridCLR.Editor.Installer Debug.LogError($"未找到当前版本:{curVersionStr} 对应的改造过的 Unity.IL2CPP.dll,打包出的程序将会崩溃"); } } - if (version.major >= 2021) - { - Debug.LogError($"如果需要打包iOS,必须手动替换UnityEditor.CoreModule.dll为修改后的版本,否则无法获得AOT dlls。详见 https://focus-creative-games.github.io/hybridclr/modify_unity_dll/#unityeditor-coremodule-dll"); - } if (HasInstalledHybridCLR()) { Debug.Log("安装成功"); diff --git a/package.json b/package.json index e2ce069..6561291 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.focus-creative-games.hybridclr_unity", - "version": "2.0.0", + "version": "2.0.1", "displayName": "HybridCLR", "description": "Unity package for HybridCLR. It includes editor and runtime scripts and assets for HybridCLR", "category": "Runtime",