Selaa lähdekoodia

Implement FastNativeDetour and clean up chainloader

Bepis 3 vuotta sitten
vanhempi
commit
41de02ae00

+ 3 - 1
BepInEx.IL2CPP/BepInEx.IL2CPP.csproj

@@ -79,10 +79,11 @@
     <Compile Include="BasePlugin.cs" />
     <Compile Include="DoorstopEntrypoint.cs" />
     <Compile Include="EnvVars.cs" />
+    <Compile Include="Hook\FastNativeDetour.cs" />
     <Compile Include="IL2CPPChainloader.cs" />
     <Compile Include="Preloader.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="TrampolineGenerator.cs" />
+    <Compile Include="Hook\TrampolineGenerator.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\BepInEx.Core\BepInEx.Core.csproj">
@@ -102,5 +103,6 @@
     <None Include="app.config" />
     <None Include="packages.config" />
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>

+ 90 - 0
BepInEx.IL2CPP/Hook/FastNativeDetour.cs

@@ -0,0 +1,90 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using MonoMod.RuntimeDetour;
+using MonoMod.Utils;
+
+namespace BepInEx.IL2CPP.Hook
+{
+	public class FastNativeDetour : IDetour
+	{
+		protected byte[] BackupBytes { get; set; }
+
+		public bool IsValid { get; protected set; } = true;
+		public bool IsApplied { get; protected set; }
+
+
+		public IntPtr OriginalFuncPtr { get; protected set; }
+		public IntPtr DetourFuncPtr { get; protected set; }
+
+
+		public IntPtr TrampolinePtr { get; protected set; } = IntPtr.Zero;
+		public int TrampolineSize { get; protected set; } = 0;
+
+		protected MethodInfo TrampolineMethod { get; set; }
+
+
+		public FastNativeDetour(IntPtr originalFuncPtr, IntPtr detourFuncPtr)
+		{
+			OriginalFuncPtr = originalFuncPtr;
+			DetourFuncPtr = detourFuncPtr;
+
+			// TODO: This may not be safe during undo if the method is smaller than 20 bytes
+			BackupBytes = new byte[20];
+			Marshal.Copy(originalFuncPtr, BackupBytes, 0, 20);
+		}
+
+
+		public void Apply()
+		{
+			TrampolinePtr = TrampolineGenerator.Generate(OriginalFuncPtr, DetourFuncPtr, out int trampolineLength);
+			TrampolineSize = trampolineLength;
+
+			IsApplied = true;
+		}
+
+		public void Undo()
+		{
+			Marshal.Copy(BackupBytes, 0, OriginalFuncPtr, BackupBytes.Length);
+
+			DetourHelper.Native.MemFree(TrampolinePtr);
+
+			TrampolinePtr = IntPtr.Zero;
+			TrampolineSize = 0;
+
+			IsApplied = false;
+		}
+
+		public void Free()
+		{
+			IsValid = false;
+		}
+
+		public MethodBase GenerateTrampoline(MethodBase signature = null)
+		{
+			if (TrampolineMethod == null)
+			{
+				TrampolineMethod = DetourHelper.GenerateNativeProxy(TrampolinePtr, signature);
+			}
+
+			return TrampolineMethod;
+		}
+
+		public T GenerateTrampoline<T>() where T : Delegate
+		{
+			if (!typeof(Delegate).IsAssignableFrom(typeof(T)))
+				throw new InvalidOperationException($"Type {typeof(T)} not a delegate type.");
+
+			return GenerateTrampoline(typeof(T).GetMethod("Invoke")).CreateDelegate(typeof(T)) as T;
+		}
+
+		public void Dispose()
+		{
+			if (!IsValid)
+				return;
+
+			Undo();
+			Free();
+		}
+	}
+}

+ 62 - 0
BepInEx.IL2CPP/TrampolineGenerator.cs

@@ -1,12 +1,74 @@
 using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
+using BepInEx.Logging;
 using Iced.Intel;
