运用Mono.Cecil 反射读取.NET程序集元数据
CLR自带的反射机智和API可以很轻松的读取.NET程序集信息,但是不能对程序集进行修改。CLR提供的是只读的API,但是开源项目Mono.Cecil不仅仅可以读取.NET程序集的元数据,还可以进行修改。
1 读取程序集的版本信息
CLR定义的对象模型是以程序集为编译和部署单元。一个程序集下面可以有多个模块,模块下面再包含类型,类型中定义方法,属性和事件等。直接传入一个完整的字符串表示的程序集,MONO可以解析它的基本信息:
var name = AssemblyNameReference.Parse ("Foo, version=2.0.0.0, culture=fr-FR");
Assert.AreEqual ("Foo", name.Name);
Assert.AreEqual (2, name.Version.Major);
Assert.AreEqual (0, name.Version.Minor);
Assert.AreEqual ("fr-FR", name.Culture);
你可以用.NET Reflector载入程序集,拷贝红色方框所表示的内容,它是一个程序集的完整名称

2 创建程序集
BLC自带的API不能新建一个程序集文件,必须用源代码,借助编译器来生成.NET平台的MSIL程序集文件。请看下面的代码
var module = ModuleDefinition.CreateModule ("Test.dll", ModuleKind.Dll);
Assert.AreEqual ("Test", module.Assembly.Name.Name);
module = ModuleDefinition.CreateModule ("Test.exe", ModuleKind.Console);
Assert.AreEqual ("Test", module.Assembly.Name.Name);
module.Write (file);
一句代码创建程序集,传入需要的属性,另一句代码保存创建的程序集对象。API背后完成了复杂的功能,把这二行代码生成的程序集用.NET Reflector载入,可以正常打开,仅仅是没有类型和方法定义。
3 读取Attribute特性
请先看下面的代码,比BCL自带的反射读取特性的代码要省略很多,几乎就是要什么,直接一个属性就可以获取到
ModuleDefinition module=......
var hamster = module.GetType ("Hamster");
Assert.IsTrue (hamster.HasCustomAttributes);
Assert.AreEqual (1, hamster.CustomAttributes.Count);
var argument = attribute.ConstructorArguments [0];
4 读取类型成员的元数据
依然保持简洁的API,所需要的值都可以直接从属性中获取,下面的代码演示读取方法的元数据:
ModuleDefinition module=......
var type = module.Types [1];
Assert.AreEqual ("Foo", type.Name);
Assert.AreEqual (2, type.Methods.Count);
var method = type.GetMethod ("Bar");
Assert.AreEqual ("Bar", method.Name);
Assert.IsTrue (method.IsAbstract);
Assert.IsNotNull (method.ReturnType);
Assert.AreEqual (1, method.Parameters.Count);
var parameter = method.Parameters [0];
Assert.AreEqual ("a", parameter.Name);
Assert.AreEqual ("System.Int32", parameter.ParameterType.FullName);
下面的代码演示读取字段的元数据:
var type = module.Types [1];
Assert.AreEqual ("Foo", type.Name);
Assert.AreEqual (1, type.Fields.Count); var field = type.Fields [0];
Assert.AreEqual ("bar", field.Name);
Assert.AreEqual (1, field.MetadataToken.RID);
Assert.IsNotNull (field.FieldType);
Assert.AreEqual ("Bar", field.FieldType.FullName);
Assert.AreEqual (TokenType.Field, field.MetadataToken.TokenType);
Assert.IsFalse (field.HasConstant);
Assert.IsNull (field.Constant);
5 创建方法定义,对现有的类型或方法进行代码注入或修改的起点
显然,这个用途是Mono.Cecil流行起来的主要原因之一,可以进行代码注入(Code Injection):
先来看,如何创建一个方法体
var object_ref = new TypeReference ("System", "Object", null, null, false);
var method = new MethodDefinition ("foo", MethodAttributes.Static, object_ref);
var body = new MethodBody (method);
var il = body.GetILProcessor ();
var first = il.Create (OpCodes.Nop);
var second = il.Create (OpCodes.Nop);
var third = il.Create (OpCodes.Nop);
body.Instructions.Add (first);
body.Instructions.Add (third);
body.Instructions.Insert (1, second);
把这段代码翻译成C#语言的方法定义,它应该是这样的:
static object foo ()
{ }
三个空指令,在C#中相当于空语句,对应的MSIL语句是NOP指令。
把这里延伸一下,我获取到现有类型的方法引用,调用方法把它的MSIL指令全部清空,再插入我需要的指令,做到代码注入:
body.Instructions.Clear();
body.Instructions.Add (myInstruction);
//or insert
body.Instructions.Insert (2, myInstruction);
在我的前一篇文章中,讲解如何应用这个技巧,做一个字符串混淆程序的模型,参考下面的地址
字符串混淆技术应用 设计一个字符串混淆程序 可混淆.NET程序集中的字符串
6 类型推断
我们在写C#程序时,当使用usiing指令后,可以不用全名称来表示一个类型,比如下面的代码
Console.WriteLine()
或是
System.Console.WriteLine()
从.NET Reflector中加载程序集,MSIL代码中看到的类型或是方法调用,都是以全名称表示的。编译器在编译时,自动帮我们将命名空间和类型连接到一起,编译成MSIL代码中。
所以,参考下面的代码,
Assert.AreEqual ("System.String System.String::Empty", string_empty.FullName);
var definition = string_empty.Resolve ();
Assert.AreEqual ("System.String System.String::Empty", definition.FullName);
var definition = string_length.Resolve ();
Assert.AreEqual ("get_Length", definition.Name);
Assert.AreEqual ("System.String", definition.DeclaringType.FullName); var definition = list_add.Resolve ();
Assert.AreEqual ("System.Void System.Collections.Generic.List`1::Add(T)", definition.FullName); var definition = try_get_value.Resolve ();
Assert.AreEqual ("System.Boolean System.Collections.Generic.Dictionary`2::TryGetValue(TKey,TValue&)", definition.FullName);
当有返型参数时,它的名字看起来有点怪怪的。旧的版本的.NET Reflector在反编译成BCL类型时,没有处理到这一点,导致反编译后的源代码不能编译,新版本已经解决这个问题。.NET Reflector自从商业化之后,功能精进不休,配合.NET平台的进步,实为.NET开发中的必备工具之一。
.NET Reflector 8.1.0.35 : .NET Reflector
运用Mono.Cecil 反射读取.NET程序集元数据的更多相关文章
- 巧用Mono.Cecil反射加载类型和方法信息
最近在做服务的细粒度治理,统一管理所有服务的方法.参数.返回值信息.方便后续的各个模块之间的对接和协作. 目前系统中所有的服务,管理到接口契约粒度,即服务接口声明和服务接口实现.要做服务的细粒度治理: ...
- Mono.Cecil
Mono Cecil十分强大,强大到可以静态注入程序集(注入后生成新的程序集)和动态注入程序集(注入后不改变目标程序集,只在运行时改变程序集行为),它甚至可以用来调试PDB MDB调试符号格式文件. ...
- 基于Mono.Cecil的静态注入
Aop注入有2种方式:动态注入和静态注入,其中动态注入有很多实现了 动态注入有几种方式: 利用Remoting的ContextBoundObject或MarshalByRefObject. 动态代理( ...
- 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)
原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...
- C# Asp.net中的AOP框架 Microsoft.CCI, Mono.Cecil, Typemock Open-AOP API, PostSharp -摘自网络 (可以利用反射 Attribute 进行面向切面编程 可以用在记录整个方法的Log方面)
Both Microsoft.CCI and Mono.Cecil are low-level, and don't validate produced assemblies. It takes lo ...
- 教你怎么用Mono Cecil - 动态注入 (注意代码的注释)
原文 教你怎么用Mono Cecil - 动态注入 (注意代码的注释) 使用 Mono Cecil 进行反编译:using Mono.Cecil; using Mono.Cecil.Cil; //.. ...
- 使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试
Unity3D 引擎在 UnityEngine 名字空间下,提供了 Profiler 类(Unity 5.6 开始似乎改变了这个名字空间),用于辅助对项目性能进行测试.以 Android 平台为例 ...
- Mono.Cecil 初探(一):实现AOP
序言 本篇文章介绍基于Mono.Cecil实现静态AOP的两种方式:无交互AOP和交互式AOP. 概念介绍 Mono.Cecil:一个可加载并浏览现有程序集并进行动态修改并保存的.NET框架. AOP ...
- 运用Mono.Ceci类库修改.NET程序集 走上破解软件的道路
代码注入在C++时代很流行,主要是对现有的程序做一些修改,以达到预期的目的.一部分的破解程序,注册机也是借助于此方法,让被注入的程序绕过验证,达到破解的目录.在.NET中,借助于Mono.Cecil程 ...
随机推荐
- 探究javascript对象和数组的异同,及函数变量缓存技巧
javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...
- Java 征途:行者的地图
前段时间应因缘梳理了下自己的 Java 知识体系, 成文一篇望能帮到即将走进或正在 Java 世界跋涉的程序员们. 第一张,基础图 大约在 2003 年我开始知道 Java 的(当时还在用 Delph ...
- Java 8五大主要功能为开发者提供了哪些便利?
两年前当Java 8发布后,立即受到了业界的欢迎,因为它大大提高了Java的性能.它独特的卖点是,顾及了编程语言的每一个方面,包括JVM(Java虚拟机)和编译器,并且改良了其它帮助系统. Java是 ...
- 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)
相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...
- Unity 序列化 总结
查找了 Script Serialization http://docs.unity3d.com/Manual/script-Serialization.html 自定义序列化及例子: http:// ...
- angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable
大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...
- Go web开发初探
2017年的第一篇博客,也是第一次写博客,写的不好,请各位见谅. 本人之前一直学习java.java web,最近开始学习Go语言,所以也想了解一下Go语言中web的开发方式以及运行机制. 在< ...
- 现代3D图形编程学习-基础简介(3)-什么是opengl (译)
本书系列 现代3D图形编程学习 OpenGL是什么 在我们编写openGL程序之前,我们首先需要知道什么是OpenGL. 将OpenGL作为一个API OpenGL 通常被认为是应用程序接口(API) ...
- C#泛型详解(转)
初步理解泛型: http://www.cnblogs.com/wilber2013/p/4291435.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber ...
- Lind.DDD.LindMQ的一些想法
回到目录 很久就想写一套属于自己的消息队列组件,前段时候看了汤雪华同学的EQueue,感觉还是不错的,他也是看了rabbitMQ之后写的Equeue,在设计上与前者有类似的地方,而大叔这次准备写一个L ...