为什么人们反汇编.NET(CLR)二进制文件?

时间:2009-05-30 19:48:34

标签: reflector il disassembly

我对.NET有些新手,但对编程并不陌生,我对有关反编译.NET代码的趋势和兴奋感到有些困惑。这似乎毫无意义。

.NET的高级易用性是我使用它的原因。我在资源有限的环境中编写了C和实际(硬件处理器)程序集。为了提高效率,这就是为了这么多细节而花费精力的原因。在.NET领域,如果你浪费时间深入了解实现中最神秘的细节,那就有可能失去拥有高级面向对象语言的目的。在使用.NET的过程中,我调试了通常的性能问题,这是一个奇怪的竞争条件,我通过阅读自己的源代码完成了所有这些,从来没有想过编译器生成什么中间语言。例如,考虑到foreach()将使用带有方法的枚举对象,for(;;)循环将比数组上的foreach()更快是显而易见的。呼吁前进到下一次,而不是简单地增加变量,这很容易通过几百万次紧密循环运行来证明(不需要拆卸)。

真正让IL愚蠢拆解的是它不是真正的机器代码。这是虚拟机代码。我听说有些人实际上喜欢移动指令来优化它。你在跟我开玩笑吗?即时编译的虚拟机代码甚至不能以本机编译代码的速度对(;;)循环进行简单的紧缩。如果你想从处理器中挤出最后一个循环,那么使用C / C ++并花时间学习实际的汇编。这样你花在理解大量低级细节上的时间实际上是值得的。

那么,除了手上有太多时间之外,为什么人们会拆解.NET(CLR)二进制文件呢?

13 个答案:

答案 0 :(得分:7)

了解各种高级语言的编译器实际上正在对您的源进行操作是一项重要的技能,可以在掌握特定环境时获取,就像理解数据库引擎如何计划执行各种类型的您可以对它们进行SQL查询。要以一种熟练的方式使用一定程度的抽象,熟悉(至少)低于它的水平是一件非常好的事情;见例如some notes on my talk on the subject of abstractionthe slides for that talk,以及我在谈话中提到的Joel Spolsky的“漏洞抽象法则”。

答案 1 :(得分:3)

我已经在源代码丢失或者特定标记版本中版本控制中的内容与发布的二进制文件不对应时使用过它。

答案 2 :(得分:2)

刚刚完成安全软件开发的4天课程后,我会说许多人会反编译源代码以查找其中的任何漏洞。了解客户端应用程序的来源有助于规划对服务器的攻击。<​​/ p>

当然,小实用程序等等,不会有任何此类问题。

如果我没记错的话,那里有一个应用程序会混淆你的.net二进制文件。我相信它被称为dotfuscator。

答案 3 :(得分:2)

了解如何使用记录不良的界面。

