четверг, 10 ноября 2022 г.

Почему все функции Windows начинаются с бессмысленной инструкции MOV EDI, EDI?

Это перевод Why do Windows functions all begin with a pointless MOV EDI, EDI instruction? Автор: Реймонд Чен.

Если вы посмотрите на ассемблерный код функций внутри DLL Windows, вы обнаружите, что они начинаются с бессмысленной инструкции MOV EDI, EDI. Эта инструкция копирует регистр в самого себя и не обновляет флаги, что совершенно не имеет смысла. Тогда зачем она там?

Это точка для внедрения кода.

Инструкция MOV EDI, EDI представляет собой двухбайтовый NOP (операция, которая ничего не делает). Двух байт как раз достаточно для вставки инструкции перехода, чтобы функцию можно было обновить на лету. Предполагается, что инструкция MOV EDI, EDI будет заменена двухбайтовой инструкцией JMP $-5 (перейти по адресу, записанному на пять байт выше) для передачи управления на пять байтов из так называемого "patch space" (места для патчей), которое вставляется непосредственно перед началом функции. Пять байтов достаточно для полной инструкции перехода, которая может передать управление на любую функцию где-то ещё в адресном пространстве.

Хотя пять байтов из места для патчей до начала функции состоят из пяти однобайтовых инструкций NOP, точка входа функции использует одну двухбайтовую NOP.

Почему бы не использовать Detours для оперативного обновления функции, тогда место для патчей будет вообще не нужно.

Проблема с Detours во время выполнения программы заключается в том, что вы никогда не можете быть уверены, что в момент внесения исправлений Detours другой поток прямо сейчас не выполняет инструкцию, затрагивающую первые пять байтов функции (и вам нужно програрантировать, чтобы в программе не было переходов на эти инструкции, начинающаяся со смещения с 1 по 4). Вы можете захотеть попробовать обойти проблему, приостановив все потоки во время исправления, но это все ещё не помешает кому-либо выполнить CreateRemoteThread после того, как вы решили, что успешно приостановили все потоки.

Почему бы просто не использовать две инструкции NOP в точке входа?

Ну, поскольку инструкция NOP использует один тактовый цикл и один конвейер, поэтому две из них будут использовать два такта и два конвейера (инструкции, скорее всего, будут парными, по одной в каждом канале, поэтому совместное выполнение займет один такт). С другой стороны, инструкция MOV EDI, EDI использует один тактовый цикл и один канал (т.е. на практике инструкция занимает один канал, а другой остается доступным для параллельного выполнения другой инструкции. Можно сказать, что инструкция выполняется за половину цикла). Как бы вы ни прикидывали, инструкция MOV EDI, EDI выполняется в два раза быстрее двух инструкций NOP.

С другой стороны, пять NOP, вставленных перед началом функции (из места для патчей), никогда не выполняются, поэтому не имеет значения, что именно вы используете для заполнения. Это могли быть пять любых произвольных (мусорных) байт, кому какое дело.

Но гораздо важнее, чем подсчет циклов, является то, что использование двухбайтового NOP позволяет избежать вышеупомянутой проблемой с Detours: если в коде использовались бы две однобайтовые инструкции NOP, то есть риск, что вы установите свой патч в то же самое время когда поток завершает выполнение первого однобайтового NOP и собирается начать выполнение второго однобайтового NOP, в результате чего поток будет трактовать вторую половину вашего JMP $-5 как начало новой инструкции.

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

Читать далее: Почему x86-64 функции Windows НЕ начинаются с бессмысленной инструкции MOV EDI, EDI?

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

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

Примечание. Отправлять комментарии могут только участники этого блога.