遇到一个技术点,记一下,.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# 从一开始就没有匿名内部类。

.net delegate 万能适配的更多相关文章

  1. Android TV开发总结(五)TV上屏幕适配总结

    前言:前面几篇总结一些TV上的小Sample,开源到GitHub:https://github.com/hejunlin2013/TVSample, 点击链接,可以持续关注.今天总结下TV上屏幕适配. ...

  2. 一行代码搞定所有屏幕适配AbViewUtil

    适配原理:抛弃google提供的dip理论与多套图片与布局方案,采用与UI设计师通用的px作为标准单位,原理是将UI设计师的设计图与当前查看的手机或其他设备的屏幕像素尺寸进行换算,得到缩放比例,在Ac ...

  3. Android Andbase应用开发框架

    [运行说明]运行AndbaseDemo需要将文件中的Andbase库Add进demo中.1.andbase中包含了大量的开发常用手段.如网络下载,多线程与线程池的管理,数据库ORM,图片缓存管理,图片 ...

  4. android开源框架之 andbase

    andbase开发框架介绍:andbase是为Android开发人员量身打造的一款开源类库产品,您能够在本站中获取到最新的代码,演示样例以及开发文档. 下载地址:http://download.csd ...

  5. 一个漂亮而强大的RecyclerView

    代码地址如下:http://www.demodashi.com/demo/13470.html 简介 主要提供了简单易用强大的RecyclerView库,包括自定义刷新加载效果.极简通用的万能适配器A ...

  6. 9款Android经常使用的高速开发框架

    1.Afinal框架 项目地址:https://github.com/yangfuhai/afinal 项目地址:http://www.oschina.net/p/afinal 主要有四大模块:  ( ...

  7. Android 开源控件与常用开发框架开发工具类

    Android的加载动画AVLoadingIndicatorView 项目地址: https://github.com/81813780/AVLoadingIndicatorView 首先,在 bui ...

  8. iOS设计模式之适配器模式

    一,适配器的定义 定义 将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 需求场景 需要使用以前开发的“一些现存的对象”,但是新环境中要求 ...

  9. iOS---iOS10适配iOS当前所有系统的远程推送

    一.iOS推送通知简介 众所周知苹果的推送通知从iOS3开始出现, 每一年都会更新一些新的用法. 譬如iOS7出现的Silent remote notifications(远程静默推送), iOS8出 ...

  10. Android—万能ListView适配器

    ListView是开发中最常用的控件了,但是总是会写重复的代码,浪费时间又没有意义. 最近参考一些资料,发现一个万能ListView适配器,代码量少,节省时间,总结一下分享给大家. 首先有一个自定义的 ...

随机推荐

  1. 《Vue.js 设计与实现》读书笔记 - 第15章、编译器核心技术概览

    第15章.编译器核心技术概览 15.1 模板 DSL 的编译器 完整的编译包括 [源代码] -->词法分析-->语法分析-->语义分析(编译前端) -->中间代码生成--> ...

  2. uprobe

    本章的我们来学习uprobe ,顾名思义,相对于内核函数/地址的监控,主要用于用户态函数/地址的监控.听起来是不是有点神奇,内核怎么监控用户态函数的调用呢?本章的内容包括: 如何使用uprobe 内核 ...

  3. store文件夹的处理,vuex模块化modules

    index文件代码: import Vue from "vue"; import Vuex from "vuex"; // 导入根getters import ...

  4. 16 Transformer 的编码器(Encodes)——我在做更优秀的词向量

    博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...

  5. go frame资源管理打包失败

    最近有个需求,需要使用golang做一个小工具,然后我就想既然是小工具,那就把前后端放在一个二进制文件中.恰好使用的项目架构是go frame,它已经提供了这样的能力,但是没想到碰到了一鼻子灰... ...

  6. 牛客网C++选择题笔记

    这是牛客网上的C/C++的选择题的错题,随做随更,有一些题还是有点意思. 当然不是所有都有总结,有明显歧义的不管,过于简单的不管(简不简单因人而异,我先阅读了C++ Primer后再开始做的练习,因此 ...

  7. Go语言学习 _基础03 _数组和切片

    Go语言学习 _基础03 _数组和切片 1.数组 package array_test import "testing" func TestArrayInit(t *testing ...

  8. Canvas简历编辑器-层级渲染与事件管理能力设计

    Canvas简历编辑器-选中绘制与拖拽多选交互设计 在之前我们聊了聊如何基于Canvas与基本事件组合实现了轻量级DOM,并且在此基础上实现了如何进行管理事件以及多层级渲染的能力设计.那么此时我们就依 ...

  9. Hadoop未授权访问

    Hadoop未授权访问 是什么? Hadoop 是一种用来处理和存储大量数据的软件工具,可以用来日志分析,推荐系统,数据备份   核心组件: 存储大数据:HDFS 文件系统 处理大数据:MapRedu ...

  10. C++递归创建文件夹

    根据传入的参数递归进行目录的创建. 函数描述: 递归创建目录. 入参: 所要创建的目录. 返回值: 创建成功,返回TRUE:否则返回FALSE. 1 BOOL CreateDirTree(LPCTST ...