C# 要求反射API覆盖System.String.Empty的含义是什么?

C# 要求反射API覆盖System.String.Empty的含义是什么?,c#,.net,reflection,C#,.net,Reflection,我偶然发现了以下代码: 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"); //o

我偶然发现了以下代码:

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
是只读的,因此不可能对其进行更改,编译应以“无法分配静态只读字段”或类似错误结束

我认为基类库程序集在某种程度上受到保护和签名等等,以防止这种攻击。下次有人可能会更改System.Security.Cryptography或其他关键类

我认为基类库程序集是在.NET安装后由NGEN编译的,因此更改字符串类的字段应该需要高级的黑客攻击,而且要困难得多

但这段代码可以编译并运行。谁能解释一下我的推理有什么问题吗?

反射可以让你无视物理定律做任何事情。您甚至可以设置私有成员的值

反射不遵循规则

另一个例子:


如果您使用的是web应用程序,则可以设置

,在“中等信任”下,您可以限制反射访问

编辑:不要运行完全信任以外的web应用程序,这是ASP.NET团队的直接建议。要保护您的应用程序,请为每个网站创建一个应用程序池


此外,不建议对所有内容都使用反射。它有正确的使用地点和时间

无法将静态只读字段分配给

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

此外,
typeof(string).GetField(“Empty”)
可以使用用户输入的变量,编译器无法确定在所有情况下,
GetField
的参数是否最终都是
的“Empty”

我认为您希望
Reflection
看到字段标记为
initonly
,并在运行时抛出错误。我可以理解为什么您会期望这样,但是对于白盒测试,即使是写入
initonly
字段也有一些应用程序

NGEN不起作用的原因是这里没有修改任何代码,只有数据。与任何其他语言一样,使用.NET将数据存储在内存中。本机程序可能会对字符串常量之类的内容使用只读内存部分,但字符串指针通常仍然是可写的,这就是这里发生的情况

请注意,您的代码必须以完全信任的方式运行,才能以这种可疑的方式使用反射。此外,更改只影响一个程序,这并不像您认为的那样是一种安全漏洞(如果您在进程中完全信任地运行恶意代码,那么设计决策是安全问题,而不是反射)


进一步注意,
mscorlib.dll
中的
initonly
字段的值是.NET运行时的全局不变量。在破坏它们之后,您甚至无法可靠地测试不变量是否被破坏,因为检查System.String.Empty当前值的代码也被破坏了,因为您违反了它的不变量。开始违反系统不变量,任何东西都不可靠

通过在.NET规范中指定这些值,编译器可以实现一系列性能优化。一个简单的问题是

s == System.String.Empty

两者相当,但后者要快得多(相对而言)

编译器还可以确定

if (int.Parse(s) > int.MaxValue)
从不为true,并生成到else块的无条件跳转(它仍然必须调用
Int32.Parse
以具有相同的异常行为,但可以删除比较)

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



而且.NET版本之间的行为可能很容易有所不同。通常,当发现新的优化机会时,它们不会被后传到JIT编译器的早期版本(即使它们被后传,也可能在实现后传之前进行安装)。特别是
String.Empty
-与.NET 2.x和Mono以及.NET 4.5+相关的优化明显不同。

只读是唯一强制的

在编译器级别

所以


可以在当前级别下更改代码编译,因为代码的每一行都是完全合法的。您认为哪一行应该是语法错误?这里没有指定给只读字段的代码行。有一行代码调用一个反射方法,分配给只读字段,但已经编译好了,最终破坏安全性的东西甚至没有用C语言编写,它是用C++编写的。它是运行时引擎本身的一部分

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

如果您尝试在部分受信任的环境中运行代码,那么您将看到反射抛出“您不允许这样做”异常

是的,集会是有签名的等等。如果您运行的是完全信任的代码,那么当然,他们可以随心所欲地处理这些程序集这就是完全信任的含义。部分信任的代码不能做到这一点,但完全信任的代码可以做到这一点
(s != null) && (s.Length == 0)
if (int.Parse(s) > int.MaxValue)
evil 
equal