少于 1 分钟阅读

在上一篇 Dotfuscator中字符串混淆算法 里面,展示了字符串在托管程序中的脆弱性, 使用混淆工具仅仅一定程度上增加了静态逆向分析的复杂度,但是在运行时,狐狸的尾巴就不是那么容易藏住了。 那么,作为信息载体很重要的部分的字符串,我们要如何处理,才能达到高标准的安全要求呢?

或许你也注意到了,在最终还原出来的字符串还原算法里有这样一段代码:

1
  return string.Intern(new string(data));

它在干什么呢,又有什么作用呢?

MSDN的解释是这样的:

String.Intern 方法 检索系统对指定 String 的引用。

返回值
如果 str 的值已经留用,则返回系统的引用;否则返回对带有 str 值的字符串的新引用

备注
公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。 因此,具有特定值的字符串的实例在系统中只有一个。
Intern 方法使用拘留池来搜索与传入字符串 str 值相等的字符串。 如果存在这样的字符串,则返回拘留池中它的引用。 如果不存在,则向拘留池添加对传入字符串 str 的引用,然后返回该引用

显然这里的意思就是,搜索CLR看有没有该串的实例,有就直接返回,没有则创建一个,最显著的作用就是减小内存消耗,而其坏处也是很明显的,一定的性能消耗,和由于字符串缓存导致字符串透明化——这里你应该想到点什么了吧:如果有个工具可以查看当前CLR的内存,因为字符串一直存在,所有的信息都无处藏身了。MSDN也提到了这一点:

性能注意事项

如果要减少应用程序分配的内存总量,请记住留用字符串有两个不希望出现的副作用。 首先,为留用的 String 对象分配的内存在公共语言运行库 (CLR) 终止之前不大可能释放。 这是因为 CLR 对留用的 String 对象的引用可能保持到应用程序终止之后,甚至可能保持到应用程序域终止之后。 其次,要留用字符串,必须先创建字符串。即使 String 对象使用的内存最终将通过垃圾回收,仍然必须分配该内存。

那么如何提高字符串的安全性呢?.NET Framework提供了一个解决方案:

.NET Framework 2.0 版引入了 CompilationRelaxations.NoStringInterning 枚举成员。 NoStringInterning 成员将程序集标记为不需要字符串拘留。 可以使用 CompilationRelaxationsAttribute 属性将 NoStringInterning 应用于某个程序集。

这样就可以使得CLR不再驻留字符串,虽然消耗一些内存资源, 但是可以相当的提高字符串的安全性——当然编码时需要注意的是及时让不再需要的字符串进入回收机制(作用域或者设置变量引用指向null)。