.NET反编译器区分"使用"并且"尝试...终于"

时间:2018-04-13 11:11:23

标签: c# .net cil decompiler

给出以下C#代码,其中以两种不同的方式调用 Dispose 方法:

class Disposable : IDisposable
{
    public void Dispose()
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var disposable1 = new Disposable())
        {
            Console.WriteLine("using");
        }

        var disposable2 = new Disposable();
        try
        {
            Console.WriteLine("try");
        }
        finally
        {
            if (disposable2 != null)
                ((IDisposable)disposable2).Dispose();
        }
    }
}

使用发布配置编译然后用ildasm反汇编,MSIL看起来像这样:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       57 (0x39)
  .maxstack  1
  .locals init ([0] class ConsoleApplication9.Disposable disposable2,
           [1] class ConsoleApplication9.Disposable disposable1)
  IL_0000:  newobj     instance void ConsoleApplication9.Disposable::.ctor()
  IL_0005:  stloc.1
  .try
  {
    IL_0006:  ldstr      "using"
    IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0010:  leave.s    IL_001c
  }  // end .try
  finally
  {
    IL_0012:  ldloc.1
    IL_0013:  brfalse.s  IL_001b
    IL_0015:  ldloc.1
    IL_0016:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_001b:  endfinally
  }  // end handler
  IL_001c:  newobj     instance void ConsoleApplication9.Disposable::.ctor()
  IL_0021:  stloc.0
  .try
  {
    IL_0022:  ldstr      "try"
    IL_0027:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_002c:  leave.s    IL_0038
  }  // end .try
  finally
  {
    IL_002e:  ldloc.0
    IL_002f:  brfalse.s  IL_0037
    IL_0031:  ldloc.0
    IL_0032:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0037:  endfinally
  }  // end handler
  IL_0038:  ret
} // end of method Program::Main

DotPeek JustDecompile 等.NET反编译器如何区分使用尝试... finally < / em>的

2 个答案:

答案 0 :(得分:8)

实际上并没有什么不同。正如Mark在评论中所说 - 如果你编写的代码与编译器为using生成的代码相同 - 反编译器将无法发挥作用。

然而,许多反编译器,包括DotPeek,实际上可以使用调试符号(.pdb)文件来定位实际的源代码,然后使用实际的源代码,这样根本不会发生反编译。此外,在调试模式下进行编译也可能会影响模式(也就是说 - 模拟using语句的尝试可能会在调试与发布编译中产生不同的结果IL。)

为防止DotPeek使用您的真实源代码文件,请转到工具&gt;选项&gt;反编译并取消选中“在可用时使用符号文件中的源”。然后在Release中编译代码并观察DotPeek将这两个语句反编译为using

答案 1 :(得分:4)

  

像DotPeek或JustDecompile这样的.NET反编译器如何区分使用和尝试...最终?

反编译器主要处理模式匹配。通常,IL被翻译成目标语言中可能的最简单的等效表示(在这种情况下,C#)。然后,该代码模型通过一系列转换,尝试将代码序列与众所周知的模式进行匹配。使用ILSpy的调试版本,您实际上可以在此管道的不同阶段查看输出。

反编译器的管道可能包括类似循环重写器的转换。循环重写器可以通过查找前面有变量初始值并且在每个后边缘之前还包含公共迭代语句的for循环来重构while循环。当检测到这样的循环时,它将被重写为更简洁的for循环。它不知道原始代码实际上包含for循环;它只是试图找到最简洁的方式来代表代码,同时保持正确性。

以类似的方式,using重写器将查找try/finally块,其中finally包含简单的空检查和Dispose()调用,然后将其重写为{{} 1}}块,根据语言规范更简洁,同时仍然是正确的。反编译器不会知道代码包含using块,但由于几乎没有人使用显式using形式,因此结果往往与原始来源。