12

我正在使用 Mono Cecil 在另一种方法中注入代码。我想在我的代码周围添加一个 Try-Catch 块。

所以我写了一个带有try catch块的HelloWorld.exe并反编译了它。

Try-Catch 的 Reflector 中看起来像这样:

.try L_0001 to L_0036 catch [mscorlib]System.Exception handler L_0036 to L_003b

我怎样才能通过mono cecil注入这样的try catch?

4

1 回答 1

24

使用 Mono.Cecil 添加异常处理程序并不困难,它只需要您知道异常处理程序在元数据中的布局方式。

假设您有 C# 方法:

static void Throw ()
{
    throw new Exception ("oups");
}

如果你反编译它,它应该看起来有点像这样:

.method private static hidebysig default void Throw ()  cil managed 
{
    IL_0000:  ldstr "oups"
    IL_0005:  newobj instance void class [mscorlib]System.Exception::.ctor(string)
    IL_000a:  throw 
}

现在假设您想在此方法中注入代码,例如它类似于 C# 代码:

static void Throw ()
{
    try {
        throw new Exception ("oups");
    } catch (Exception e) {
        Console.WriteLine (e);
    }
}

也就是说,您只想将现有代码包装在 try catch 处理程序中。您可以通过 Cecil 轻松做到这一点:

    var method = ...;
    var il = method.Body.GetILProcessor ();

    var write = il.Create (
        OpCodes.Call,
        module.Import (typeof (Console).GetMethod ("WriteLine", new [] { typeof (object)})));
    var ret = il.Create (OpCodes.Ret);
    var leave = il.Create (OpCodes.Leave, ret);

    il.InsertAfter (
        method.Body.Instructions.Last (), 
        write);

    il.InsertAfter (write, leave);
    il.InsertAfter (leave, ret);

    var handler = new ExceptionHandler (ExceptionHandlerType.Catch) {
        TryStart = method.Body.Instructions.First (),
        TryEnd = write,
        HandlerStart = write,
        HandlerEnd = ret,
        CatchType = module.Import (typeof (Exception)),
    };

    method.Body.ExceptionHandlers.Add (handler);

这段代码正在操作之前的方法,看起来像这样:

.method private static hidebysig default void Throw ()  cil managed 
{
    .maxstack 1
    .try { // 0
      IL_0000:  ldstr "oups"
      IL_0005:  newobj instance void class [mscorlib]System.Exception::'.ctor'(string)
      IL_000a:  throw 
    } // end .try 0
    catch class [mscorlib]System.Exception { // 0
      IL_000b:  call void class [mscorlib]System.Console::WriteLine(object)
      IL_0010:  leave IL_0015
    } // end handler 0
    IL_0015:  ret 
}

我们添加了三个新指令:一个对 Console.WriteLine 的调用,一个优雅地退出 catch 处理程序的离开,最后(双关语),一个 ret。然后我们简单地创建一个 ExceptionHandler 实例来表示一个 try catch 处理程序,它的 try 包含现有的主体,并且它的 catch 是 WriteLine 语句。

需要注意的重要一点是范围的结束指令不包含在范围内。它基本上是一个 [TryStart:TryEnd[ 范围。

于 2012-06-17T21:27:05.650 回答