如何在重建C#应用程序时始终生成逐字节相同的.exe文件?

时间:2009-09-15 10:30:00

标签: c# binary-reproducibility

我先给大家介绍一下我为什么要问这个问题的背景:

我目前正在一个严格监管的行业工作,因此我们的代码由官方测试机构仔细查看。这些测试机构希望能够构建代码并生成.exe或.dll,每次都完全相同(显然不会更改任何代码!)。他们检查MD5和他们创建的可执行文件的SHA1以确保这一点。

到目前为止,我主要使用C ++进行编码,其中(在几个项目设置调整之后)我设法让项目能够相同地重建到相同的MD5 / SHA1。我现在正在一个项目中使用C#,并且在重建之后很难让MD5匹配。我知道文件的PE头中有“Time-Stamps”,它们已经被清除为0.我也知道.exe有一个GUID,它再次被清除为00 00 00 ......等但是文件仍然不匹配。

我正在使用CFF资源管理器查看和编辑PE标头以删除时间和日期戳。使用二进制比较工具后,.exe中只有2个字节块不同(都非常小)。

其中一个不一致的块在某些二进制代码之前出现 ,在ASCII中详细说明了*Project*\obj\Release\xxx.pdb文件的路径。

编辑:现在知道这是* .pdb文件的GUID,但我仍然不知道是否可以修改它而不会导致任何错误!?

另一个块出现在看起来是函数名称的中间,即。 (典型部分)AssemblyName.GetName.Version.get_Version.System.IO.Ports.SerialPort.Parity.Byte.<PrivateImplementationDetails>{

然后是不同的代码块:

4A134ACE-D6A0-461B-A47C-3A4232D90816

接下来是:

“}。ValueType .__ StaticArrayInitTypeSize = 7. $$ method0x60000ab-1.RuntimeFieldHandle.InitializeArray` ...等..

欢迎任何想法或建议!

6 个答案:

答案 0 :(得分:5)

更新:Roslyn似乎有一个/feature:deterministic编译器标志,用于可重现的构建,尽管it's not 100% working yet


您应该能够通过禁用PDB生成来摆脱调试GUID。如果没有,将GUID设置为零是很好的 - 只有调试器会查看该部分(您将无法再调试程序集,但它仍然可以正常运行)。

PrivateImplementationDetails有点困难 - 这些是编译器为某些语言结构(数组初始化器,使用字符串的switch语句等)生成的内部帮助器类。因为它们仅在内部使用,所以类名称并不重要,因此您可以为它们分配一个运行编号。

我会通过浏览#Strings元数据流并将“&lt; PrivateImplementationDetails&gt; {GUID}”形式的所有字符串替换为“&lt; PrivateImplementationDetails&gt; {运行号码,填充到与GUID相同的长度}”来完成此操作

#Strings元数据流只是元数据使用的字符串列表,以UTF-8编码并以\ 0分隔;因此,一旦您知道#Strings流在可执行文件中的位置,就可以轻松找到并替换名称。

不幸的是,包含此信息的“元数据流标题”完全隐藏在文件格式中。你必须从NT Optional Header开始,找到指向CLI Runtime Header的指针,使用PE section表将它解析为文件位置(它是一个RVA,但你需要在文件中有一个位置),然后转到元数据根并读取流标题。

答案 1 :(得分:2)

我不确定这个,但只是一个想法:你使用的是编译器可能在幕后生成名称的任何匿名类型,每次编译器运行时可能会有所不同吗?只是有可能发生在我身上。可能是Jon Skeet的一个; - )

更新:您也可以使用Reflector addins进行比较和反汇编。

答案 2 :(得分:2)

关于PDB GUID问题,如果指定在编译发布版本时不应生成PDB,二进制文件是否仍包含PDB的文件系统GUID?

禁用PDB生成:

  1. 在解决方案资源管理器中右键单击您的项目,然后选择“属性”。
  2. 从左侧菜单中选择Build。
  3. 确保配置选择是Release(您仍然需要PDB进行调试)。
  4. 单击右下角的“高级”按钮。
  5. 在“输出/调试信息”下,选择“无”。
  6. 如果您是从控制台构建的,请使用/ debug-来获得相同的结果。

答案 3 :(得分:1)

查看this问题的答案。特别是在第3个提供的外部链接上。

修改

我实际上想要链接到this文章。

答案 4 :(得分:0)

你说在经过一些项目调整后,你可以让C ++应用程序重复编译为相同的SHA1 / MD5值。我和第三方测试实验室在同一个行业中,我需要重复完全相同的可执行文件。

在研究如何在VS2005中实现这一点时,我在这里发现了你的帖子。您是否可以分享您为使C ++应用程序始终构建相同的SHA1 / MD5值所做的项目调整?这对我自己以及任何其他有共同要求的人都有很大的帮助。

答案 5 :(得分:0)

使用ildasm.exe完全反汇编这两个程序并比较IL。然后,您可以使用基于文本的方法“清理”代码,并(可预测地)再次重新编译它。