mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-20 01:46:13 +00:00
Implement FastNativeDetour and clean up chainloader
This commit is contained in:
4 files changed
+159
-49
No files matched your search
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
@@ -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 trampolinePtr = TrampolineGenerator.Generate(unhollowerLogSource, originalFunc, patchedFunctionPtr);
|
||||
|
||||
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);
|
||||
|
||||
*(void**)ptr = (void*)trampolineAlloc;
|
||||
*(void**)ptr = (void*)trampolinePtr;
|
||||
};
|
||||
|
||||
var gameAssemblyModule = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().First(x => x.ModuleName.Contains("GameAssembly"));
|
||||
|
||||
Reference in new issue
Block a user