4

我对 .NET 有点陌生,但对编程并不陌生,而且我对反汇编已编译的 .NET 代码的趋势和兴奋感到有些困惑。似乎毫无意义。

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

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

那么,除了手头有太多时间之外,人们为什么还要反汇编 .NET (CLR) 二进制文件呢?

4

13 回答 13

7

了解各种高级语言的编译器实际上对您的源代码做了什么是您掌握特定环境时需要掌握的一项重要技能,就像了解数据库引擎将如何计划执行各种 SQL 查询一样可以向他们折腾。要以熟练的方式使用某种抽象级别,熟悉(至少)低于它的级别是一件非常好的事情;例如,请参阅我关于抽象主题的演讲的一些注释该演讲的幻灯片,以及我在演讲中提到的 Joel Spolsky 的“泄漏抽象法则”。

于 2009-05-30T19:55:10.587 回答
3

当源代码丢失或特定标记版本中的版本控制中的内容似乎与随附的二进制文件不对应时,我会使用它。

于 2009-05-30T20:06:58.987 回答
2

在刚刚完成 4 天的安全软件开发课程后,我想说很多人会反编译源代码以发现其中的任何漏洞。了解客户端应用程序的来源有助于计划对服务器的攻击。

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

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

于 2009-05-30T20:41:23.007 回答
2

了解如何使用文档记录不佳的界面。

(遗憾的是,在 BizTalk 或 WCF 等基于 .net 的工具中过于频繁,只能生成通用生成的文档,因此有时需要反汇编为 C# 以查看方法正在做什么,在什么上下文中使用它)

于 2009-05-30T22:40:00.253 回答
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)));
    }
}
于 2009-05-31T19:12:33.053 回答
1

学习。

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

于 2009-10-29T23:06:14.660 回答
1

每种 .NET 语言都实现了自己的 CLR 功能子集。知道 CLR 能够完成您当前使用的语言所不具备的功能,可以让您就是否更改语言或发出 IL 或寻找其他方式做出明智的决定。

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

于 2009-05-30T20:00:43.320 回答
1

定位库错误并找出解决方法。

例如:没有反射,你不能远程异常并重新抛出它而不杀死它的回溯。但是框架可以做到。

于 2009-05-30T21:02:08.393 回答
1

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

于 2009-05-30T21:08:49.377 回答
0

Something that folks haven't mentioned is that reflector comes in super useful if you use a compile time weaving AOP framework like PostSharp.

于 2009-10-26T05:38:37.770 回答
0

我自己经常想知道这个...... :)

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

至于优化,我从来没有听说过。我认为尝试优化 MIL 最终是愚蠢的,因为它将被馈送到一个翻译器,该翻译器将以相当高的效率生成真正的机器代码,并且无论如何你的“优化”可能会丢失。

于 2009-05-30T19:56:41.160 回答
0

要了解底层系统是如何实现的,请了解 IL 中高级代码的等价物,规避许可......

于 2009-05-30T21:06:24.077 回答
0

我在以下更多案例中使用了它:

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