我想看看DLL(可能是在不同的机器上编译)是否相同。要做到这一点,我正在做的是加载DLL并计算MD5,这是因为DLL在不同的机器上失败(但具有相同的源)。这似乎是由于在编译时添加的其他元数据(如有人提到here)。
我想到对整个DLL进行反向工程并查看代码是否匹配,但是,我有两个问题:
任何提示,提示和指示都将不胜感激。
答案 0 :(得分:5)
你可能是对的 - 它可能是元数据。不过,我认为这不太可能是最可能的。
DLL的不同之处可能是它们是针对不同版本的.NET或可能是MONO编译的。
无法保证反编译DLL会产生相同的代码,即使它们是从同一个源编译的;实际上,鉴于编译器的优化性质,有一个微小的,理论上(但现存的)机会,稍微不同的源可以编译到相同的可执行文件 - 通常,一个循环将被展开 - 也就是说,变成顺序的,非循环的指令 - 这样可以节省内存使用量或CPU时间。
如果程序员手动展开循环并重新编译,那么这是编译器一直在进行的优化 - presto,具有相同输出的两个不同来源。
更好的问题是你希望通过比较两个DLL来学习。如果它仅仅是为了学习,那就太棒了并且值得称赞 - 但是,对于对此进行有意义的研究所需要的知识量非常高。通过学习一般的,更适用的C#/ .net技术,您可能会找到更好的结果。
答案 1 :(得分:1)
使用强名称对此程序集进行签名,您将能够绝对确定两个或多个程序集只是同一个 - 或者不同 - ,因为它们具有相同的程序集版本,相同的公钥令牌等。
如果代码源和Visual Studio项目不同,我怀疑两个不同的开发人员会有重复的私钥。
答案 2 :(得分:1)
如果你以任何方式监督他们在目标机器上的初始安装,你可以使用普通的旧DLL资源做一些穷人的水印。
将具有您自己内容的二进制资源附加到安装的每个DLL版本,然后检查该laster。如果你在每个代码中嵌入一个public static readonly class Something{ public static SomeData MyImportantInformation = ...; }
并在运行时读取它,或者就像你在某些类中使用[Attributes]一样的数据并通过反射读取它们,那就更好了 - 但是使用二进制资源有两个很小的优点:
请注意,我的意思是“低级资源”,例如Manifest,它通常位于插槽#0或.exe / .dll图标上。
二进制资源:
http://www.codeproject.com/Articles/4221/Adding-and-extracting-binary-resources
在托管的嵌入式资源上,这些资源更易于使用:
http://keithelder.net/2007/12/14/how-to-load-an-embedded-resource-from-a-dll/ https://stackoverflow.com/a/7978410/717732
您可以添加/修改构建脚本的资源,以确保发布的每个版本都添加了不同/正确的信息。当然,如果你控制构建过程,那么你也可以启动前面提到的ILmerge将任何东西放入任何DLL中。虽然大部分都可以工作,但总的来说,我认为这是一种矫枉过正的做法,如果不正确的话如果在签名后修改DLL,则会破坏任何安全签名。它必须在它之前完成..
如果您控制构建过程,您可以将必要的版本控制信息作为类静态数据放入代码中,或者仅作为程序集级别的属性,或者(...)
或者您为什么不使用版本号区分版本? :)即。 semantic versioning?
另一方面,如果您正在使用不属于您的DLL,并且如果您无法控制其部署,那么您就有了强硬的理由。正如其他人所说,编译器可能会在编译过程中应用许多不同的技巧,但是 - 请注意 - 他们对编译代码的作用有一些法律和逻辑限制。
“逻辑”约束的例子:
- 他们可能会更改说明,但可能不会改变整体含义和(副作用)
- 它们可能会改变代码和数据布局/结构,但不会改变算法来处理它们
等
“合法”约束的例子:
- 不允许他们删除任何公共符号(public =其他代码模块可见,也就是说.Net中包含:public和protected,有时甚至是内部和私有)
- 不允许他们更改任何公共符号的名称
- 他们不得更改任何公共标志的签名
等
现在,如果您仅限于此类信息,则可以有机会编译器和平台无关的方式收集/计算任何代码的哈希/签名。你不会得到一个相同或不相同的定义答案,但你会得到一个关于它们可能性的观点。
对于最简单的示例:通过反射加载DLL并扫描所有类的公共和非公共成员名称。然后,要么计算该字符串集的哈希值,要么只使用整个字符串集,我最多可能以千字节为单位。如果对代码进行了大的更改,则几乎可以肯定会添加或删除某些字段/方法。对于较小的更改,您还可以扫描方法的签名:添加参数列表和参数类型,并将值返回到池中。更多的工作和更多的检测变化的可能性。
对于非平凡的更改:您可以尝试扫描方法的ILCode并检测其中的结构。编译器可以内联并有时删除方法/循环/等,但保留整体结构。特定的代码块在这里或那里被执行n次,分支在它们的位置但是可能与侧面交换等。但是,检测控制结构并不容易,并且比较代码更加困难。对于某些代码,它可能会给你一个“完全相同”的明确答案,但很多时候你会得到“不一样”,即使它们是。关于该主题的一些关键词是......复制或抄袭检测器。这就是对这类事情的研究开始的方式:)参见ie。 https://stackoverflow.com/questions/546487/tools-to-identify-code-duplications虽然我不知道所提到的工具是扫描代码还是“字节”..
答案 3 :(得分:0)
我们确实找到了解决这个问题的方法...我们所做的是我们添加了一个预构建事件,该事件通过一些相关文件(我们更改的文件,例如.CS文件)并计算哈希值每个人的价值。每个哈希值最终都会导致DLL的全局哈希。由于我们只有少量文件,因此碰撞的可能性非常小。
然后我们在DLL的描述中添加校验和。这允许我们在不同的机器上编译DLL,但由于它们的源相同,因此产生了相同的校验和。
感谢所提供的所有答案,他们非常有帮助。