Почему этот код работает без небезопасного ключевого слова? - PullRequest
19 голосов
/ 27 апреля 2009

В ответ на свой спорный вопрос , 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;
   }

И тогда вы можете делать такие вещи, как изменение «неизменной» строки. Следующий код печатает «бар» на моей машине:

 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 необходимо для таких вещей. Почему это не нужно здесь? Значит ли это, что чисто управляемый "безопасный" код вообще не совсем безопасен?

Ответы [ 3 ]

12 голосов
/ 27 апреля 2009

ОК, это противно ... опасность использования союза. Это может сработать, но это не очень хорошая идея - я думаю, я бы сравнил это с рефлексией (где вы можете делать большинство вещей). Мне было бы интересно посмотреть, работает ли это в среде с ограниченным доступом - если это так, это может представлять большую проблему ...


Я только что протестировал его без флага «Полное доверие», и среда выполнения отклоняет его:

Не удалось загрузить тип MemoryAccess из сборки 'ConsoleApplication4, Версия = 1.0.0.0, Культура = нейтральная, PublicKeyToken = null ', потому что объекты перекрываются со смещением 0 и сборка должна быть проверяемой.

И чтобы иметь этот флаг, вам уже нужно высокое доверие - так что вы уже можете делать более неприятные вещи. Строки - это немного другой случай, потому что они не являются обычными объектами .NET - но есть и другие примеры способов их изменения - хотя подход «объединение» интересен. Для другого хакерского пути (с достаточным доверием):

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
5 голосов
/ 27 апреля 2009

Упс, я запутался unsafe с fixed. Вот исправленная версия:

Причина того, что образец кода не требует пометки ключевым словом unsafe, заключается в том, что он не содержит указателей (см. Цитату ниже, почему это считается небезопасным). Вы совершенно правы: «безопасный» лучше назвать «дружественным во время выполнения». Для получения дополнительной информации по этой теме я отсылаю вас к Дону Боксу и Крису Селлсу Essential .NET

Цитировать MSDN,

В общеязыковой среде выполнения (CLR), небезопасный код упоминается как непроверяемый код. Небезопасный код в C # не обязательно опасно; это просто код, безопасность которого не может быть проверено CLR. CLR будет поэтому выполнять небезопасный код только в том случае, если это в полностью доверенной сборке. Если вы используете небезопасный код, это ваш ответственность за то, чтобы ваш код не представляет угрозы безопасности или ошибки указателя.

Различие между фиксированным и небезопасным состоит в том, что фиксированный не дает CLR перемещать объекты в памяти, так что к объектам вне времени выполнения можно безопасно обращаться к ним, тогда как небезопасный - это как раз противоположная проблема: в то время как CLR может гарантировать правильное разрешение для ссылки dotnet, это не может сделать это для указателя. Вы можете вспомнить, как разные микрософты рассказывали о том, что ссылка не является указателем, и именно поэтому они так суетятся из-за тонкого различия.

0 голосов
/ 27 апреля 2009

Вы все еще отказываетесь от «управляемого» бита. Существует основополагающее предположение, что если вы можете сделать это, то вы знаете, что делаете.

...