Mono Cecil修改程序集

Mono Cecil : http://www.mono-project.com/Cecil
Fun with IronPython and Cecil : http://weblogs.asp.net/nleghari/pages/ironpythoncecil.aspx


1. 反编译

获取程序集方法的 IL 代码。

代码原形:
public class Class1
{
  public void Test()
  {
    Console.WriteLine("Hello, World!");
  }
}

Lutz Roeder's Reflector 反编译结果:
.method public hidebysig instance void Test() cil managed
{
  .maxstack 8
  L_0000: nop
  L_0001: ldstr "Hello, World!"
  L_0006: call void [mscorlib]System.Console::WriteLine(string)
  L_000b: nop
  L_000c: ret
}

使用 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 == "Class1")
  {
    foreach (MethodDefinition method in type.Methods)
    {
      Console.WriteLine(".maxstack {0}", method.Body.MaxStack);
      foreach (Instruction ins in method.Body.Instructions)
      {
        Console.WriteLine("L_{0}: {1} {2}", ins.Offset.ToString("x4"),
          ins.OpCode.Name,
          ins.Operand is String ? String.Format("\"{0}\"", ins.Operand) : ins.Operand);
      }
    }
  }
}

输出:
.maxstack 8
L_0000: nop
L_0001: ldstr "Hello, World!"
L_0006: call System.Void System.Console::WriteLine(System.String)
L_000b: nop
L_000c: ret

呵呵~~~~ 效果不错!

2. 修改 IL 代码

继续以上面的代码为例,我们的目标是将 "Hello, World!" 改成 "Hello, C#!"。当然,更复杂也可以,只不过需要你自己花时间了。
AssemblyDefinition asm = AssemblyFactory.GetAssembly("MyLibrary.dll");
foreach (TypeDefinition type in asm.MainModule.Types)
{
  if (type.Name == "Class1")
  {
    foreach (MethodDefinition method in type.Methods)
    {
      if (method.Name == "Test")
      {
        foreach (Instruction ins in method.Body.Instructions)
        {
          if (ins.OpCode.Name == "ldstr" && (string)ins.Operand == "Hello, World!")
          {
            ins.Operand = "Hello, C#!";
          }
        }
      }
    }
  }
}

AssemblyFactory.SaveAssembly(asm, "Test.dll");

用 Lutz Roeder's Reflector 打开 Test.dll 看看结果。
.method public hidebysig instance void Test() cil managed
{
  .maxstack 8
  L_0000: nop
  L_0001: ldstr "Hello, C#!"
  L_0006: call void [mscorlib]System.Console::WriteLine(string)
  L_000b: nop
  L_000c: ret
}

达成所愿~~~~~

3. 代码注入

完成代码修改以后,我们玩一个更难点的,注入额外的代码。(好像有点做病毒的意思,呵呵~)

任务目标是在 Console.WriteLine("Hello, World!"); 前注入 Console.WriteLine("Virus? No!");,还好这不是真的破坏性代码。
AssemblyDefinition asm = AssemblyFactory.GetAssembly("MyLibrary.dll");
foreach (TypeDefinition type in asm.MainModule.Types)
{
  if (type.Name == "Class1")
  {
    foreach (MethodDefinition method in type.Methods)
    {
      if (method.Name == "Test")
      {
        foreach (Instruction ins in method.Body.Instructions)
        {
          if (ins.OpCode.Name == "ldstr" && (string)ins.Operand == "Hello, World!")
          {
            CilWorker worker = method.Body.CilWorker;
            
            Instruction insStr = worker.Create(OpCodes.Ldstr, "Virus? NO!");
            worker.InsertBefore(ins, insStr);

            MethodReference refernce = asm.MainModule.Import(typeof(Console).GetMethod("WriteLine",
              new Type[]{typeof(string)}));

            Instruction insCall = worker.Create(OpCodes.Call, refernce);
            worker.InsertAfter(insStr, insCall);

            Instruction insNop = worker.Create(OpCodes.Nop);
            worker.InsertAfter(insCall, insNop);

            break;
          }
        }
      }
    }
  }
}

继续用 Lutz Roeder's Reflector 看一下结果。

C#
public void Test()
{
  Console.WriteLine("Virus? NO!");
  Console.WriteLine("Hello, World!");
}

IL
.method public hidebysig instance void Test() cil managed
{
  .maxstack 8
  L_0000: nop
  L_0001: ldstr "Virus? NO!"
  L_0006: call void [mscorlib]System.Console::WriteLine(string)
  L_000b: nop
  L_000c: ldstr "Hello, World!"
  L_0011: call void [mscorlib]System.Console::WriteLine(string)
  L_0016: nop
  L_0017: ret
}

用反射测试一下修改后的程序集。
AssemblyFactory.SaveAssembly(asm, "Test.dll");

Assembly testAssembly = Assembly.LoadFrom("Test.dll");
Type class1Type = testAssembly.GetType("MyLibrary.Class1");
Object o = Activator.CreateInstance(class1Type);
class1Type.InvokeMember("Test", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, o, null);

输出:
Virus? No!
Hello, World!

一切OK! 注入更复杂的代码,甚至是为类型添加方法,或者直接用来做 AOP…… 好像还有很多事可做……

4. 获取程序集信息
AssemblyDefinition asm = AssemblyFactory.GetAssembly("Learn.Library.dll");

Console.WriteLine("Kind:{0}", asm.Kind);
Console.WriteLine("Runtime:{0}", asm.Runtime);

输出:
Kind:Dll
Runtime:NET_2_0

利用 ModuleDefinition.Image 属性,我们可以获取程序集几乎全部的细节信息。包括 CLIHeader、DebugHeader、DOSHeader、FileInformation、HintNameTable、ImportAddressTable、ImportLookupTable、ImportTable、MetadataRoot、PEFileHeader、PEOptionalHeader、ResourceDirectoryRoot、Sections、TextSection 等。

posted on 2008-01-21 12:19  有一说一  阅读(1433)  评论(0)    收藏  举报

导航