我先给大家介绍一下我为什么要问这个问题的背景:
我目前正在一个严格监管的行业工作,因此我们的代码由官方测试机构仔细查看。这些测试机构希望能够构建代码并生成.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` ...等..
欢迎任何想法或建议!
答案 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生成:
如果您是从控制台构建的,请使用/ debug-来获得相同的结果。
答案 3 :(得分:1)
答案 4 :(得分:0)
你说在经过一些项目调整后,你可以让C ++应用程序重复编译为相同的SHA1 / MD5值。我和第三方测试实验室在同一个行业中,我需要重复完全相同的可执行文件。
在研究如何在VS2005中实现这一点时,我在这里发现了你的帖子。您是否可以分享您为使C ++应用程序始终构建相同的SHA1 / MD5值所做的项目调整?这对我自己以及任何其他有共同要求的人都有很大的帮助。
答案 5 :(得分:0)
使用ildasm.exe完全反汇编这两个程序并比较IL。然后,您可以使用基于文本的方法“清理”代码,并(可预测地)再次重新编译它。