如何干净地去掉强签名:About Stong Name Removing

强签名(StrongName)是一种对程序集进行唯一标识的手段,它基于RSA公/私钥实现。强签名之后的程序集不会和同名的不同程序集产生冲突。

强签名有两个作用:一是版本控制,二是完整性保护。之所以它能实现这两点,《CLR via C#》中的一幅图比较好地做出了解释:

版本控制:因为公私钥是唯一的,特别是私钥握在开发者自己手中,除非主动泄露,谁都签不出同样的名字来。因此一个正常的完整的强签名的程序集是基本不可能伪造的,因为你不可能伪造一个匹配同样公钥的RSA私钥证书。

完整性保护:因为签名过程中,计算了PE文件内容的哈希值,并对这个哈希值进行了签名并保存在CLR头中,所以当检验强签名程序集时,自然会把这个哈希值对照一下。没有私钥的你,是不可能修改CLR头的这个部分,改出一个正确加密后的哈希值的。所以整个程序你一个字节都不能动了,否则程序集会拒绝运行(除非你用碰撞算法搞出哈希值相同的内容……)。

但是事实上,只要你是系统的主人(管理员权限),这两点都是无法保证的,因为强签名是可以跳过验证的,也是可以去签名甚至重签名的。

跳过验证,适用于具有管理员权限或能说服用户提供管理员权限的情况,用到的工具正是WINSDK自己提供的签名用工具SN.exe。此工具的使用方法请查阅MSDN。跳过验证后,具有原来的公钥/公钥Token的程序集的内容已经不做验证,这通杀了强签名的所有作用。你可以通过为自己的程序集伪造同样的公钥/公钥Token来破坏版本控制并取代原本的程序集,也可以任意修改原本的程序集而不必担心程序集因为哈希值不正确拒绝运行。

去签名/重签名,主要是突破完整性保护。适用于你拿到一个强签名程序/类库却想做些改动再发布或者放到你的程序里,同时不关心这些程序集的复用(在GAC中)的情况。而且相对于上一种办法,这样做也不需要管理员权限先批准才能运行。不过,随着.NET平台的发展,程序集可能通过各种途径花式引用强签名程序集。引用强签名程序集的一般格式是这样的:

VOICe.Audio, Version=1.0.5938.25217, Culture=neutral, PublicKeyToken=c90da69f58ce61df

这种引用可能出现在很多很多地方,包括但不限于以下几种:

  • 程序集引用(AssemblyRef)表中,也就是记录了这个程序集引用了哪些其他程序集的地方。所有引用的强签名程序集,这个表中都记录有其公钥Token
  • WPF程序的BAML中,为了在XAML中引用别的强签名程序集的功能或者控件,自然要注明其公钥Token,这也是传统去签名工具基本没有处理的一处
  • 各种针对程序集的标签,比如InternalsVisibleTo(内部对其可见)标签,要使一个程序集对另一个强签名程序集可见,也需要注明其公钥

那么,我们要去掉一个类库的强签名,仅仅是处理这个目标程序集本身,把它的的公钥和公钥Token置为空,通常是不够的。至少还需要扫描并处理所有引用它的程序集以及BAML。针对每个引用它的程序集、BAML、以及程序集中的含引用标签,我们把其中记载的目标程序集的公钥Token也都要置为空。最终可能要修改很多程序集。

事实上,通常同一个开发者开发的一个程序的多个程序集通常都是使用同样的公私钥的。在去签名时,可以将这个公钥/公钥Token记录下来,凡是遇到此公钥一律删除,最后即可得到完整的去签名程序,同时也不改变其他第三方的强签名程序集。

我自己搞了个实现上面的描述功能的小工具:ASNR(AutoStrongNameRemover)。能够追踪和去除所有相关程序集、BAML和标签中的指定的公钥/公钥Token记录。据我所知的老一代去签名工具(如SNR、SNK)都不会去除BAML和标签中的强签名的,因此ASNR可能是目前效果最强的去签名工具也说不定(笑,肯定是我想多了)。

如果你想使用强签名真正保护程序集,还需要多动些手脚,比如某些混淆器就提供强签名验证功能,在程序运行时悄悄地自己进行基于强签名信息的验证,即使你使用了上述技术,如果不彻底删除四处分布的验证代码,仍然无法使用这个程序集。

——————

12-24补记:我已经进一步把ASNR升级到不仅可以去签名,还可以针对一个签名进行自动重签名,同时还能够保证其他程序集的引用正确。(有意思的是,我对某软件测试过重签名并取得成功后,它随后的新版本竟然自己也换了一套新的签名……)

评论 (3) -

添加评论

Loading