(遗憾的是,在基于.net的工具(例如BizTalk或WCF)中,只有通用生成的文档太频繁,所以有时需要反汇编到C#以查看方法正在做什么,在哪种情况下使用它)

答案 4 :(得分:1)

每种.NET语言都实现了自己的CLR功能子集。知道CLR能够处理您当前使用的语言不具备的功能,可以让您在是否更换语言或发布IL或找到其他方式时做出明智的决定。

你认为人们做这样事情的唯一原因是他们有太多时间侮辱和未受过教育。

答案 5 :(得分:1)

查找库错误并弄清楚如何解决它们。

例如:如果没有反射,您无法远程处理异常并重新抛出它而不会屠宰其回溯。但是框架可以做到。

答案 6 :(得分:1)

从您的问题看来,您似乎不知道Reflector将CLR程序集反汇编回C#或VB,因此您几乎可以看到原始代码,而不是IL!

答案 7 :(得分:1)

实际上,一个关于int []的foreach被编译成for语句。如果我们将它转​​换为可枚举,你是对的,它使用枚举器。 然而,奇怪的是,因为没有增加temp int,所以它变得更快。为了证明这一点,我们使用基准测试和反编译器来增加理解......

所以我想通过问这个问题,你真的自己回答了。

如果此基准与您的基准不同,请告诉我如何。我尝试使用对象数组,空值等等...

代码:

    static void Main(string[] args)
    {

        int[] ints = Enumerable.Repeat(1, 50000000).ToArray();

        while (true)
        {
            DateTime now = DateTime.Now;
            for (int i = 0; i < ints.Length; i++)
            {
                //nothing really
            }
            Console.WriteLine("for loop: " + (DateTime.Now - now));

            now = DateTime.Now;
            for (int i = 0; i < ints.Length; i++)
            {
                int nothing = ints[i];
            }
            Console.WriteLine("for loop with assignment: " + (DateTime.Now - now));

            now = DateTime.Now;
            foreach (int i in ints)
            {
                //nothing really
            }
            Console.WriteLine("foreach: " + (DateTime.Now - now));

            now = DateTime.Now;
            foreach (int i in (IEnumerable<int>)ints)
            {
                //nothing really
            }
            Console.WriteLine("foreach casted to IEnumerable<int>: " + (DateTime.Now - now));
        }

    }

结果:

for loop: 00:00:00.0273438
for loop with assignment: 00:00:00.0712890
foreach: 00:00:00.0693359
foreach casted to IEnumerable<int>: 00:00:00.6103516
for loop: 00:00:00.0273437
for loop with assignment: 00:00:00.0683594
foreach: 00:00:00.0703125
foreach casted to IEnumerable<int>: 00:00:00.6250000
for loop: 00:00:00.0273437
for loop with assignment: 00:00:00.0683594
foreach: 00:00:00.0683593
foreach casted to IEnumerable<int>: 00:00:00.6035157
for loop: 00:00:00.0283203
for loop with assignment: 00:00:00.0771484
foreach: 00:00:00.0771484
foreach casted to IEnumerable<int>: 00:00:00.6005859
for loop: 00:00:00.0273438
for loop with assignment: 00:00:00.0722656
foreach: 00:00:00.0712891
foreach casted to IEnumerable<int>: 00:00:00.6210938

反编译(注意空的foreach必须添加一个变量赋值...我们的空for循环没有但显然需要):

private static void Main(string[] args)
{
    int[] ints = Enumerable.Repeat<int>(1, 0x2faf080).ToArray<int>();
    while (true)
    {
        DateTime now = DateTime.Now;
        for (int i = 0; i < ints.Length; i++)
        {
        }
        Console.WriteLine("for loop: " + ((TimeSpan) (DateTime.Now - now)));
        now = DateTime.Now;
        for (int i = 0; i < ints.Length; i++)
        {
            int num1 = ints[i];
        }
        Console.WriteLine("for loop with assignment: " + ((TimeSpan) (DateTime.Now - now)));
        now = DateTime.Now;
        int[] CS$6$0000 = ints;
        for (int CS$7$0001 = 0; CS$7$0001 < CS$6$0000.Length; CS$7$0001++)
        {
            int num2 = CS$6$0000[CS$7$0001];
        }
        Console.WriteLine("foreach: " + ((TimeSpan) (DateTime.Now - now)));
        now = DateTime.Now;
        using (IEnumerator<int> CS$5$0002 = ((IEnumerable<int>) ints).GetEnumerator())
        {
            while (CS$5$0002.MoveNext())
            {
                int current = CS$5$0002.Current;
            }
        }
        Console.WriteLine("foreach casted to IEnumerable<int>: " + ((TimeSpan) (DateTime.Now - now)));
    }
}

答案 8 :(得分:1)

学习。

文章很好,但它们不提供生产代码。如果没有 .NET Reflector ,我需要花费几周的时间来弄清楚Microsoft如何在FileSystemWatcher组件中实现事件。相反,它只有几个小时,我能够完成我的FileSystemSearcher组件。

答案 9 :(得分:0)

我自己经常想这......:)

有时候需要了解特定的库方法是如何工作的,或者为什么它以这种方式工作。可能存在这样一种情况,即关于此功能的文档含糊不清或存在一些需要调查的奇怪行为。在这种情况下,有些人会去反汇编库来查看某些方法中的调用。

至于优化,我从来没有听说过这个。我认为最终愚蠢的尝试优化MIL,因为它将被提供给翻译器,翻译器将生成具有相当高效率的真实机器代码,并且您的“优化”可能会丢失。

答案 10 :(得分:0)

要了解底层系统的实现方式,请了解IL中高级代码的等价物,绕过许可......

答案 11 :(得分:0)

我在下面使用过它,更多的案例:

  1. 我没有源代码的内部程序集出现问题。
  2. 需要弄清楚特定的第三方控件库如何查找运行时许可证。
  3. 需要了解.Net许可证编译器的工作原理。 (刚刚将lc.exe放在Reflector中)
  4. 用它来确保我有正确构建的某些库。

答案 12 :(得分:0)

人们没有提到的一点是,如果使用编译时编织AOP框架(如PostSharp),反射器会非常有用。