30

有谁知道比较两个 .NET 程序集以确定它们是否是从“相同”源文件构建的方法?

我知道有一些可用的差异实用程序,例如 Reflector 的插件,但我对查看 GUI 中的差异不感兴趣,我只想要一种自动化的方式来比较二进制文件的集合,看看它们是否是从相同(或等效)的源文件。我知道多个不同的源文件可以生成相同的 IL,并且意识到该过程只会对 IL 中的差异敏感,而不是原始源。

仅比较两个程序集的字节流的主要障碍是 .NET 在程序集中包含一个名为“MVID”(模块版本标识符)的字段。这似乎对每次编译都有不同的值,所以如果你构建相同的代码两次,程序集会有所不同。

一个相关的问题是,有谁知道如何强制每个编译的 MVID 相同?这将避免我们需要一个对 MVID 值差异不敏感的比较过程。一致的 MVID 更可取,因为这意味着可以使用标准校验和。

这背后的背景是,在我们被允许发布到生产环境之前,第三方公司负责独立审查和签署我们的发布。这包括审查源代码。他们希望独立确认我们提供给他们的源代码与我们之前构建、测试和当前计划部署的二进制文件相匹配。我们正在寻找一种流程,使他们能够从我们提供给他们的源代码独立构建系统,并将校验和与我们测试过的二进制文件的校验和进行比较。

顺便提一句。请注意,我们正在使用持续集成、自动构建、源代码控制等。该问题与内部缺乏对特定构建中的源文件的控制无关。问题是第三方负责验证我们提供给他们的源是否产生了我们已经测试并计划投入生产的相同二进制文件。他们不应该信任我们的任何内部系统或控制,包括构建服务器或源代码控制系统。他们所关心的只是获取与构建相关联的源,自己执行构建,并验证输出是否与我们所说的部署相匹配。

比较解决方案的运行速度并不是特别重要。

谢谢

4

7 回答 7

10

It's not too painful to use command-line tools to filter out MVID and date-time stamps from a text representation of the IL. Suppose file1.exe and file2.exe are built from the same sources:

c:\temp> ildasm /all /text file1.exe | find /v "Time-date stamp:" | find /v "MVID" > file1.txt

c:\temp> ildasm /all /text file2.exe | find /v "Time-date stamp:" | find /v "MVID" > file2.txt

c:\temp> fc file1.txt file2.txt

Comparing files file1.txt and FILE2.TXT

FC: no differences encountered

于 2010-07-07T13:51:06.370 回答
8

我在 .Net 4 程序集上使用了 Jerry Currry 的解决方案,发现现在每个版本都有第三个项目:校验和。在程序集中找到校验和是不是很奇怪?我认为在该文件中添加文件的校验和会改变校验和......

无论如何,修改后的命令是:

ildasm /all /text "assembly.dll"
| find /v "// Time-date stamp:"
| find /v "// MVID:"
| find /v "// Checksum:"
> assembly.dasm

请注意,我还通过添加斜杠对搜索字符串进行了一些更改,以避免意外匹配。此命令的行应在同一行上一起运行,为便于阅读而拆分。如果文件名包含空格,则需要在它们周围加上双引号。

于 2011-05-19T08:01:14.897 回答
8

在将类库与 ILDasm v4.0.319.1 进行比较时,似乎没有初始化图像库。为避免不匹配,请使用修改后的解决方案:

ildasm /all /text assembly.dll
| find /v "// Time-date stamp:"
| find /v "// MVID:"
| find /v "// Checksum:"
| find /v "// Image base:"
> assembly.dasm

入口点(图像库)实际上是可执行程序集的有趣信息,必须仔细验证。注入一个新的镜像库是让程序做一些完全不同的事情的常用方法。就我而言,我正在尝试验证多线程构建的一致性,因此跳过入口点是安全的。

关于性能的说明:我采用了一个为 AnyCPU 构建的 8MB DLL,并运行了 ILDasm。生成的文件大小为 251MB,制作需要几分钟时间。大约生产了 32 倍的尺寸。

于 2011-11-22T15:56:45.877 回答
3

有几种方法可以做到这一点,具体取决于您愿意做的工作量以及性能和/或准确性的重要性。Eric J. 指出的一种方法是比较二进制程序集,不包括每次编译时更改的部分。此解决方案简单快捷,但可能会给您带来很多误报。一种更好的方法是使用反射进行深入研究。如果性能很关键,您可以从比较类型开始,如果它们匹配,则转到成员定义。在检查类型和成员定义之后,如果一切都等于该点,您可以通过检查每个方法的实际 IL 来进一步了解它GetILAsByteArray方法。即使一切都相同,但使用稍微不同的标志或不同版本的编译器编译,您也会再次发现差异。我想说最好的解决方案是使用持续集成工具,用源代码控制的变更集编号标记构建(您正在使用一个,对吗?)。

相关文章

于 2010-05-31T01:03:16.383 回答
3

您可以使用 MonoCecil 并对其进行小修改以解决问题。我做到了,你可以在这里阅读:http ://groups.google.com/group/mono-cecil/browse_thread/thread/6ab42df05daa3a/49e8b3b279850f13#49e8b3b279850f13

问候弗洛里安

于 2011-06-18T22:02:30.917 回答
1

您可以在此处使用 Reflector Diff AddIn 。

于 2014-03-13T08:12:09.930 回答
0

另一个需要考虑的解决方案:

在调试模式下编译二进制文件时会存储源代码信息。然后您可以检查 pdb 是否与 exe 匹配以及 pdb 行是否与源代码匹配。

于 2010-11-18T07:29:26.313 回答