+using MonoMod.RuntimeDetour;
 
 namespace BepInEx.IL2CPP
 {
 	public static class TrampolineGenerator
 	{
+		public static IntPtr Generate(IntPtr originalFunctionPtr, IntPtr patchedFunctionPtr, out int trampolineLength)
+		{
+			var trampolineAlloc = DetourHelper.Native.MemAlloc(80);
+			DetourHelper.Native.MakeExecutable(trampolineAlloc, 80);
+			DetourHelper.Native.MakeWritable(originalFunctionPtr, 24);
+
+			trampolineLength = Generate(originalFunctionPtr, patchedFunctionPtr, trampolineAlloc, IntPtr.Size == 8 ? 64 : 32);
+
+			return trampolineAlloc;
+		}
+
+		private static void Disassemble(ManualLogSource logSource, IntPtr memoryPtr, int size)
+		{
+			byte[] data = new byte[size];
+			Marshal.Copy(memoryPtr, data, 0, size);
+
+			var formatter = new NasmFormatter();
+			var output = new StringOutput();
+			var codeReader = new ByteArrayCodeReader(data);
+			var decoder = Decoder.Create(64, codeReader);
+			decoder.IP = (ulong)memoryPtr.ToInt64();
+			while (codeReader.CanReadByte)
+			{
+				decoder.Decode(out var instr);
+				formatter.Format(instr, output);
+				logSource.LogDebug($"{instr.IP:X16} {output.ToStringAndReset()}");
+			}
+		}
+
+		public static IntPtr Generate(ManualLogSource logSource, IntPtr originalFunctionPtr, IntPtr patchedFunctionPtr)
+		{
+			logSource.LogDebug($"DoHook 0x{originalFunctionPtr.ToString("X")} -> 0x{patchedFunctionPtr.ToString("X")}");
+
+			var trampolineAlloc = DetourHelper.Native.MemAlloc(80);
+			DetourHelper.Native.MakeExecutable(trampolineAlloc, 80);
+			DetourHelper.Native.MakeWritable(originalFunctionPtr, 24);
+
+			logSource.LogDebug($"Trampoline allocation: 0x{trampolineAlloc.ToString("X")}");
+
+
+			logSource.LogDebug("Original (24) asm");
+
+
+			Disassemble(logSource, originalFunctionPtr, 24);
+
+
+			var trampolineLength = Generate(originalFunctionPtr, patchedFunctionPtr, trampolineAlloc, IntPtr.Size == 8 ? 64 : 32);
+
+
+			logSource.LogDebug("Modified (24) asm");
+
+			Disassemble(logSource, originalFunctionPtr, 24);
+
+			logSource.LogDebug($"Trampoline ({trampolineLength}) asm");
+
+			Disassemble(logSource, trampolineAlloc, trampolineLength);
+
+			return trampolineAlloc;
+		}
+
 		public static int Generate(IntPtr originalFunctionPtr, IntPtr patchedFunctionPtr, IntPtr trampolineFunctionPtr, int bitness)
 		{
 			byte[] instructionBuffer = new byte[80];

+ 4 - 48
BepInEx.IL2CPP/IL2CPPChainloader.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Diagnostics;
-using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.InteropServices;
@@ -8,7 +7,6 @@ using BepInEx.Bootstrap;
 using BepInEx.Logging;
 using BepInEx.Preloader.Core;
 using BepInEx.Preloader.Core.Logging;
-using Iced.Intel;
 using MonoMod.RuntimeDetour;
 using UnhollowerBaseLib.Runtime;
 using UnhollowerRuntimeLib;
@@ -58,23 +56,6 @@ namespace BepInEx.IL2CPP
 			}
 		}
 
-		private static void Disassemble(ManualLogSource logSource, IntPtr memoryPtr, int size)
-		{
-			byte[] data = new byte[size];
-			Marshal.Copy(memoryPtr, data, 0, size);
-
-			var formatter = new NasmFormatter();
-			var output = new StringOutput();
-			var codeReader = new ByteArrayCodeReader(data);
-			var decoder = Decoder.Create(64, codeReader);
-			decoder.IP = (ulong)memoryPtr.ToInt64();
-			while (codeReader.CanReadByte)
-			{
-				decoder.Decode(out var instr);
-				formatter.Format(instr, output);
-				logSource.LogDebug($"{instr.IP:X16} {output.ToStringAndReset()}");
-			}
-		}
 
 		public override unsafe void Initialize(string gameExePath = null)
 		{
@@ -84,40 +65,15 @@ namespace BepInEx.IL2CPP
 
 			// One or the other here for Unhollower to work correctly
 
-			////////ClassInjector.Detour = new DetourHandler();
+			//ClassInjector.Detour = new DetourHandler();
 
-			ClassInjector.DoHook = (ptr, intPtr) =>
+			ClassInjector.DoHook = (ptr, patchedFunctionPtr) =>
 			{
 				IntPtr originalFunc = new IntPtr(*(void**)ptr);
 
-				unhollowerLogSource.LogDebug($"DoHook 0x{originalFunc.ToString("X")} -> 0x{intPtr.ToString("X")}");
-
-				var trampolineAlloc = DetourHelper.Native.MemAlloc(80);
-				DetourHelper.Native.MakeExecutable(trampolineAlloc, 80);
-				DetourHelper.Native.MakeWritable(originalFunc, 24);
-
-				unhollowerLogSource.LogDebug($"Trampoline allocation: 0x{ptr.ToString("X")}");
-
-
-				unhollowerLogSource.LogDebug("Original (24) asm");
-
-
-				Disassemble(unhollowerLogSource, originalFunc, 24);
-
-
-				var trampolineLength = TrampolineGenerator.Generate(originalFunc, intPtr, trampolineAlloc,
-					IntPtr.Size == 8 ? 64 : 32);
-
-
-				unhollowerLogSource.LogDebug("Modified (24) asm");
-
-				Disassemble(unhollowerLogSource, originalFunc, 24);
-
-				unhollowerLogSource.LogDebug($"Trampoline ({trampolineLength}) asm");
-
-				Disassemble(unhollowerLogSource, trampolineAlloc, trampolineLength);
+				var trampolinePtr = TrampolineGenerator.Generate(unhollowerLogSource, originalFunc, patchedFunctionPtr);
 
-				*(void**)ptr = (void*)trampolineAlloc;
+				*(void**)ptr = (void*)trampolinePtr;
 			};
 
 			var gameAssemblyModule = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().First(x => x.ModuleName.Contains("GameAssembly"));