10

我想看看 DLL(可能在不同的机器上编译)是否相同。为此,我正在做的是加载 DLL 并计算 MD5,因为 DLL 在不同的机器上失败(但具有相同的源)而失败。这似乎是由于在编译时添加的其他元数据(正如有人提到的这里)。

我想对整个 DLL 进行逆向工程,看看代码是否匹配,但是,我有两个问题:

  • 我只能找到执行此操作的工具,我似乎无法找到 C# 库或类似的东西来满足我的需要。
  • 我不是 100% 确定反编译的源代码是否与跨不同机器编译的源代码相同。

任何提示、提示和指示将不胜感激。

4

4 回答 4

5

您可能是对的 - 它可能是元数据。不过,我认为这不一定是最有可能的可能性。

DLL 不同的另一个原因可能是它们是针对不同版本的 .NET 编译的,或者可能是 MONO。

无法保证反编译 DLL 会产生相同的代码,即使它们是从相同的源编译的;确实,考虑到编译器的优化特性,稍微不同的源代码可以编译为相同的可执行文件的可能性很小,理论上(但存在) - 通常,循环将被展开 - 即变成顺序的非循环指令- 当这会节省内存使用或 CPU 时间时。

如果程序员手动展开循环并重新编译,那是编译器一直在做的优化——presto,两个不同的源具有相同的输出。

一个更好的问题是您希望通过比较两个 DLL 来了解什么。如果纯粹是为了学习,那就太棒了,值得称赞——但是,要对此进行有意义的研究,您需要的知识量是相当高的。通过学习通用的、更适用的 C#/.net 技术,您可能会找到更好的结果。

于 2012-08-21T07:25:21.327 回答
1

使用强名称对该程序集进行签名,您将能够绝对确定两个或多个程序集只是同一个或不同的程序集,因为它们具有相同的程序集版本、相同的公钥令牌等。

如果代码源和 Visual Studio 项目不是同一个,我怀疑两个不同的开发人员会有重复的私钥。

于 2012-08-21T07:23:03.463 回答
1
  • 我的意思是,这些库是你的,是你的代码吗?
  • 它们是由您的安装人员安装的,还是独立安装的,您只需检查它们?

如果你能以任何方式监督它们在目标机器上的初始安装,你可以用普通的旧 DLL 资源做一些穷人的水印。

将包含您自己内容的二进制资源附加到已安装的每个版本的 DLL,然后检查该文件。如果您public static readonly class Something{ public static SomeData MyImportantInformation = ...; }在每个代码中嵌入 a 并在运行时读取它,或者好像您将 [Attributes] 与某些类上的数据一起使用并通过反射读取它们,那就更棒了——但是使用二进制资源有两个微小的优势:

  • 您可以在构建 DLL后从 DLL 中添加/删除资源(有点像 ILMerge 工具)
  • 您可以像从托管代码中一样轻松地从本机代码中读取资源,并且要读取它们,您可以以非常有限且节省资源的方式加载 DLL

请注意,我的意思是“低级资源”,例如 Manifest,它通常位于插槽 #0 上的资源,或 .exe/.dll 图标。

关于二进制资源:

http://www.codeproject.com/Articles/4221/Adding-and-extracting-binary-resources

以及更易于使用的托管嵌入式资源:

http://keithelder.net/2007/12/14/how-to-load-an-embedded-resource-from-a-dll/ https://stackoverflow.com/a/7978410/717732

您可以在构建脚本中添加/修改资源,以确保发布的每个版本都添加了不同/正确的信息。当然,如果您控制构建过程,那么您也可以启动前面提到的 ILmerge 以将任何内容放入任何 DLL 中。虽然大部分都可以工作,但总的来说,我认为这是一种矫枉过正的做法,如果做得不正确的话如果它在签名后修改 DLL,则会破坏任何安全签名。必须在它之前完成。。

如果您控制构建过程,您可以将必要的版本信息作为类静态数据或简单地作为程序集级别的属性放入代码中,或者 (...)

或者你为什么不只使用版本号来区分版本?:) IE。语义版本控制

另一方面,如果您正在使用非您的 DLL,并且您无法控制它们的部署,那么您将处于艰难的境地。正如其他人所说,编译器可能会在编译期间应用许多不同的技巧,但是 - 请注意 - 他们对编译后的代码可以做的事情有一些法律和逻辑限制。

“逻辑”约束的示例:
-它们可能会更改指令,但可能不会更改整体含义和(副作用)-它们可能会更改代码和数据布局/结构,但不会以将算法更改为处理它们等

“法律”约束的示例:
- 它们不允许删除任何公共符号(公共 = 其他代码模块可见,即在 .Net 中涵盖:公共和受保护,有时甚至是内部和私有) - 它们不是允许更改任何公共符号的名称 - 他们不允许更改任何公共符号的签名等

现在,如果您仅将自己限制在此类信息中,则可以以一种可能与编译器和平台无关的方式收集/计算任何代码的哈希/签名。您不会得到相同或不同的明确答案,但您会了解它们的可能性有多大。

举个最简单的例子:通过反射加载 DLL 并扫描所有类的公共和非公共成员名称。然后,要么计算该字符串集的哈希值,要么只使用整个字符串集,我可能最多以千字节计。如果对代码进行了较大的更改,几乎可以肯定会添加或删除某些字段/方法。对于较小的更改,您还可以扫描方法的签名:添加参数列表和参数类型并将值返回到池中。更多的工作和更多的检测变化的可能性。

对于重要的更改:您可以尝试扫描方法的 ILCode 并检测其中的结构。编译器可能会内联,有时会删除方法/循环/等,但会保留整体结构。特定的代码块在这里或那里执行 n 次,分支在它们的位置但可能交换边等。但是,检测控制结构并不容易,比较代码更加困难。对于某些代码,它可能会给你一个“完全相同”的明确答案,但很多时候你会得到“不一样”,即使它们是。该主题的一些关键字是......重复或抄袭检测器。这就是对此类事物的研究的开始:) 参见 ie。https://stackoverflow.com/questions/546487/tools-to-identify-code-duplications虽然我不知道提到的工具是否扫描代码,

于 2012-08-21T08:50:25.130 回答
0

我们确实设法解决了这个问题......我们所做的是我们添加了一个预构建事件,该事件通过一些相关文件(我们更改的文件,例如 .CS 文件)并计算每个文件的哈希值. 每个散列值最终都会对DLL的全局散列有所贡献。由于我们只有少量文件,因此发生冲突的可能性很小。

然后我们在 DLL 的描述中添加校验和。这使我们能够在不同的机器上编译 DLL,但由于它们的源相同,因此产生了相同的校验和。

感谢您提供的所有答案,它们很有帮助。

于 2012-08-21T11:27:34.233 回答