private void Init() { try { var methodField = UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original); if (methodField == null) { throw new Exception($"No IL2CPP equivalent found for {Original.FullDescription()}. The target might have been automatically generated by Unhollower (e.g. field accessor)."); } // Get the native MethodInfo struct for the target method originalNativeMethodInfo = (Il2CppMethodInfo *)(IntPtr)methodField.GetValue(null); // Create a trampoline from the original target method var trampolinePtr = DetourGenerator.CreateTrampolineFromFunction(originalNativeMethodInfo->methodPointer, out _, out _); // Create a modified native MethodInfo struct to point towards the trampoline modifiedNativeMethodInfo = (Il2CppMethodInfo *)Marshal.AllocHGlobal(Marshal.SizeOf <Il2CppMethodInfo>()); Marshal.StructureToPtr(*originalNativeMethodInfo, (IntPtr)modifiedNativeMethodInfo, false); modifiedNativeMethodInfo->methodPointer = trampolinePtr; isValid = true; } catch (Exception e) { DetourLogger.LogWarning($"Failed to init IL2CPP patch backend for {Original.FullDescription()}, using normal patch handlers: {e.Message}"); } }
private void GenerateTrampolineInner(out int trampolineLength, out int jmpLength) { if (TrampolinePtr != IntPtr.Zero) { trampolineLength = TrampolineSize; jmpLength = TrampolineJmpSize; return; } var instructionBuffer = new byte[32]; Marshal.Copy(OriginalFunctionPtr, instructionBuffer, 0, 32); var trampolineAlloc = PageAllocator.Instance.Allocate(OriginalFunctionPtr); logger.Log(LogLevel.Debug, $"Original: {OriginalFunctionPtr.ToInt64():X}, Trampoline: {trampolineAlloc:X}, diff: {Math.Abs(OriginalFunctionPtr.ToInt64() - trampolineAlloc):X}; is within +-1GB range: {PageAllocator.IsInRelJmpRange(OriginalFunctionPtr, trampolineAlloc)}"); DetourHelper.Native.MakeWritable(trampolineAlloc, PageAllocator.PAGE_SIZE); var arch = IntPtr.Size == 8 ? Architecture.X64 : Architecture.X86; DetourGenerator.CreateTrampolineFromFunction(instructionBuffer, OriginalFunctionPtr, trampolineAlloc, DetourGenerator.GetDetourLength(arch), arch, out trampolineLength, out jmpLength); DetourHelper.Native.MakeExecutable(trampolineAlloc, PageAllocator.PAGE_SIZE); TrampolinePtr = trampolineAlloc; TrampolineSize = trampolineLength; TrampolineJmpSize = jmpLength; }
private void GenerateTrampolineInner(out int trampolineLength, out int jmpLength) { if (TrampolinePtr != IntPtr.Zero) { trampolineLength = TrampolineSize; jmpLength = TrampolineJmpSize; return; } byte[] instructionBuffer = new byte[32]; Marshal.Copy(OriginalFunctionPtr, instructionBuffer, 0, 32); var trampolineAlloc = DetourHelper.Native.MemAlloc(80); DetourHelper.Native.MakeWritable(trampolineAlloc, 80); var arch = IntPtr.Size == 8 ? Architecture.X64 : Architecture.X86; DetourGenerator.CreateTrampolineFromFunction(instructionBuffer, OriginalFunctionPtr, trampolineAlloc, DetourGenerator.GetDetourLength(arch), arch, out trampolineLength, out jmpLength); DetourHelper.Native.MakeExecutable(trampolineAlloc, 80); TrampolinePtr = trampolineAlloc; TrampolineSize = trampolineLength; TrampolineJmpSize = jmpLength; }
public void Apply(ManualLogSource debuggerLogSource) { if (IsApplied) { return; } DetourHelper.Native.MakeWritable(OriginalFunctionPtr, 32); if (debuggerLogSource != null) { debuggerLogSource.LogDebug($"Detouring 0x{OriginalFunctionPtr.ToString("X")} -> 0x{DetourFunctionPtr.ToString("X")}"); debuggerLogSource.LogDebug("Original (32) asm"); DetourGenerator.Disassemble(debuggerLogSource, OriginalFunctionPtr, 32); } var arch = IntPtr.Size == 8 ? Architecture.X64 : Architecture.X86; GenerateTrampolineInner(out int trampolineLength, out int jmpLength); DetourGenerator.ApplyDetour(OriginalFunctionPtr, DetourFunctionPtr, arch, trampolineLength - jmpLength); if (debuggerLogSource != null) { debuggerLogSource.LogDebug($"Trampoline allocation: 0x{TrampolinePtr.ToString("X")}"); debuggerLogSource.LogDebug("Modified (32) asm"); DetourGenerator.Disassemble(debuggerLogSource, OriginalFunctionPtr, 32); debuggerLogSource.LogDebug($"Trampoline ({trampolineLength}) asm"); DetourGenerator.Disassemble(debuggerLogSource, TrampolinePtr, trampolineLength); } DetourHelper.Native.MakeExecutable(OriginalFunctionPtr, 32); IsApplied = true; }
public void TrampolineTest(int bitness) { byte[] exampleCode = { 0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D, 0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B, 0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF }; var exampleCodePointer = Marshal.AllocHGlobal(80); var trampolineCodePointer = Marshal.AllocHGlobal(80); Marshal.Copy(exampleCode, 0, exampleCodePointer, exampleCode.Length); void Disassemble(byte[] data, ulong ip) { var formatter = new NasmFormatter(); var output = new StringOutput(); var codeReader = new ByteArrayCodeReader(data); var decoder = Decoder.Create(bitness, codeReader); decoder.IP = ip; while (codeReader.CanReadByte) { decoder.Decode(out var instr); formatter.Format(instr, output); Console.WriteLine($"{instr.IP:X16} {output.ToStringAndReset()}"); } Console.WriteLine(); } Console.WriteLine("Original:"); Console.WriteLine(); Disassemble(exampleCode, (ulong)exampleCodePointer.ToInt64()); DetourGenerator.CreateTrampolineFromFunction(exampleCodePointer, out var trampolineLength, out _); Console.WriteLine("Modified:"); Console.WriteLine(); Marshal.Copy(exampleCodePointer, exampleCode, 0, exampleCode.Length); Disassemble(exampleCode, (ulong)exampleCodePointer.ToInt64()); Console.WriteLine(); Console.WriteLine("Trampoline:"); Console.WriteLine(); var trampolineArray = new byte[trampolineLength]; Marshal.Copy(trampolineCodePointer, trampolineArray, 0, trampolineLength); Disassemble(trampolineArray, (ulong)trampolineCodePointer.ToInt64()); Marshal.FreeHGlobal(exampleCodePointer); Marshal.FreeHGlobal(trampolineCodePointer); Assert.IsFalse(false); }