【发布时间】:2020-12-04 13:22:55
【问题描述】:
我想从同一个进程中加载的 DLL 中挂钩一个函数,因此每次调用该函数时都会执行我的自定义函数。
为了做到这一点,我已经声明了一个虚拟函数,它将替换 DLL 中的原始函数:
public static bool hookFunc(string a, ulong b, string c, IntPtr d)
{
Console.WriteLine("hi");
return true;
}
之后,我声明了一个Delegate,并生成了一个指向我的hookFunc 函数的新实例,然后我使用GetFunctionPointerForDelegate() 从委托中获取了一个指针。我还生成了用于修补原始函数内存的字节序列,并使用函数 hookFunc 的内存地址(从新指针获得)完成此序列:
public delegate bool hookDel(string a, ulong b, string c, IntPtr d);
public void hookManager()
{
var a = new hookDel(hookFunc);
var hookPtr = Marshal.GetFunctionPointerForDelegate(a);
var hookPointerBytes = BitConverter.GetBytes(hookPtr.ToInt64());
newBytes = new byte[] { 0x49, 0xbb, hookPointerBytes[0], hookPointerBytes[1], hookPointerBytes[2], hookPointerBytes[3], hookPointerBytes[4], hookPointerBytes[5],
hookPointerBytes[6], hookPointerBytes[7], 0x41, 0xff, 0xe3 };
}
此时,变量newBytes翻译成汇编的内容应该是如下指令:
mov r11, [memory address of my hookFunc function]
jmp r11
最后,我使用 GetProcAddress 获取原始 DLL 的函数内存地址,并使用 WriteProcessMemory 修补第一个字节,使用变量newBytes 的内容进行替换。在遇到几个 StackOverflow 错误后,我用 WinDbg 调试了代码,我发现变量 hookPointerBytes 不包含 hookFunc 的地址,因此触发钩子时jmp 指令将代码执行流驱动到没有可执行代码的内存地址,这可能是我的程序崩溃的原因。
我已经验证 DLL 的函数挂钩已成功完成,并且正在执行 jmp 指令。我的问题是,为什么我没有通过使用从Delegate 获得的指针得到 hookFunc 的真实内存地址?在 C# 中还有其他方法可以获取我的 hookFunc 函数的内存地址吗?
【问题讨论】:
-
我的猜测是您的问题是委托正在被垃圾收集。根据
Marshal.GetFunctionPointerForDelegate的文档:“您必须手动阻止垃圾收集器从托管代码中收集委托。垃圾收集器不会跟踪对非托管代码的引用。” -
@RossRidge 我已经尝试了你所说的在委托和指针上使用 GC.KeepAlive() ,但仍然得到同样的错误。
-
@KuroshD。你想要
GCHandle.Alloc()而不是GC.KeepAlive()
标签: c# assembly delegates hook function-pointers