ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

.net delegate 万能适配

2022-05-12 16:04:25  阅读:172  来源: 互联网

标签:IntPtr invoke 适配 delegate VirtualAllocDelegate net Expression UInt32


遇到一个技术点,记一下,.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){...}
}

我需要实现两个环节:

  1. 把这个函数适配到任意的 Type t,把参数发到 params object[] args
  2. 要先调用我这个钩子函数

下面是实现:

            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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有