mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-19 15:51:45 +00:00
Follow PE export jmp thunks before applying a native detour (#1387)
* Follow PE export jmp thunks before applying a native detour. GetProcAddress of il2cpp_runtime_invoke can be a jmp rel32 stub. Dobby aborting on that stub is BepInEx#1188-class. Resolve E9 and FF 25 in INativeDetour.Create so Default/Dobby and Funchook all patch the prologue. No-op when the pointer is already a function body. Co-authored-by: Cursor <cursoragent@cursor.com> * Use IntPtr.Size when following FF 25 thunks. nint.Size does not exist on the net6.0 BepInEx IL2CPP target. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
7cb1246fcc
commit
5b766a3b7f
1 file changed
+45
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using BepInEx.Configuration;
|
||||
using BepInEx.Logging;
|
||||
using BepInEx.Unity.IL2CPP.Hook.Dobby;
|
||||
using BepInEx.Unity.IL2CPP.Hook.Funchook;
|
||||
using MonoMod.RuntimeDetour;
|
||||
@@ -17,6 +19,8 @@ public interface INativeDetour : IDetour
|
||||
"The native provider to use for managed detours"
|
||||
);
|
||||
|
||||
private static readonly ManualLogSource ThunkLog = Logger.CreateLogSource("NativeDetour");
|
||||
|
||||
public nint OriginalMethodPtr { get; }
|
||||
public nint DetourMethodPtr { get; }
|
||||
public nint TrampolinePtr { get; }
|
||||
@@ -27,6 +31,11 @@ public interface INativeDetour : IDetour
|
||||
|
||||
public static INativeDetour Create<T>(nint original, T target) where T : Delegate
|
||||
{
|
||||
var resolved = FollowExportThunks(original);
|
||||
if (resolved != original)
|
||||
ThunkLog.LogDebug($"Resolved export thunk 0x{original:X} -> 0x{resolved:X}");
|
||||
original = resolved;
|
||||
|
||||
var detour = DetourProviderType.Value switch
|
||||
{
|
||||
DetourProvider.Dobby => new DobbyDetour(original, target),
|
||||
@@ -41,6 +50,42 @@ public interface INativeDetour : IDetour
|
||||
return detour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PE exports are often a <c>jmp rel32</c> (or <c>jmp [rip+disp]</c>) stub with int3 padding.
|
||||
/// Dobby aborts if asked to patch that stub. Follow to the real prologue first.
|
||||
/// No-op when the pointer is already a function body.
|
||||
/// </summary>
|
||||
private static nint FollowExportThunks(nint func)
|
||||
{
|
||||
if (func == 0) return func;
|
||||
var fn = func;
|
||||
for (var hops = 0; hops < 8; hops++)
|
||||
{
|
||||
byte op;
|
||||
try { op = Marshal.ReadByte(fn); }
|
||||
catch { return fn; }
|
||||
|
||||
if (op == 0xE9)
|
||||
{
|
||||
fn += 5 + Marshal.ReadInt32(fn + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op == 0xFF && Marshal.ReadByte(fn + 1) == 0x25)
|
||||
{
|
||||
if (IntPtr.Size == 8)
|
||||
fn = Marshal.ReadIntPtr(fn + 6 + Marshal.ReadInt32(fn + 2));
|
||||
else
|
||||
fn = Marshal.ReadIntPtr((nint)(uint)Marshal.ReadInt32(fn + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
public static INativeDetour CreateAndApply<T>(nint from, T to, out T original)
|
||||
where T : Delegate
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user