.net delegate 万能适配
作者:互联网
遇到一个技术点,记一下,.net 有一个 Delegate Marshall.GetDelegateForFunctionPointer(IntPtr ptr, Type t)
用来将内存地址映射为一个 delegate,转为 delegate 后就可以对内存段的二进制代码进行 .net 内的调用了。例如 ptr 是 VirtualAlloc 的地址,t 是具有 IntPtr(IntPtr lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect)
签名的代理类型,那么获得的就是一个这个 delegate 的实例:
delegate IntPtr VirtualAllocDelegate(IntPtr lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
VirtualAllocDelegate d = Marshall.GetDelegateForFunctionPointer(ptr, typeof(VirtualAllocDelegate))
d(IntPtr.Zero, 200, 1, 0)
这是把 API 映射为 .net 的一个重要方法。
现在我要做的是拦截它,抓取调用。我要提供一个万能函数,然后所有调用都先回调到这个函数:
class Hook{
void Invoke(params object[] args){...}
}
我需要实现两个环节:
- 把这个函数适配到任意的 Type t,把参数发到 params object[] args
- 要先调用我这个钩子函数
下面是实现:
var h = Marshal.GetDelegateForFunctionPointer(entrance, delegateType); // 指针对应的 delegate
// 构造一个头和 delegateType 对齐的 lambda expression
var invoke = delegateType.GetMethod("Invoke");
var parameters = invoke.GetParameters().Select(p => ParameterExpression.Variable(p.ParameterType)).ToArray();
var args = Expression.NewArrayInit(typeof(object),
parameters.Select(p => Expression.TypeAs(p, typeof(object))).ToArray());
MethodCallExpression callExpression = Expression.Call(Expression.Constant(new Hook()), "Invoke", null, args); // 参数转发到 new Hook().Invoke
LambdaExpression lambdaExpression;
if (invoke.ReturnType == typeof(void))
{
lambdaExpression = Expression.Lambda(t, callExpression, parameters); // t 是 void 类型,lambda 无需返回
}
else
{
var block = Expression.Block(callExpression, Expression.Default(invoke.ReturnType)); // 否则返回默认值,注意这里只要加一个 Expression.Default(..) 就行,不需要 Expression.Return()...
lambdaExpression = Expression.Lambda(t, block, parameters);
}
dynamic h2 = Delegate.Combine(lambdaExpression.Compile(), h); // Combine 在一起,先调用我再调用原来的指针,目标达到!
至于怎么替掉原来的 Marshall.GetDelegateForFunctionPointer 就不展开了。
总体来说,.net 的 delegate 对应的级别很低,它是内存函数指针的类型化,其 DynamicInvoke Invoke 等方法既不暴露也不能覆盖,是一个魔法原子。相比来说 Java 面向对象的更彻底。
class Hook {
void invoke(object... args)
}
class VirtualAllocDelegate {
IntPrt invoke(IntPtr a, IntPtr b, IntPtr c)
}
// 这里也要用到 Java 的动态技术,生成一个临时类 MyDel extends VirtualAllocDelegate
dynamic class MyDel extends VirtualAllocDelegate{
MyDel(VirtualAllocDelegate inner, Hook h){
}
override IntPtr invoke(IntPtr a, IntPtr b, IntPtr c){
hook.invoke(a,b,c);
return inner.invoke(a,b,c);
}
}
var d = new MyDel(new VirtualAllocDelegate(offset), new Hook());
d.invoke(...)
Java 里 invoke 会是一个符合 OO 思想的 override,而不是 Delegate.Combine 这种更 c-style 还带点 fp 的玩意儿。
和 java 相比 c# 的面向对象程度不够彻底,有时像是 C 和 FP 结合的怪胎,一个典型的特点是 c# 从一开始就没有匿名内部类。
标签:IntPtr,invoke,适配,delegate,VirtualAllocDelegate,net,Expression,UInt32 来源: https://www.cnblogs.com/inshua/p/16262780.html