Mono.Cecil简介与示例
方法SayHello的IL代码如下:
修改后的方法SayHello的IL代码如下:
从上面的基本使用方法可以看出,Cecil的确是易于使用,对象模型结构非常实用,这里是官方网站的一个主要对象结构图:
调用tt2.SayHello(false);时,应该也会有一个>>Intercepted SayHello消息,但是没有输出,对比一下IL代码就清楚了:
修改后的IL代码如下:
IL_000b那一句,为false时就直接跳转到IL_0021这个返回指令上了,不会输出Intercepted的消息
使用Mono.Cecil也可以修改这个跳转地址,例如:
发布日期:2021-06-30 19:38:38
浏览次数:2
分类:技术文章
本文共 4041 字,大约阅读时间需要 13 分钟。
转载注明出处:
CLR自带的反射机智和API可以很轻松的读取.NET程序集信息,但是不能对程序集进行修改。CLR提供的是只读的API,但是开源项目Mono.Cecil不仅仅可以读取.NET程序集的元数据,还可以进行修改。
先写一个简单的Console exe程序,这里项目名称使用Cecil.Program:
using System;using System.Reflection;namespace Cecil.Program{ class Program { static void Main(string[] args) { TestType tt = new TestType(); tt.SayHello(); tt.AboutMe(); Console.ReadKey(); } } public class TestType { [Obsolete] public void SayHello() { Console.WriteLine("\tHello Cecil !"); } public void AboutMe() { Type type = typeof(TestType); MethodInfo method = type.GetMethod("SayHello"); if (method.IsVirtual) Console.WriteLine("\tI'm a virtual method"); else Console.WriteLine("\tI'm a non-virtual method"); object[] attributes = method.GetCustomAttributes(false); if (attributes != null && attributes.Length > 0) { Console.WriteLine("\tI have the following attributes:"); foreach (object attr in attributes) Console.WriteLine("\t\t" + attr.GetType().Name); } } }}这个程序集的运行结果如下:
方法SayHello的IL代码如下:
接下来使用另外一个Console exe程序来修改Cecil.Program.exe,项目名称使用Cecil:
using Mono.Cecil;using Mono.Cecil.Cil;AssemblyDefinition assembly = AssemblyFactory.GetAssembly("Cecil.Program.exe");TypeDefinition type = assembly.MainModule.Types["Cecil.Program.TestType"];MethodDefinition sayHello = null;foreach (MethodDefinition md in type.Methods) if (md.Name == "SayHello") sayHello = md;//Console.WriteLine(string value)方法MethodInfo writeLine = typeof(Console).GetMethod("WriteLine" , new Type[] { typeof(string) });//Console.WriteLine方法导入MainModule,并返回在AssemblyDefinition中的引用方式MethodReference writeLineRef = assembly.MainModule.Import(writeLine);//在SayHello方法开始位置插入一条trace语句// Console.WriteLine(">>Intercepting ");//如果插入的语句需要使用函数入参,则必须插入在OpCodes.Ldarg等指令之后CilWorker worker = sayHello.Body.CilWorker;Instruction ldstr = worker.Create(OpCodes.Ldstr, ">>Intercepting " + sayHello.Name);Instruction call = worker.Create(OpCodes.Call, writeLineRef);Instruction first = sayHello.Body.Instructions[0];worker.InsertBefore(first, call);worker.InsertBefore(call, ldstr);//在SayHello方法结束位置插入一条trace语句// Console.WriteLine(">>Intercepted ");//语句必须插入在OpCodes.Ret指令的前面int offset = sayHello.Body.Instructions.Count - 1;Instruction last = sayHello.Body.Instructions[offset--];while (last.OpCode == OpCodes.Nop || last.OpCode == OpCodes.Ret) last = sayHello.Body.Instructions[offset--];ldstr = worker.Create(OpCodes.Ldstr, ">>Intercepted " + sayHello.Name);worker.InsertAfter(last, ldstr);worker.InsertAfter(ldstr, call);//把SayHello方法改为虚方法sayHello.IsVirtual = true;//给SayHello方法添加一个SerializableAttributeCustomAttribute attribute = new CustomAttribute( assembly.MainModule.Import( typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes)));sayHello.CustomAttributes.Add(attribute);AssemblyFactory.SaveAssembly(assembly, "Cecil.Program.modified.exe");Console.WriteLine("Assembly modified successfully!");Console.ReadKey();编译生成Cecil.exe,然后把Cecil.Program.exe拷贝到这个目录下,运行Cecil.exe,便会在当前目录生成Cecil.Program.modified.exe,运行Cecil.Program.modified.exe结果如下:
修改后的方法SayHello的IL代码如下:
从上面的基本使用方法可以看出,Cecil的确是易于使用,对象模型结构非常实用,这里是官方网站的一个主要对象结构图:
IL指令的复杂性
在assembly、type、method级别上对程序集做修改是非常简单的,但是如果要修改方法体的IL代码,则可能会遇到一些较麻烦的事情,需要细致的处理
例如上面的SayHello方法如果是这样:
public void SayHello(bool print){ if (print) Console.WriteLine("\tHello Cecil !");}测试代码这样来调用:
TestType2 tt2 = new TestType2();tt2.SayHello(true);tt2.SayHello(false);Console.ReadKey();其运行结果只会输出一条Hello Cecil !消息,仍然使用Cecil.exe来修改这个程序集,其运行结果如下图:
调用tt2.SayHello(false);时,应该也会有一个>>Intercepted SayHello消息,但是没有输出,对比一下IL代码就清楚了:
修改后的IL代码如下:
IL_000b那一句,为false时就直接跳转到IL_0021这个返回指令上了,不会输出Intercepted的消息
使用Mono.Cecil也可以修改这个跳转地址,例如:
//得到指令brfalse.sInstruction jmp = sayHello.Body.Instructions[1];....//把跳转的目标地址改成IL_0017 ldstr指令位置jmp.Operand = ldstr;
转载地址:https://linxinfa.blog.csdn.net/article/details/51803200 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2024年04月30日 00时58分42秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
AOP面向切面编程【图文教程】_第1章
2019-04-30
AOP面向切面编程【图文教程】_第2章
2019-04-30
二叉树之前序、中序、后序和层次遍历【图文教程】
2019-04-30
java类的构成
2019-04-30
创建安装linux:centOS
2019-04-30
Xshell连接CentOS及安装hadoop的准备
2019-04-30
在linux上配置jdk和hadoop
2019-04-30
HDFS配置及常见命令
2019-04-30
xshell连接linux速度很慢或者连接一段时间后会自动断
2019-04-30
Hadoop Windows插件配置
2019-04-30
存储 HDFS内部运行原理
2019-04-30
二丶存储+分析处理信息MapReduce内部原理
2019-04-30
static代码块设置全局变量和eclipse java配好HDFS类对HDFS的操作
2019-04-30
互联网行业为何缺少web前端工程师?
2019-04-30
零基础学UI设计,海报设计需思考这些点!
2019-04-30
零基础该怎么学java,学习心得分享!
2019-04-30
互联网行业如此热门,如何脱颖而出成功入行!
2019-04-30
小白到高级UI设计师,如何实现完美蜕变?
2019-04-30
软件测试培训之自动化测试中要注意的要点
2019-04-30