Kry*_*lik 61 .net c# 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");
//output:
//evil
//equal
}
Run Code Online (Sandbox Code Playgroud)
我想知道如何编译这段代码甚至是可能的.我的理由是:
根据MSDN String.Empty
是只读的,因此更改它应该是不可能的,并且编译应以"无法分配静态只读字段"或类似错误结束.
我认为Base Class Library程序集以某种方式受到保护和签名,以及什么可以防止这种攻击.下次有人可能会更改System.Security.Cryptography或其他关键类.
我认为基类库程序集是在安装.NET之后由NGEN编译的,因此更改String类的字段应该需要高级黑客并且要困难得多.
然而,这段代码编译和工作.有人可以解释我的推理有什么问题吗?
Ben*_*igt 52
无法分配静态只读字段
你没有分配给它.您正在System.Reflection
命名空间中调用公共函数.没有理由让编译器抱怨这一点.
此外,typeof(string).GetField("Empty")
可以使用用户输入的变量,但是编译器无法确定是否GetField
最终会告诉参数"Empty"
.
我认为您希望Reflection
看到该字段已标记initonly
并在运行时抛出错误.我可以看到你为什么会这么想,但对于白盒测试,甚至写入initonly
字段都有一些应用.
NGEN没有效果的原因是你不是在这里修改任何代码,只修改数据.与任何其他语言一样,数据与.NET一起存储在内存中.本机程序可以使用readonly内存部分来处理字符串常量之类的东西,但是指向字符串的指针通常仍然是可写的,这就是这里发生的事情.
请注意,您的代码必须以完全信任的方式运行才能以这种有问题的方式使用反射.此外,更改只影响一个程序,这不是您认为的任何类型的安全漏洞(如果您在流程中以完全信任的方式运行恶意代码,那么设计决策是安全问题,而不是反射) .
进一步注意,initonly
里面的字段值是mscorlib.dll
.NET运行时的全局不变量.打破它们之后,你甚至无法可靠地测试不变量是否被破坏,因为检查System.String.Empty的当前值的代码也已经破坏,因为你违反了它的不变量.开始违反系统不变量,不能依赖任何东西.
通过在.NET规范中指定这些值,它使编译器能够实现一大堆性能优化.只是一个简单的就是那个
s == System.String.Empty
Run Code Online (Sandbox Code Playgroud)
和
(s != null) && (s.Length == 0)
Run Code Online (Sandbox Code Playgroud)
是相同的,但后者更快(相对而言).
编译器也可以确定
if (int.Parse(s) > int.MaxValue)
Run Code Online (Sandbox Code Playgroud)
永远不会是真的,并且生成一个无条件跳转到else块(它仍然必须调用Int32.Parse
具有相同的异常行为,但可以删除比较).
System.String.Empty
在BCL实现中也广泛使用.如果你覆盖它,可能会发生各种疯狂的事情,包括程序外泄漏的损坏(例如,你可能会写一个名字是用字符串操作构建的文件......当字符串断开时,你可能会覆盖错误的文件)
并且.NET版本之间的行为可能很容易不同.通常,当找到新的优化机会时,它们不会被反向移植到以前版本的JIT编译器(即使它们是,也可能在实现backport之前进行安装).特别是.String.Empty
.NET 2.x与Mono和.NET 4.5+之间的相关优化明显不同.
Eri*_*ert 42
代码编译,因为代码的每一行都是完全合法的C#.您认为哪条特定行应该是语法错误?那里没有代码行分配给只读字段.有一行代码调用Reflection中的一个方法,该方法分配给一个只读字段,但是已经编译了,最终破坏安全性的东西甚至没有用C#编写,它是用C++编写的.它是运行时引擎本身的一部分.
代码运行成功,因为完全信任意味着完全信任.您在完全信任环境中运行代码,并且由于完全信任意味着完全信任,运行时假设您在执行此愚蠢危险时知道自己在做什么.
如果您尝试在部分受信任的环境中运行代码,那么您将看到Reflection抛出"您不允许这样做"的异常.
是的,组件已签名,以及诸如此类的东西.如果您正在运行完全信任的代码,那么他们可以根据需要随意使用这些程序集.这就是完全信任的含义.部分受信任的代码无法做到这一点,但完全受信任的代码可以做任何你能做的事情.只对您真正信任的代码授予完全信任,才能代表您做疯狂的事情.
Bru*_*oLM 20
反射允许你违抗物理定律做任何事情.您甚至可以设置私有成员的值.
反射不遵循规则,您可以在MSDN上阅读它.
另一个例子:我可以使用反射更改C#中的私有只读字段吗?
如果您使用的是Web应用程序,则可以设置应用程序的信任级别.
level="[Full|High|Medium|Low|Minimal]"
Run Code Online (Sandbox Code Playgroud)
这些是信任级别的限制,与MSDN相关,在中等信任下,您限制反射访问.
编辑:不要运行除完全信任之外的Web应用程序,这是ASP.NET团队的直接建议.要保护您的应用程序,请为每个网站创建一个应用程
此外,不建议使用反射来解决所有问题.它有合适的地方和时间使用.
前面没有提到的一点:这段代码在不同的.net实现/平台上导致不同的行为.实际上在Mono上它什么都没有返回:参见IDE One(Mono 2.8),我的本地Mono 2.6.7(linux)产生相同的"输出".
我还没有看过低级代码,但我认为它是编译器特定的,正如PRASHANT P或运行时环境所提到的那样.
UPDATE
在Windows上运行Mono编译的exe(MS Dotnet 4)会产生
evil
equal
Run Code Online (Sandbox Code Playgroud)
在Linux上运行Windows编译的exe是不可能的(Dotnet 4 ...)所以我用Dotnet 2重新编译(它仍然说在Windows上邪恶和相同).有"无"输出.当然必须至少"\n"
从第一个开始WriteLine
,事实上它就在那里.我将输出传送到文件并启动我的hexeditor以查看单个字符0x0A
.
简而言之:它似乎是特定于运行时环境的.