Hotpatch. Патчим память ядра Windows
В версии Windows Server 2003 SP1 была представлена технология, называемая «хотпатчингом». То есть обновление системы «на лету», без необходимости ее перезагрузки. Технология позволяет устанавливать патчи на отдельные функции (как пользовательские, так и режима ядра). В версии 8.1 возможность установки хотпатчей была ликвидирована. Примечательно, что использовать данную возможность можно из user-mode'a даже в случае kernel-mode патчей. Стоит отметить, что такого рода патчи выпускались непродолжительное время и только под Windows Server 2003 SP1.
Рассмотрим конкретный пример патча: Security Update KB914389. Данный апдейт содержит несколько патчей функций из драйверов mrxsmb.sys и rdbss.sys.
В составе патча для каждого драйвера находятся два файла: драйвер, которым будет заменен патчируемый после перезагрузки, и загадочный файл с расширением *.hp.sys, который является обыкновенным драйвером. В нем должна присутствовать секция с названием ".hotp1 " (два пробела в конце обязательны). Рассмотрим подробнее сам процесс патчинга.
Для начала, нужно ввести понятие hotpatchable функции. Это такая функция, первая инструкция которой является двухбайтовой инструкцией «mov edi, edi», а перед началом функции находятся пять nop'ов. Так же различают semi-hotpatchable функции — те, у которых первая инструкция двухбайтовая, но не mov edi, edi.
Инструкция «mov edi, edi» введена в hotpatchable функции для того, чтобы обезопасить хотпатч на мультипроцессорных системах. Например, если первая инструкция была бы однобайтовая, то мог получиться следующий результат: один из потоков входит в патчируемую функцию и выполняет первую команду. В то же время другой поток устанавливает патч на данную функцию, в результате чего первый оказывается посреди двухбайтовой инструкции «jmps -5», что приведет к падению системы.
Суть технологии такова: сначала происходит загрузка драйвера *.hp.sys в память с помощью функции MmLoadSystemImage. Далее считываются все характеристики патча, которые находятся в секции ".hotp1 ". Примерная структура, представляющая заголовок патча представлена ниже. Структура взята отсюда, в абсолютной ее точности уверенности нет, но разногласий с дизасмом не обнаружено.
В случае, если патч накладывается на hotpatchable функцию x86 системах первая инструкция патчируемой функции заменяется на короткий jmp — jmps -5 (оппкод ebf9). Он переводит поток управления на пять байт назад, где помещается пятибайтовая инструкция jmp m32, то есть дальний jmp на адрес, указанный в хотпатче. В остальных случаях, независимо от типа патчируемой функции, проверяется разница в адресах target — модуля и загруженного *.hp.sys. Патч устанавливается только в случае, если модуль загрузился в пределах +-2GB от target-модуля (ограничивается размером операнда «ff 25» jmp'а). Первая инструкция заменяется на шестибайтовую инструкцию «jmp m32», на rip-relative адрес до target функции.
А теперь рассмотрим как же можно запустить процесс хотпатчинга.
Из ntdll.dll экспортируется функция NtSetSystemInformation, которая работает аналогично часто используемой в свое время функции NtQuerySystemInformation, то есть принимает на вход одним из аргументов SystemInformationClass, который определяет дальнейшее поведение функции. Если передать функции SystemInformationClass = 69, то, провалившись в kernel-mode посредством syscall'a, управление передается функции MmHotPatchRoutine.
Там происходит загрузка в память *.hp файла и дальнейшая передача управления на функцию MiPerformHotpatch. В ней, кроме прочего, происходит поиск секции ".hotp1 " в загруженном модуле, вызов функции RtlFindRtlPatchHeader, а так же поиск целевого модуля в памяти посредством перебора всех сессий в системе. Далее происходит передача управления на функцию RtlInitializeHotpatch.
Не будем углубляться в функции RtlpApplyRelocationFixups и RtlpValidateTargetRanges, скажем только, что с помощью последней можно убедиться, что целевая функция является hotpatchable.
В функции RtlReadHookInformation происходит, собственно, установка патчей.
Структура каждого патча представлена ниже.
Далее два раза происходит вызов функции RtlpReadSingleHookInformation, в которой первый раз происходит определение размера трамплина (формат и размер команды «jmp»), а второй раз непосредственно установка патча.
Так же в этой функции происходит проверка расстояния между загруженным и целевым модулем. Ели оно больше 2GB, то установка патча не происходит.
Допустим, мы устанавливаем патч на Windows 7 x64. Попробуем реализовать патч какой — либо функции. Например, можно выбрать функцию FatCommonWrite подсистемы fastfat, которая вызывается при записи каких — либо данных на fat32 флешку. Для начала нужно написать драйвер, который будет содержать в себе заполненную секцию ".hotp1 " и новую функцию.
Теперь нужно написать приложение, которое вызовет процесс хотпатчинга. Для этого напишем обычное Win32 приложение.
Данное приложение запустит процесс хотпатчинга. Останется только вставить fat32 флешку в компьютер, записать на нее что — нибудь и лицезреть немногословный BSOD.
FatCommonWrite до патча:
FatCommonWrite после патча:
В заключение стоит отметить, что хотя данная технология и не является потенциальной уязвимостью, но все равно представляет собой достаточно интересный способ патчинга памяти, принадлежащей ядру Windows.