Зачастую возникают потребности изучения процесса загрузки операционной системы Windows на уровне исходного кода. Ну а тут уж без внесения изменений в код просто не обойтись.. и в этом самом месте нас ждет разочарование. Дело в том, что если вносить изменения в код модулей bootmgr, winload.exe и файлы драйверов, участвующих в процессе начальной загрузки, то процесс запуска операционной системы начинает "вылетать" с ошибками, "валиться" в синий экран и режим восстановления. Логично, ведь мы столкнулись с проверкой целостности bootmgr и winload, которая препятствует модификации исполняемых образов, задействованных в цепочке запуска. Подобный механизм впервые появился в линейке операционных систем Windows начиная с версии Vista и теперь приобрел перманентно-устойчивый вид. Для самописных драйверов разработчики оставили возможность загрузки в тестовом режиме (флаг TESTSIGNING), тем не менее остаются условия, при которых внесение изменений в код компонентов оканчивается отказом в запуске ОС. Исследователям архитектуры Windows этот факт существенно портит настроение, поэтому сегодня мы разберем несколько нестандартный метод отключения проверки целостности bootmgr и winload.
Для подавления проверки целостности мы будем патчить код модулей таким образом, чтобы алгоритмы внутренних функций проверки хэшей файлов всегда выполнялись по ветвлению типовой (нормальной) загрузки. В качестве учебного материала статьи будет использована операционная система Windows 7 SP1 Professional RUS x86 в нормальном (legacy, PC/AT-MBR) режиме загрузки. Не смотря на то, что отключение проверки целостности в 32-битной системы имеет меньше смысла нежели в x64-версии, приведенное в данной статье решение является универсальным, поскольку логика работы внутренних функций проверки целостности аналогична (даже для режима загрузки UEFI), за исключением, разумеется, исходного кода.
- %SystemRoot%\System32\Boot\Winload.exe;
- %SystemRoot%\System32\Winload.exe;
- Bootmgr (находящийся на скрытом системном разделе/разделе EFI);
Итак, нам потребуется:
- Интерактивный дизассемблер IDA
- Шестнадцатеричный редактор с возможностью поиска/замены шестнадцатеричных значений (ваш любимый)
- Утилита bmzip
- Утилита BOOTMGR Recompiler v2
- PE-редактор LordPE
Bootmgr
В процессе загрузки операционной системы Windows, после окончания этапов MBR и PBR, управление передается на код менеджера загрузки Bootmgr. Что бы сам менеджер загрузки в случае модификации у нас не "брыкался" разнообразными ошибками контрольных сумм, нам требуется его пропатчить:
- Для начала нам необходимо получить файл bootmgr со специального "скрытого" системного раздела. Метод описан в этой статье, тем не менее я приведу здесь краткую последовательность действий: запускаем оснастку Управление дисками, выделяем (маркируем) первый раздел диска (размер ~100-300Мб), щелкаем правой кнопкой мыши - выбираем пункт меню Изменить букву диска или путь к диску.. и назначаем литеру.
- В системе появляется (становится доступен) новый диск. Открываем, настраиваем представление проводника на показ скрытых файлов, папок и дисков, отменяем сокрытие защищенных системных файлов.
- В корне обнаруживаем сжатый файл Bootmgr.
- Распаковываем его при помощи консольной утилиты bmzip (ссылка выше):
bmzip bootmgr bootmgr.exe
В целевом каталоге обнаруживаем новый только что распакованный файл bootmgr.exe.
- Дизассемблируем полученный модуль bootmgr.exe при помощи IDA. Обязательно с работоспособной возможностью по выкачиванию символов с сервера Microsoft.
- В интерфейсе IDA, идем в Options - General - вкладка Disassembly, выставляем значение параметра Number of opcode bytes в
8
. Это нужно для отображения в листинге опкодов инструкций. - В основном окне дизассемблированного исходного кода откручиваем на начало листинга, ставим курсор в начало, затем жмем Alt+T и вводим в строку поиска ImgpValidateImageHash (можно без учета регистра символов), жмем OK.
- Для дальнейшего поиска можно использовать комбинацию Ctrl+T. Перемещаясь по исходному коду находим точку входа функции (не путать с вызовами функции
call _ImgpValidateImageHash
) - Найдя таким образом функцию, перемещаемся в конец её кода и видим примерно следующее:
123456789101112. . ..text:00421ED9 loc_421ED9:.text:00421ED9.text:00421ED9 8B C7 mov eax, edi.text:00421EDB 5F pop edi.text:00421EDC 5E pop esi.text:00421EDD 5B pop ebx.text:00421EDE 8B E5 mov esp, ebp.text:00421EE0 5D pop ebp.text:00421EE1 C2 10 00 retn 10h.text:00421EE1 _ImgpValidateImageHash@20 endp. . .
- Функция
ImgpValidateImageHash
выполняет проверку контрольной суммы (хэш) загружаемых модулей. Для того, что бы обойти разнообразные проверки собственной целостности и загружаемого в дальнейшем модуля Winload.exe, необходимо заставить функцию возвращать нулевое значение (согласно соглашению о вызовах в регистреeax
) в любой случае. - На данный момент в приведенном исходном коде мы можем наблюдать сигнатуру для поиска и замены:
18B C7 5F 5E 5B 8B E5 5D C2 10 00
Конкретно в вашем случае она может быть иной, поскольку все зависит от текущей ревизии файла bootmgr;
- Скачиваем и запускаем шестнадцатеричный редактор. Открываем в нем наш файл bootmgr.exe.
- Поиском по шестнадцатеричному значению находим сигнатуру. Нужно поменять найденный нами код так, что бы в регистре
eax
возвращалось значение0
. В оригинале мы видим командуmov eax, edi
(опкоды 8B C7), которая выполняет присвоениеeax
=edi
. Давайте заменим её наxor eax, eax
(опкоды 33 C0), которая обнуляет регистрeax
. Для этого в редакторе байты 8B C7 меняем на 33 C0. Сохраняем изменения, закрываем редактор. - Теперь нам необходимо преобразовать образ bootmgr.exe обратно в сжатый bootmgr. Для этого используем утилиту BOOTMGR Recompiler v2 (ссылка выше).
- Помещаем получившийся сжатый файл bootmgr в корень скрытого раздела (который был у нас подмаплен в систему в начале). Атрибуты на скрытый менять не надо!
Winload.exe
Но это еще не все, поскольку после того, как код менеджера загрузки Bootmgr находится на завершающей стадии исполнения, им подгружается загрузчик следующей стадии - так называемый загрузчик операционной систем под названием Winload.exe. Данный модуль так же имеет привычку проверять целостность ключевых файлов, поэтому нам необходимо обновить и его код тоже. Дабы не описывать несколько раз одно и то же, условимся, что шаги предыдущего раздела актуальны и для текущего.
Метод №1
- По вышеописанной методике ищем функцию
ImgpValidateImageHash
. - В конце функции находим примерно такой вот фрагмент кода:
123456789101112. . ..text:00429812 loc_429812:.text:00429812.text:00429812 8B 44 24 18 mov eax, [esp+70h+var_58].text:00429816 5F pop edi.text:00429817 5E pop esi.text:00429818 5B pop ebx.text:00429819 8B E5 mov esp, ebp.text:0042981B 5D pop ebp.text:0042981C C2 10 00 retn 10h.text:0042981C _ImgpValidateImageHash@20 endp. . . - В шестнадцатеричном редакторе находим сигнатуру и меняем (выделены цветом) четыре байта 8B 44 24 18 на 33 C0 90 90, что будет эквивалентно серии команд:
123xor eax, eaxnopnop
Метод №2
- По вышеописанной методике ищем функцию
OslInitializeCodeIntegrity
. - Смотрим точку входа функции:
1234567891011121314151617181920212223. . ..text:004045BE ; =============== S U B R O U T I N E =======================================.text:004045BE.text:004045BE ; Attributes: bp-based frame.text:004045BE.text:004045BE ; __stdcall OslInitializeCodeIntegrity(x).text:004045BE _OslInitializeCodeIntegrity@4 proc near.text:004045BE.text:004045BE var_10 = dword ptr -10h.text:004045BE var_C = dword ptr -0Ch.text:004045BE var_8 = dword ptr -8.text:004045BE var_2 = byte ptr -2.text:004045BE var_1 = byte ptr -1.text:004045BE arg_0 = dword ptr 8.text:004045BE.text:004045BE 8B FF mov edi, edi.text:004045C0 55 push ebp.text:004045C1 8B EC mov ebp, esp.text:004045C3 83 EC 10 sub esp, 10h.text:004045C6 53 push ebx.text:004045C7 56 push esi.text:004045C8 57 push edi. . . - В шестнадцатеричном редакторе находим сигнатуру и меняем (выделены цветом) первые три байта 8B FF 55 на B0 01 C3, что будет эквивалентно серии команд:
12mov al, 1ret
Корректировка контрольной суммы файлов
Имеется еще один тонкий момент. Возьмите в привычку, что если вы модифицируете winload.exe или ntoskrnl.exe, или любой драйвер начальной загрузки, то после всех сделанных изменений не забудьте пересчитать и поменять контрольную сумму (поле Checksum
заголовка PE). Осуществляется это посредством утилиты LordPE или любого аналогичного редактора PE-модулей. Причина простая: где-то в коде Winload.exe имеется еще одна проверка (которую я пока не смог обнаружить), которая верифицирует контрольную сумму модуля, и при несовпадении уходит на процесс восстановления системы.
Заключение
После модификации исполняемых бинарных образов bootmgr и winload.exe, мы имеем возможность вносить в их код любые требуемые изменения без риска наскочить на проверку и получить отказ в загрузке операционной системы. Тем не менее, это еще далеко не все, ведь дальнейшая загрузка подразумевает работу с ядром операционной системы, поэтому мы переходит к следующему этапу отключения проверки целостности ядра.