Каковы последствия запроса API отражения для перезаписи системы.Строка.Пусто?


я наткнулся на этот код:

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

 }

и мне интересно, как вообще можно скомпилировать этот кусок кода. Мои рассуждения таковы:

согласно MSDN String.Empty только для чтения, поэтому изменение его должно быть невозможно, а компиляция должна заканчиваться "статическое поле только для чтения не может быть назначено" или аналогичной ошибкой.

Я думал, что сборки библиотеки базовых классов каким-то образом защищены и подписаны, и что-то еще, чтобы предотвратить именно такую атаку. В следующий раз кто-то может изменить систему.Безопасность.Криптография или другой критический класс.

Я думал, что сборки библиотеки базовых классов компилируются NGEN после установки .NET, поэтому изменение полей класса String должно требовать расширенного взлома и быть намного сложнее.

и все же этот код компилируется и работает. Может кто-нибудь объяснить, что не так с моими рассуждениями?

5 61

5 ответов:

статическое поле только для чтения, не может быть назначен

вы не назначаете его. Вы вызываете публичные функции в System.Reflection пространство имен. Нет причин для компилятора жаловаться на это.

кроме того, typeof(string).GetField("Empty") можно использовать переменные, введенные пользователем вместо этого, нет никакого способа для компилятора, чтобы сказать во всех случаях, является ли аргумент к GetField будут "Empty".

я думаю, что вы хотите Reflection чтобы увидеть, что поле помечено initonly и выдаст ошибку во время выполнения. Я понимаю, почему вы ожидаете этого, но для тестирования белого ящика даже пишете initonly поля имеет некоторое применение.

причина, по которой NGEN не имеет никакого эффекта, заключается в том, что вы не изменяете здесь никакой код, только данные. Данные хранятся в памяти с .NET так же, как и с любым другим языком. Собственные программы могут использовать разделы памяти только для чтения для таких вещей, как строковые константы, но указатель на строку обычно остается доступно для записи, и именно это происходит здесь.

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


далее отметим, что значения initonly поля внутри mscorlib.dll являются глобальными инвариантами среды выполнения .NET. После их взлома вы даже не можете достоверно проверить, был ли нарушен инвариант,потому что код для проверки текущего значения системы.Строка.Пустота также сломалась, потому что вы нарушили ее инварианты. Начните нарушать системные инварианты, и ни на что нельзя положиться.

указывая эти значения внутри спецификаций .NET, он позволяет компилятору реализовать целую кучу оптимизаций производительности. Просто простой в том, что

s == System.String.Empty

и

(s != null) && (s.Length == 0)

эквивалентны, но последний гораздо быстрее (условно говоря).

и компилятор может определить, что

if (int.Parse(s) > int.MaxValue)

никогда не является истинным и генерирует безусловный переход к блоку else (он все еще должен вызывать Int32.Parse чтобы иметь такое же поведение исключения, но сравнение может быть удалено).

System.String.Empty также широко используется внутри реализаций BCL. Если вы перепишите его,всякие сумасшедшие вещи могут произойти, в том числе повреждения, которые протекают за пределами вашей программы (например, вы можете записать в файл, имя которого строится с помощью строковых манипуляций... когда строка ломается, вы можете перезаписать неправильный файл)


и поведение может легко отличаться между версиями .NET. Обычно, когда новые возможности оптимизации найдены, они не возвращаются к предыдущим версиям JIT-компилятора (и даже если они были, могут быть установки до того, как был реализован backport). Особенно. String.Empty-связанные оптимизации заметно отличаются между .NET 2.X и моно .Чистая 4.5+.

код компилируется, потому что каждая строка кода является совершенно законным в C#. Какая конкретная строка, по вашему мнению, должна быть синтаксической ошибкой? Там нет строки кода, которая назначает поле только для чтения. Есть строка кода, которая вызывает метод в отражении, который назначает поле только для чтения, но это уже скомпилировано, и в конечном итоге то, что нарушает безопасность, даже не было написано на C#, оно было написано на C++. Это часть самого выполнения.

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

Если вы попытаетесь запустить свой код в частично доверенной среде, вы увидите, что отражение выдает исключение "вам не разрешено это делать".

и да, сборки подписываются и еще много чего. Если вы используете полностью доверенный код, то, конечно, они могут прокручивать эти сборки столько, сколько захотят. вот что значит полное доверие. частично доверенный код не может этого сделать, но полностью доверенный код может сделать все, что вы можете сделать. только предоставьте полное доверие коду, которому вы действительно доверяете, чтобы не делать сумасшедшие вещи от вашего имени.

рефлексия позволяет бросьте вызов законам физики сделать что-нибудь. Вы даже можете установить значение частных членов.

отражение не следует правилам,вы можете прочитать об этом на MSDN.

еще пример: могу ли я изменить личное поле только для чтения в C# с помощью отражения?


Если вы находитесь в веб-приложении, вы можете установить уровень доверия вашего приложение.

level="[Full|High|Medium|Low|Minimal]"

это ограничение уровня доверия, соответственно MSDN, при среднем доверии вы ограничиваете доступ к отражению.

Edit:НЕ запустите веб-приложение, отличное от полного доверия, это прямая рекомендация от ASP.NET команда. Для защиты вашего приложения создайте один пул приложений для каждого веб-сайта.


кроме того, не рекомендуется выходить из себя, используя отражение для всего. Он имеет правильное место и время для использования.

точка, о которой никто не упоминал здесь раньше: этот фрагмент кода вызывает разное поведение на разных реализациях/платформах .net. На самом деле на моно он вообще ничего не возвращает:см. IDE One (Mono 2.8), мой локальный моно 2.6.7 (linux) производит тот же "выход".

Я еще не смотрел на код низкого уровня, но я полагаю, что это компилятор, как упоминалось PRASHANT P или во время выполнения окружающая среда.

обновление

запуск Mono compiled exe на Windows (MS Dotnet 4) производит

evil 
equal

запуск Windows compiled exe на linux был невозможен (Dotnet 4...) поэтому я перекомпилировал с Dotnet 2 (он все еще говорит зло и равной в Windows). Существует" нет " выход. Конечно, должен быть хотя бы "\n" С первого WriteLine, на самом деле он есть. Я передал вывод в файл и начал свой hexeditor, чтобы посмотреть на один символ 0x0A.

короче говоря: похоже, что среда выполнения конкретные.

только для чтения только исполнение

на уровне компилятора

может быть изменен на текущем уровне ниже