请求Reflection API重写System.String.Empty有什么含义?

我偶然发现了这个代码:

static void Main() { typeof(string).GetField("Empty").SetValue(null, "evil");//from DailyWTF Console.WriteLine(String.Empty);//check //how does it behave? if ("evil" == String.Empty) Console.WriteLine("equal"); //output: //evil //equal } 

我不知道如何编译这段代码。 我的推理是:

根据MSDN String.Empty是只读的,因此改变它应该是不可能的,编译应该以“静态只读字段不能被分配”或类似的错误结束。

我认为基类库程序集是不知何故被保护和签名,什么不能防止这种types的攻击。 下次有人可能会更改System.Security.Cryptography或另一个关键类。

我以为Base Class Library程序集是在.NET安装之后由NGEN编译的,因此改变String类的字段应该需要高级的黑客程序,而且要困难得多。

然而这个代码编译和工作。 有人能解释我的推理有什么问题吗?

静态只读字段不能分配给

你没有分配给它。 你在System.Reflection命名空间中调用公共函数。 没有理由让编译器抱怨。

此外, typeof(string).GetField("Empty")可以使用用户input的variables,编译器在所有情况下都无法确定GetField的参数是否最终为"Empty"

我想你想Reflection看到该字段被标记为initonly并在运行时抛出一个错误。 我可以看到为什么你会期望,但是对于白盒testing,即使写入到initonly字段有一些应用程序。

NGEN没有任何作用的原因是你在这里没有修改任何代码,只有数据。 与其他语言一样,数据也被存储在内存中。 本地程序可能会使用只读内存部分,如string常量,但指向string的指针通常仍然是可写的,这就是发生在这里的情况。

请注意,您的代码必须以完全信任的方式运行,才能以可疑的方式使用reflection。 另外,这个改变只会影响到一个程序,这不像你想象的那样是一种安全漏洞(如果你在程序中充满信任地运行恶意代码,那么这个devise决定就是安全问题,而不是reflection) 。


另外请注意, mscorlib.dll中的initonly字段的值是.NET运行时的全局不variables。 破坏它们之后,甚至不能可靠地testing不variables是否被破坏,因为检查System.String.Empty的当前值的代码也被破坏了,因为你违反了它的不variables。 开始违反系统不variables,什么都不能依赖。

通过在.NET规范中指定这些值,它使编译器能够实现大量的性能优化。 只是一个简单的是

 s == System.String.Empty 

 (s != null) && (s.Length == 0) 

是相当的,但后者要快得多(相对而言)。

编译器也可以确定

 if (int.Parse(s) > int.MaxValue) 

永远不会是真的,并生成无条件跳转到else块(它仍然必须调用Int32.Parse具有相同的exception行为,但可以删除比较)。

System.String.Empty在BCL实现中也被广泛使用。 如果覆盖它, 可能会发生各种各样的疯狂事情,包括在程序外部泄漏的损坏 (例如,您可能会写入一个名称是使用string操作构build的文件…当string中断时,您可能会覆盖错误的文件)


而且.NET版本之间的行为可能很不一样。 通常情况下,当发现新的优化机会时,它们不会回溯到之前版本的JIT编译器(即使它们是在执行backport之前也可能有安装)。 尤其是。 在.NET 2.x和Mono以及.NET 4.5+之间, String.Empty相关的优化是显而易见的。

代码编译,因为代码的每一行是完全合法的C#。 你认为什么具体的行应该是一个语法错误? 没有一行代码分配给只读字段。 有一行代码调用Reflection中的一个方法,将其分配给只读字段,但已经编译完成,最终破坏安全性的事情甚至没有用C#编写,而是用C ++编写的。 它是运行时引擎本身的一部分。

代码运行成功,因为完全信任意味着完全信任 。 您在完全信任的环境中运行您的代码,并且由于完全信任意味着完全信任,所以运行时假设您知道自己在做这个愚蠢的危险事情时正在做什么。

如果您尝试在部分信任的环境中运行您的代码,那么您会看到,Reflection会抛出“您不允许这样做”的例外。

是的,这些组件是签署的,什么都不是。 如果你运行的是完全信任的代码,那么可以肯定的是,他们可以随心所欲地使用这些程序集。 这就是完全信任的意思。 部分信任的代码不会这样做,但完全可信的代码可以做任何你可以做的事情。 只有授予你真正信任的代码的完全信任,才能代表你不做疯狂的事情。

reflection让你无视任何物理定律 。 你甚至可以设置私人会员的价值。

反思不遵守规则, 你可以在MSDN上阅读 。

另一个例子: 我可以使用reflection在C#中更改私有只读字段吗?


如果您使用的是Web应用程序,则可以设置应用程序的信任级别。

 level="[Full|High|Medium|Low|Minimal]" 

这些是对信任级别的限制,对MSDN有一定的限制 ,在中等信任的情况下限制reflection访问。

编辑: 不要运行除完全信任以外的Web应用程序,这是ASP.NET团队的直接推荐。 为了保护你的应用程序,为每个网站创build一个App Pool。


此外,不build议使用reflection为所有事情疯狂。 它有正确的地点和时间来使用。

前面没有提到的一点:这段代码在不同的.NET实现/平台上会导致不同的行为。 实际上在单声道它什么也没有返回: 看到IDE One(单声道2.8) ,我本地的单声道2.6.7(Linux)产生相同的“输出”。

我还没有看过低层次的代码,但是我想它是PRASHANT P或者运行时环境中提到的编译器特有的。

UPDATE

在Windows上运行Mono编译的exe(MS Dotnet 4)会产生

 evil equal 

在Linux上运行Windows编译的exe是不可能的(Dotnet 4 …),所以我重新编译Dotnet 2(它仍然说在Windows上邪恶平等 )。 有“没有”输出。 当然,第一个WriteLine至less应该有"\n" ,实际上它就在那里。 我输出到一个文件,并启动我的hexeditor看单个字符0x0A

长话短说:似乎是运行环境特定的。

只读只是强制执行

在编译器级别

因此

可能会在目前的水平下改变