Подтвердить что ты не робот

Почему этот код работает без ключевого слова unsafe?

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

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

 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, развращая ссылки на объекты той же техникой.

Вопрос: Я думал, что (в чистом управляемом коде С#) ключевое слово unsafe было необходимо для выполнения подобных действий. Почему здесь не нужно? Означает ли это, что чистый управляемый "безопасный" код вообще не безопасен?

4b9b3361

Ответ 1

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


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

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

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

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

Ответ 2

Упс, я запутался unsafe с помощью fixed. Здесь скорректированная версия:

Причина, по которой код примера не требует тегирования с ключевым словом unsafe, заключается в том, что он не содержит указателей (см. ниже цитату, почему это считается небезопасным). Вы совершенно правы: "безопасный" лучше назвать "дружественным во времени". Для получения дополнительной информации по этой теме я отсылаю вас к Don Box и Chris Sells Essential.NET.

Чтобы указать MSDN,

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

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

Ответ 3

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