在an answer到他自己的controversial question,Mash表明您不需要“unsafe”关键字来直接读取和写入任何.NET对象实例的字节。您可以声明以下类型:
[StructLayout(LayoutKind.Explicit)]
struct MemoryAccess
{
[FieldOffset(0)]
public object Object;
[FieldOffset(0)]
public TopBytes Bytes;
}
class TopBytes
{
public byte b0;
public byte b1;
public byte b2;
public byte b3;
public byte b4;
public byte b5;
public byte b6;
public byte b7;
public byte b8;
public byte b9;
public byte b10;
public byte b11;
public byte b12;
public byte b13;
public byte b14;
public byte b15;
}
然后你可以做一些事情,比如改变一个“不可变的”字符串。以下代码在我的机器上打印“bar”:
string foo = "foo";
MemoryAccess mem = new MemoryAccess();
mem.Object = foo;
mem.Bytes.b8 = (byte)'b';
mem.Bytes.b10 = (byte)'a';
mem.Bytes.b12 = (byte)'r';
Console.WriteLine(foo);
您也可以通过使用相同的技术破坏对象引用来触发AccessViolationException。
问题:我认为(在纯托管C#代码中)unsafe关键字是做这样的事情所必需的。为什么这里没有必要? 这是否意味着纯粹的托管“安全”代码根本不安全?
答案 0 :(得分:12)
我刚刚测试它没有“完全信任”标志,运行时拒绝它:
无法加载“MemoryAccess”类型 从程序集'ConsoleApplication4, 版本= 1.0.0.0,文化=中立, PublicKeyToken = null'因为对象 重叠在偏移0和 装配必须是可验证的。
要拥有这面旗帜,你已经需要高度信任 - 所以你已经可以做更多令人讨厌的事了。字符串是一个稍微不同的情况,因为它们不是正常的.NET对象 - 但是还有其他一些方法可以改变它们 - 但“联合”方法是一个有趣的方法。对于另一种hacky方式(有足够的信任):
string orig = "abc ", copy = orig;
typeof(string).GetMethod("AppendInPlace",
BindingFlags.NonPublic | BindingFlags.Instance,
null, new Type[] { typeof(string), typeof(int) }, null)
.Invoke(orig, new object[] { "def", 3 });
Console.WriteLine(copy); // note we didn't touch "copy", so we have
// mutated the same reference
答案 1 :(得分:5)
哎呀,我跟unsafe
混淆了fixed
。这是一个更正版本:
示例代码不需要使用unsafe
关键字进行标记的原因是它不包含指针(请参阅下面的报价,了解为什么这被认为是不安全的)。你是完全正确的:“安全”可能更好地被称为“运行时友好”。有关此主题的更多信息,请参阅Don Box和Chris Sells Essential .NET
引用MSDN,
在公共语言运行库(CLR)中, 不安全的代码称为 无法验证的代码。 C#中的不安全代码 不一定是危险的;它是 只是安全不可能的代码 由CLR验证。 CLR会 因此,只有执行不安全的代码 它是一个完全可信的组件。如果 你使用不安全的代码,这是你的 有责任确保你的 代码不会带来安全风险 或指针错误。
固定和不安全之间的区别在于固定阻止CLR在内存中移动东西,因此运行时之外的东西可以安全地访问它们,而不安全则恰恰相反的问题:CLR可以保证正确对于一个dotnet引用的分辨率,它不能为指针执行此操作。你可能还记得各种关于引用如何不是一个指针的微软,这就是为什么他们对一个微妙的区别做了如此大惊小怪。
答案 2 :(得分:0)
你仍然选择退出'托管'位。有一个潜在的假设,如果你能做到那么你知道你在做什么。