Mono.Cecil - 0.6

原文:Mono.Cecil - 0.6
项目地址:Mono.Cecil
项目描述:In simple English, with Cecil, you can load existing managed assemblies, browse all the contained types, modify them on the fly and save back to the disk the modified assembly.
类似项目:Microsoft CCI
Common Compiler Infrastructure: Metadata API
Common Compiler Infrastructure: Code Model and AST API
Common Compiler Infrastructure: Sample applications
Common Compiler Infrastructure - Contrib
对比评价:来自StackOverflow
Mono.Cecil has better, more understandable and easy in use object model. However, I had an ugly bug when used it in my program (reference to the wrong method was saved in the assembly; I think there was some bug with metadata tokens handling)
Microsoft.CCI has an ugly, utterly over-designed object model in the same time lacking many simple features; however, it's more mature than Mono.Cecil. Finally, I abandoned Mono.Cecil and used Microsoft.CCI for my program.

基本示例
Cecil是对已编译生成IL的程序集进行操作,所以先写一个简单的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方法添加一个SerializableAttribute
CustomAttribute 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.s
Instruction jmp = sayHello.Body.Instructions[1];
....
//把跳转的目标地址改成IL_0017 ldstr指令位置
jmp.Operand = ldstr;
时间: 2024-10-25 22:00:36

Mono.Cecil - 0.6的相关文章

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

   紧接上两篇浅谈.NET编译时注入(C#-->IL)和浅谈VS编译自定义编译任务-MSBuild Task(csproject),在第一篇中我们简单研究了c#语法糖和PostSharp的MSIl注入,紧接第二篇中我们介绍了自定义MSBuild编译任务(记得有位老兄发链接用 MSBuild自动发布Silverlight xap ,我想说的我做的是自定义编译任务,不是什么发布,MSBuild本就是一个发布工具).之所以在此前介绍编译Task是因为我讲介绍的就是利用MSBuild和MSILInje

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

原文:利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)      Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还可以修改已有的程序集,并且它支持多个运行时框架上例如:.net2.0/3.5/4.0,以及silverlight程序 官方地址:http://www.mono-project.com/Cecil      首先,我先假想有一个这样的商业组件,该组件满足了以下条件: 1. 该程序集的代码被混

请教mono.cecil 对系统dll做代码插入后无效的问题

问题描述 大家好:最近在使用mono.cecil做代码注入,在System.Data.dll中的SqlConnection类的Open方法最前面插入Console,WriteLine("runOpen");但是启动测试代码去运行Open方法是并没有在控制台显示"runOpen",又用mono.cecil对Open方法做改动,都没效果.AssemblyDefinitionasm=AssemblyDefinition.ReadAssembly("F:\Syst

教你怎么用Mono Cecil - 动态注入 (注意代码的注释)

原文 教你怎么用Mono Cecil - 动态注入 (注意代码的注释) 使用 Mono Cecil 进行反编译:using Mono.Cecil; using Mono.Cecil.Cil; //...... AssemblyDefinition asm = AssemblyFactory.GetAssembly("MyLibrary.dll"); foreach (TypeDefinition type in asm.MainModule.Types) { if (type.Name

C# 使用Mono Cecil 修改程序集,添加一个字段和属性,方法,怎样做实现

问题描述 在C#中,我想使用MonoCecil修改程序集,就是打开一个程序集,然后在里面添加一个字段和属性,方法,并且给字段赋一个初始值,然后保存程序集,怎样做实现,给一下代码,谢谢 解决方案 解决方案二:快,谁给个答案,给分啊解决方案三:给个恢复啊,,,,解决方案四:回复一下啊,,,,,,,,,,,,

使用Cecil修改.Net程序集

Cecil 是 Mono 的一个子项目,用于对程序集进行读写,并且已经用于 Mono 的调试,Reflector 也使用它作为底层库.最近把 DbEntry 使用 Emit 生成程序集的方式,改成了使用 Cecil 的方式,就我的感受来说,Cecil 是比较优秀的,有一些地方,比 Emit 使用起来还舒服的多:不过,有一些地方也比较繁琐. 我使用的是 Git 里的最新版本,如果大家要测试的话,也建议使用 Git 版,所以,需要安装一个 Git 客户端. 这里,用一个非常简单的例子,说明一下 Ce

Mono发布了1.1.13

Mono发布了1.1.13关于更新的部分可以看这里http://go-mono.com/archive/1.1.13/ 1.1.13下载地址http://www.go-mono.com/archive/1.1.13/windows-installer/0/mono-1.1.13-gtksharp-2.4.0-win32-0.exe 但是当我下完安装配置完系统Path之后准备使用Mcs编译一个简单的测试程序的时候错误就来了如图:   不是吧,于是仔细检查了一下发现bin目录下的Mcs.bat这样写

艾伟_转载:在Mono中创建ASP.NET程序

一只可爱的猴子: 一次偶然的机会碰到这只猴子,并在工作中也用到它了.现将自己的使用经验分享与此(以OpenSUSE为例介绍). 对于不熟悉Mono的朋友来说,Mono就是.Net在Linux和其它操作系统上的实现(Unix.Mac.iphone.Windows...).Mono的官方网站是:http://www.mono-project.com 严格来讲,Mono是一个开源的.跨平台的C#语言和其CLR的实现,并与微软的.NET二进制兼容.很绕口吧,这是Mono官方网站写的:An open so

LJMM平台( Linux +Jexus+MySQL+mono) 上使用MySQL的简单总结

近准备把PDF.NET框架的开源项目"超市管理系统"移植到Linux上跑(演示地址:http://221.123.142.196),使用Jexus服务器和MySQL数据库,相对使用SQLite而言,用MySQL问题比较多,但最后还是一一解决了,先总结如下: 1,MySQL驱动: 有人说在mono 下跑MySQL需要老点的MySQL驱动,我实验发现跟此无关,我用的驱动 MySQL.Data.dll 版本是 6.3.6,在mono 3.0.3 下跑是没有问题的. 2,MySQL服务的版本: