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

Siy*_*ion 18 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` ...等.

任何想法或建议将是最受欢迎的!

Dan*_*iel 5

更新:Roslyn似乎有/feature:deterministic可重复构建的编译器标志,尽管它还没有100%正常工作.


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

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

我会通过浏览#Strings元数据流并将"<PrivateImplementationDetails> {GUID}"形式的所有字符串替换为"<PrivateImplementationDetails> {running number,填充到与GUID相同的长度}"来完成此操作.

#Strings元数据流只是元数据使用的字符串列表,以UTF-8编码并以\ 0分隔; 因此,一旦知道#Strings流在可执行文件中的位置,查找和替换名称应该很容易.

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