Cla*_*ton 29 .net il checksum assemblies binary-reproducibility
有没有人知道比较两个.NET程序集以确定它们是否是从"相同"源文件构建的方法?
我知道有一些差异实用程序可用,例如Reflector的插件,但我对查看GUI中的差异不感兴趣,我只是想要一种自动方式来比较二进制文件的集合,看看它们是否是从相同(或等效)的源文件.我知道多个不同的源文件可以产生相同的IL,并且意识到该过程只对IL的差异敏感,而不是原始源.
仅仅比较两个程序集的字节流的主要障碍是.NET包含一个名为"MVID"(模块版本标识符)的程序集.这似乎对每个编译都有不同的值,因此如果您构建相同的代码两次,则程序集将有所不同.
一个相关的问题是,是否有人知道如何强制MVID为每个编译相同?这将避免我们需要具有对MVID值的差异不敏感的比较过程.一致的MVID将是更可取的,因为这意味着可以使用标准校验和.
这背后的背景是,在我们被允许发布到Production之前,第三方公司负责独立审核和签署我们的版本.这包括查看源代码.他们希望独立确认我们提供的源代码与我们之前构建,测试并且当前计划部署的二进制文件相匹配.我们正在寻找一个允许它们从我们提供的源中独立构建系统的过程,并将校验和与我们测试的二进制文件的校验和进行比较.
BTW.请注意,我们正在使用持续集成,自动构建,源代码控制等.该问题与内部缺乏对源文件进入给定构建的控制无关.问题在于,第三方负责验证我们提供的来源是否与我们测试过并计划投入生产的二进制文件相同.他们不应该信任我们的任何内部系统或控件,包括构建服务器或源代码控制系统.他们所关心的只是获取与构建相关的源,自己执行构建,并验证输出是否与我们所说的部署相匹配.
比较解决方案的运行速度并不是特别重要.
谢谢
小智 10
使用命令行工具从IL的文本表示中过滤掉MVID和日期时间戳并不太痛苦.假设file1.exe和file2.exe是从相同的源构建的:
c:\ temp> ildasm/all/text file1.exe | find/v"时间戳日期:"| find/v"MVID"> file1.txt
c:\ temp> ildasm/all/text file2.exe | find/v"时间戳日期:"| find/v"MVID"> file2.txt
c:\ temp> fc file1.txt file2.txt
比较文件file1.txt和FILE2.TXT
FC:没遇到任何差异
我在.Net 4程序集上使用了Jerry Currry的解决方案,发现现在有第三个项目会因每个构建而异:校验和.在程序集中找到校验和是不是很令人惊讶?我认为在该文件中添加文件的校验和将改变校验和...
无论如何,修改后的命令是:
ildasm /all /text "assembly.dll"
| find /v "// Time-date stamp:"
| find /v "// MVID:"
| find /v "// Checksum:"
> assembly.dasm
请注意,我还通过添加斜杠稍微更改了搜索字符串,以避免无意的匹配.此命令的行应该在同一行上一起运行,拆分以便于阅读.如果文件名包含空格,则它们将需要双引号.
将类库与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倍.
有几种方法可以做到这一点,具体取决于您愿意做的工作量以及性能和/或准确性的重要性。Eric J. 指出的一种方法是比较二进制的程序集,排除每次编译时发生更改的部分。该解决方案既简单又快速,但可能会给您带来很多误报。一种更好的方法是使用反射进行深入研究。如果性能很关键,您可以从比较类型开始,如果它们匹配,则转到成员定义。检查类型和成员定义后,如果一切都等于这一点,您可以进一步通过方法获取每个方法的实际 IL GetILAsByteArray。即使一切都相同但使用稍微不同的标志或不同版本的编译器进行编译,您也会再次发现差异。我想说最好的解决方案是使用持续集成工具,用源代码管理的变更集编号来标记构建(您正在使用一个,对吧?)。