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程序集元数据相关推荐

  1. 巧用Mono.Cecil反射加载类型和方法信息

    最近在做服务的细粒度治理,统一管理所有服务的方法.参数.返回值信息.方便后续的各个模块之间的对接和协作. 目前系统中所有的服务,管理到接口契约粒度,即服务接口声明和服务接口实现.要做服务的细粒度治理: ...

  2. 使用Mono.Cecil辅助ASP.NET MVC使用dynamic类型Model

    使用Mono.Cecil辅助ASP.NET MVC使用dynamic类型Model 2011-09-06 00:21 by 老赵, 8645 visits 这也是之前在珠三角技术沙龙上的示例之一,解决 ...

  3. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文:利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  4. Mono.Cecil使用示例之使指定程序集成为UnityEditor.dll的友元程序集

    Mono.Cecil使用示例之使指定程序集成为UnityEditor.dll的友元程序集 Mono.Cecil是一个开源的库,使用Mono.Cecil可以非常方便的在代码中修改C#程序集.在Unity ...

  5. 编译时MSIL注入--实践Mono Cecil(1)

    紧接上两篇浅谈.NET编译时注入(C#-->IL)和浅谈VS编译自定义编译任务-MSBuild Task(csproject),在第一篇中我们简单研究了c#语法糖和PostSharp的MSIl注 ...

  6. 关于反射Assembly.Load(程序集).CreateInstance(命名空间.类)

    关于反射Assembly.Load("程序集").CreateInstance("命名空间.类") 而不管在哪一层写这段代码其中的("程序集" ...

  7. java map 泛型 反射_Java通过反射读取泛型

    packagecom.waibizi;importjava.lang.reflect.Method;importjava.lang.reflect.ParameterizedType;importja ...

  8. java程序中的图片与数值关联_Java从图片中读取图片的元数据Exif信息

    一般情况下是java程序读取不到gps等扩展信息的.如果想要解析到里面的信息需要下载一个jar包,metadata-extractor-2.6.4.jar(下载地址: http://code.goog ...

  9. 使用com.sun.imageio.plugins.png.PNGMetadata读取图片的元数据

    所谓图片元数据,就是除了我们肉眼看到的图片内容外,隐藏在这些内容背后的一些技术数据. 本文介绍如何使用Java代码将一张图片的隐藏信息读取出来. 首先不需要下载任何额外的Java库,用JDK自带的库就 ...

最新文章

  1. Emptiness 空值语义
  2. Docker 完全指南
  3. Java手机游戏新流星蝴蝶剑,手机游戏平台java游戏经典的五款游戏回顾-经典游戏...
  4. Dubbo线程模型和调度策略
  5. t3 修改服务器配置,t3如何修改服务器地址
  6. 重温6 ListView相关|单位dp/sp
  7. 2.用Python套用Excel模板,一键完成原亮样式
  8. 前端学习(505):垂直居中的第一种方式的优点和缺点
  9. 项目中获取系统的用例的基本步骤
  10. 软考高级网络规划设计师5天修炼
  11. PYPL 6 月编程语言排行
  12. MUI框架开发HTML5手机APP(一)--搭建第一个手机APP
  13. [转载] 详解 Numpy.ndarray
  14. WCF技术剖析之九:服务代理不能得到及时关闭会有什么后果?
  15. (四)数据建模和数据库设计
  16. SD卡无法识别怎么办?
  17. js如何获取当天开始时间和结束时时间并传递(时间戳)给后端
  18. Java实现doc转docx
  19. 叔本华的《人生的智慧》感悟
  20. Linux设置支持中文

热门文章

  1. java实例属性_Java 静态属性与实例属性的初始化
  2. 环境变量的配置导致无法登录
  3. 搭建Vue2.0脚手架(vue-cli)
  4. C#对象序列化与反序列化zz
  5. Lnmp架构之PHP
  6. 2013-开始新的一年
  7. Visual Entity 手册(一)简介
  8. 局域网内通过代理服务器上网的详细设置
  9. redis的IM的聊天工具
  10. Initializer Lists