使用Mono.Cecil对MSIL进行注入

本文介绍如何使用MonoCecil进行动态注入,通过修改程序集中的代码指令,在不改变原有程序集的情况下,实现在指定方法执行前插入自定义代码。文章提供了具体的C#示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mono Cecil十分强大,强大到可以静态注入程序集(注入后生成新的程序集)和动态注入程序集(注入后不改变目标程序集,只在运行时改变程序集行为)

先看Mono.Cecil是如何读取程序集的

点这里


一个基本使用示例(此示例版本为0.6,后期版本AssemblyFactory已被去掉,改用AssemblyDefinition.ReadAssembly)

点这里


下面是网上的一个例子,我觉得比较典型,拿来收藏了


     有一个名为 MyLibrary.dll 的程序集,内有 Class1 类型,我们需要动态创建其对象,并调用 Test 方法。注入的要求是在执行 Test 内部代码前先执行一个外部程序集的方法 InjectMethod。

[csharp]  view plain  copy
 print ?
  1. class Class1  
  2. {  
  3.    void Test()  
  4.   {  
  5.     Console.WriteLine("Hello, World!");  
  6.   }  
  7. }  

注入代码

[csharp]  view plain  copy
 print ?
  1. public class Program  
  2. {  
  3.   public static void InjectMethod()  
  4.   {  
  5.     Console.WriteLine("Haha...");  
  6.   }  
  7.   static void Main(string[] args)  
  8.   {  
  9.     AssemblyDefinition asm = AssemblyFactory.GetAssembly("MyLibrary.dll");  
  10.     foreach (TypeDefinition type in asm.MainModule.Types)  
  11.     {  
  12.       if (type.Name == "Class1")  
  13.       {  
  14.         foreach (MethodDefinition method in type.Methods)  
  15.         {  
  16.           if (method.Name == "Test")  
  17.           {  
  18.             Instruction ins = method.Body.Instructions[0];  
  19.             CilWorker worker = method.Body.CilWorker;  
  20.             MethodInfo m = typeof(Program).GetMethod("InjectMethod", BindingFlags.Static | BindingFlags.Public);  
  21.             MethodReference refernce = asm.MainModule.Import(m);  
  22.             Instruction insCall = worker.Create(OpCodes.Call, refernce);  
  23.             worker.InsertBefore(ins, insCall);  
  24.             Instruction insNop = worker.Create(OpCodes.Nop);  
  25.             worker.InsertAfter(insCall, insNop);  
  26.           }  
  27.         }  
  28.       }  
  29.     }  
  30.     Assembly testAssembly = AssemblyFactory.CreateReflectionAssembly(asm);   
  31.     Type class1Type = testAssembly.GetType("MyLibrary.Class1");  
  32.     Object o = Activator.CreateInstance(class1Type);  
  33.     class1Type.InvokeMember("Test", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, o, null);  
  34.   }  
  35. }  

输出:
Haha...
Hello, World!
  动态注入和静态注入不同,它不会修改目标程序集文件,因此不会有强类型签名等问题,也不易发生相关 "版权" 问题。可用来跳过某段验证代码,或者替换某些内部执行代码,用途有点像 "外挂"。使用动态注入实现 AOP 可能比较有意思,我们可以遍历添加了 "[AOP(aopMethodName=)]" 方法特性的所有类型,然后动态注入 AOP 拦截代码。


以上这种注入方式十分强大,居然可以注入到某一行代码之前,改变它的行为,一般的AOP实现恐怕也没有这么强大的。


另一个例子是直接把泛型约束给修改掉,例子中由于编译器不支持泛型约束中出现Enum类型,但在IL底层支持这么干,作者就用Mono.Ceil修改了程序集,让泛型约束直接支持Enum类型,详细看这里

点这里


Mono.Cecil 还可以用来破解商业组件(我勒个去的,连程序都可以改,去掉程序集签名,改成自己生成的签名,还有什么办不到的?)

下面的例子是去掉一个组件中的水印,当然作者只不过是举个例子,组件是自己写的,“破解”起来比较容易,真正的商业组件破解起来,得花好多时间研究的

点这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值