19

回答他自己有争议的问题时,Mash已经说明您不需要“不安全”关键字来直接读取和写入任何 .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# 代码中)不安全关键字是执行此类操作所必需的。为什么这里不需要?这是否意味着纯托管的“安全”代码根本不安全?

4

3 回答 3

12

好吧,这很讨厌......使用工会的危险。这可能有效,但不是一个好主意 - 我想我会将它与反射(你可以做大部分事情)进行比较。我很想看看这是否适用于受限访问环境 - 如果是这样,它可能代表一个更大的问题......


我刚刚在没有“完全信任”标志的情况下对其进行了测试,运行时拒绝了它:

无法从程序集“ConsoleApplication4,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”加载类型“MemoryAccess”,因为对象在偏移量 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
于 2009-04-27T09:04:40.900 回答
5

哎呀,我糊涂unsafefixed。这是一个更正的版本:

示例代码不需要使用unsafe关键字标记的原因是它不包含指针(请参阅下面的引用以了解为什么这被认为是不安全的)。您说得很对:“安全”最好称为“运行时友好”。有关此主题的更多信息,请参阅 Don Box 和 Chris Sells Essential .NET

引用 MSDN,

在公共语言运行时 (CLR) 中,不安全的代码被称为无法验证的代码。C# 中的不安全代码不一定是危险的;它只是 CLR 无法验证其安全性的代码。因此,如果 CLR 位于完全受信任的程序集中,则 CLR 只会执行不安全的代码。如果您使用不安全的代码,您有责任确保您的代码不会引入安全风险或指针错误。

fixed 和 unsafe 之间的区别在于,fixed 阻止 CLR 在内存中移动东西,以便运行时之外的东西可以安全地访问它们,而 unsafe 则完全相反:而 CLR 可以保证正确解析dotnet 引用,它不能为指针这样做。您可能还记得各种关于引用如何不是指针的微软人,这就是为什么他们对细微的区别如此大惊小怪的原因。

于 2009-04-27T09:40:04.027 回答
0

您仍然选择退出“托管”位。有一个基本的假设是,如果你能做到这一点,那么你就知道你在做什么。

于 2009-04-27T09:07:00.363 回答