Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:life-after-bsod [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== life-after-bsod ======
 +<​sub>​{{life-after-bsod.odt|Original file}}</​sub>​
 +
 +====== жизнь после BDOS\\ или night of the living undead II ======
 +
 +крис касперски ака мыщъх, no-email
 +
 +**голубой экран смерти — это последний вздох системы,​ после которого душа отделяется от тела (дамп памяти падает на диск) и компьютер уходит в перезагрузку,​ унося с собой все несохраненные данные… есть ли жизнь после смерти доподлинно неизвестно,​ но установка термоядерного отладчика типа ****soft-ice ****или ****syser'​****а позволяет взять ситуацию под свой контроль,​ вытаскивая систему из мира мертвых,​ в мир живых. мыщъх об этом уже писал ранее, но то было давно и неправда ;) сейчас другое время, иные операционные системы — ****XP SP2,​ ****Висла,​ ****Server 2003/2008, ****соответственно,​ древние ритуалы "​воскрешения"​ требуют адоптации,​ в общем, тут есть куда махнуть хвостом!**
 +
 +===== введение =====
 +
 +Вопреки распространенному мнению,​ Windows намного надежнее,​ чем это принято считать в народе. Моя основная машина (на базе W2K) перезагружается не чаще двух раз в месяц, а файловый сервер (и по совместительству рабочая станция для цифрового монтажа,​ так же вращающаяся под W2K) проработала полгода,​ после чего "​упала"​ из-за броска по питанию,​ с которым не смогла справится UPS.
 +
 +Голубые экраны смерти,​ вспыхивающие время от времени,​ отлавливаются soft-ice, который мыщъх держит постоянно загруженным и в подавляющем большинстве случаев возвращает систему к жизни, продолжая работать как ни в чем ни бывало. Это вопрос чести и хакерской этики. Перезагрузки — тривиальный,​ но порочный путь. Каждый сбой компьютера,​ каждый глюк системы мыщъх воспринимает чуть ли не как физическую боль и борется за здоровье машины как за свое собственное!
 +
 +И пускай меня сочтут ненормальным,​ назовут чокнутым… главное — что методики реанимации системы,​ разработанные и обкатанные мыщъхем,​ полезны не только ему одному. Так что, но пасаран!!! Баги не пройдут!!!
 +
 +===== >>>​ врезка что нам понадобиться =====
 +
 +Для экспериментов,​ описанных в статье,​ нам понадобятся следующие вещи, программы и инструменты:​
 +
 +  - **Windows Driver Kit (WDK) ****для всех систем по Вислу включительно:​**
 +    - __http://​www.microsoft.com/​whdc/​DevTools/​default.mspx__ (требует регистрации);​
 +  - **Windows Server 2003 SP1 DDK:**
 +    - __http://​www.microsoft.com/​whdc/​devtools/​ddk/​default.mspx__;​
 +  - **IA-32 Architecture Software Developer'​s Manual Vol****. ****3: System Programming Guide****:​**
 +    - __http://​www.intel.com/​products/​processor/​manuals/​____;​__
 +  - **Syser 1.95.19000.0894**:​*
 +    - __http://​www.sysersoft.com/​download/​download.php__;​
 +  - **Soft-Ice**:​
 +    - в настоящее время поддержка soft-ice прекращена,​ и хотя старые версии все еще можно найти в Сети, они не дружат с Вислой и Server 2008, однако,​ мыщъх (при финансировании компании K7 Computing) вплотную занялся переносом soft-ice под новые системы,​ так что следите за новостями! первая пре-альфа уже на подходе!
 +===== по ту сторону BSOD'​ов =====
 +
 +Голубые экраны вспыхивают всякий раз, когда ядро сталкивается с ситуацией,​ которую не может разрулить самостоятельно и которая способна пустить систему в разнос если не остановить некорректно работающий код, завершив работу всех механизмов оси в аварийном режиме,​ что кардинально отличает NT-подобные системы от мира UNIX, впадающие в BSOD (kernel panic – в их терминологии) только в реально хардкорных обстоятельствах,​ а все остальное время просто выгружают порочный драйвер (примерно так же, как NT завершает работу некорректно работающего приложения,​ не трогая остальных).
 +
 +Конечно,​ если ошибка возникнет в драйвере файловой системы,​ то далеко на такой тачанке уже не уедешь,​ однако,​ это уже клиника. Подавляющее большинство сбоев приходится на драйвера,​ установленные вирусами,​ антивирусами,​ брандмауэрами,​ звуковыми и видео-картами,​ причем,​ как показывает практика,​ 90% ошибок носит не фатальный характер,​ вполне совместимый с жизнью. К сожалению,​ ядро не спрашивает нас, хотим ли мы продолжить работу или предпочитаем внезапно умереть,​ в том самый момент,​ когда открыта куча приложений с тучей не сохраненных файлов…
 +
 +Но, прежде,​ чем бросаться в бой, нужно отделить программные ошибки от аппаратных отказов железа (как разогнанного,​ так и нет). Если голубые экраны вспыхивают в случайное время, каждый раз отображая разные данные (да только кто эти данные читает?​!),​ то с высокой степенью вероятности мы имеем дело с глюками железа. Пытаться реанимировать компьютер при этом чрезвычайно опасно. Если содержимое оперативной памяти разрушено из-за разгона или некачественного блока питания,​ то после выхода из BSOD'​а операционная система попытается скинуть дисковые буфера,​ а там у нас что? Правильно — мусор. И дисковый том отправится к праотцам,​ что _намного_ хуже, чем потеря оперативный данных.
 +
 +Впрочем,​ дефекты программного обеспечения так же могут приводить к генерации "​рандомных"​ экранов голубой смерти и без полного анализа ситуации здесь не обойтись. Однако,​ не будем падать духом!!! Рано или поздно мы "​объездим"​ ядро и разберемся во всех тонкостях его организации,​ ну а пока ограничимся лишь общей схемой.
 +
 +===== как устроен BSOD =====
 +
 +Роль палача в NT-системах играет функция **KeBugCheckEx**,​ экспортируемая ядром и вызываемая из сотен (если не тысяч!) мест с теми или иными параметрами. Что это за параметры?​ Обратившись к NTDDK, мы узнаем,​ что функция KeBugCheckEx принимает пять аргументов,​ первый из которых (BugCheckCode) содержит код ошибки,​ а четыре следующих параметра — места/​время/​обстоятельства ее возникновения.
 +
 +Перечень BugCheck-кодов можно найти в том же NTDDK, там же содержится описание и четырех аргументов,​ специфичных для каждого BugCheck-кода,​ количество которых чуть меньше сотни и потому,​ чтобы не держать кучу ненужной информации в голове,​ рекомендуется распечатать документацию и всегда держать ее под рукой.
 +
 +BugCheck-коды можно разделить на две большие категории:​ первая содержит адрес инструкции,​ вызвавшей исключение (например,​ 1Eh: KMODE_EXCEPTION_NOT_HANDLED,​ 0Ah: IRQL_NOT_LESS_OR_EQUAL,​ 24h: NTFS_FILE_SYSTEM),​ что позволяет "​заглянуть"​ отладчиком непосредственно на место аварии,​ исправить пробоину и, выйдя из отладчика,​ продолжить плавание (естественно,​ для этого нужно не только знать ассемблер,​ но и разбираться в тонкостях драйверостроения,​ но это — в идеале).
 +
 +Другая категория BugCheck-кодов не содержит адреса дефективной инструкции,​ поскольку ядро диагностирует аварийную ситуацию на поздней стадии. Найти виновника в этих случаях довольно затруднительно. Взять хотя бы такой BugCheck-код как C2h: BAD_POOL_CALLER,​ вызываемый из функции распределения памяти,​ обнаружившей,​ что память на конкретной измене,​ но кто ее разрушил и когда — этого система сказать не может. ​
 +
 +Поиск "​диверсанта"​ зачастую отнимает несколько дней кропотливого _ручного_ труда и что самое неприятное — исправить разрушенные структуры данных практически невозможно,​ а, значит,​ перезагрузки все равно не избежать,​ хотя с некоторым риском для жизни еще можно вернутся на уровень прикладного режима,​ попробовав сохранить хотя бы часть данных. Если нам повезет,​ то с разрушенным пулом (специальной областью ядерной памяти) можно проработать несколько минут, а иногда и дней (!). В исключительных ситуациях система держится на плаву целую неделю,​ однако,​ никакого смысла в таком экстриме нет. Риск разрушения дисковых томов в самом деле очень велик и потому,​ сохранив все не сохраненные данные,​ лучше всего все-таки перезагрузится.
 +
 +===== подготовка к работе =====
 +
 +Для борьбы с голубыми экранами смерти нам понадобится любой достойный термоядерный отладчик,​ загруженный _до_ их возникновения (надеюсь,​ не нужно объяснять почему?​). Достойных отладчиков ядра всего три: soft-ice, syser и Microsoft Kernel Debugger, но soft-ice не работает на Висле и Server'​е 2008, а Microsoft Kernel Debugger — это не вариант вообще. Остается syser, который мы и будем использовать.
 +
 +Установка обычно проходит гладко и нареканий не вызывает. Выбираем ручной режим загрузки (boot — manual), но чтобы не грузить его вручную (это же напряг какой!) перетягиваем иконку "Syser Loader",​ созданную инсталлятором в папку "​Автозагрузка"​. В принципе,​ можно не извращается и выбрать автоматический режим загрузки,​ но в этом случае,​ если возникнет конфликт отладчика с операционной системой его будет довольно трудно выгрузить.
 +
 +ОК, будет считать что syser загружен и готов работе,​ что подтверждается наличием соответствующей управляющей консоли на экране,​ которую можно свернуть или совсем закрыть. Отладчику от этого хуже не станет,​ но мы ничего закрывать не будем, поскольку планируем немного поэкспериментировать с заведомо дефективным драйвером,​ запуск которого как раз и осуществляется через эту консоль,​ но это будет потом. А сейчас нажимаем <​CTRL-F12>​ и вводим магическую команду "​bpm KeBugCheckEx x<​ENTER>​x<​ENTER>",​ заставляющую syser перехватывать вызов функции KeBugCheckEx _до_ возникновения голубого экрана смерти. Набирать ее придется вручную при _каждом_ запуске syser'​а,​ поскольку текущие версии отладчика макросов автозапуска не поддерживают,​ увы (в soft-ice вообще делать ничего не надо, поскольку он перехватывает KeBugCheckEx по умолчанию).
 +
 +На этом нашу миссию можно считать законченной. Теперь ни один голубой экран смерти не пробежит мимо нас незамеченным!
 +
 +===== уроки практической магии =====
 +
 +Напишем простой драйвер,​ обращающийся к памяти по нулевому указателю (что категорически недопустимо) и, как следствие,​ вызывающий BSOD, с которым мы и будем сражаться.
 +
 +Исходный ассемблерный текст простейшего драйвера-убийцы приведен ниже:
 +
 +.686
 +
 +.model flat, stdcall
 +
 +extern DbgPrint:​PROC
 +
 +.code
 +
 +DriverEntry proc
 +
 +push offset to_die; вывод предупредительного сообщения
 +
 +CALL DbgPrint
 +
 +pop eax
 +
 +
 +
 +XOR EAX,EAX; обнуляем регистр EAX
 +
 +MOV EAX,[EAX]; <-- здесь выскакивает BSOD
 +
 +
 +
 +push offset happy; если вы читаете этот текст,
 +
 +CALL DbgPrint; значит,​ вы еще живы ;-)
 +
 +pop eax
 +
 +
 +
 +mov eax, 0C0000182h; STATUS_DEVICE_CONFIGURATION_ERROR
 +
 +;RET; Four-F says
 +
 +RETN 8; <- haron says
 +
 +DriverEntry endp
 +
 +.data
 +
 +to_die DB "*] prepare to die! [*",​0Dh,​0Ah,​0
 +
 +happy  DB "*] welcome to life [*",​0Dh,​0Ah,​0
 +
 +end DriverEntry
 +
 +Листинг 1 исходный текст драйвера убийцы CALL-the-BSOD.asm
 +
 +Для его сборки нам понадобиться NTDDK (который можно бесплатно скачать с серверов Microsoft), а так же командный файл следующего содержания,​ в котором переменная окружения ntoskrnl содержит полный путь к библиотеке ntoskrnl.lib,​ зависящий от того, куда инсталлятор установил NTDDK. Как легко видеть,​ мыщъх использует путь отличный от пути по умолчанию ("​C:​\Prpgram Files\…) и потому нуждающийся в коррекции,​ в противном случае собрать драйвер не получится.
 +
 +На всякий случай,​ готовый драйвер CALL-the-BSOD.sys прилагается к статье.
 +
 +@ECHO OFF
 +
 +REM устанавливаем необходимые переменные окружения
 +
 +SET FILE_NAME=CALL-the-BSOD
 +
 +SET ntoskrnl=D:​\NTDDK\libchk\i386\ntoskrnl.lib
 +
 +REM удаляем результаты предыдущей сборки
 +
 +IF EXIST %FILE_NAME%.obj DEL %FILE_NAME%.obj
 +
 +REM транслируем ассемблерный листинг
 +
 +ml /nologo /c /coff %FILE_NAME%.asm
 +
 +IF NOT EXIST %FILE_NAME%.obj GOTO err
 +
 +REM линкуем сгенерированный .obj файл
 +
 +link /nologo /driver /​base:​0x10000 /align:32 /​out:​%FILE_NAME%.sys /​subsystem:​native %FILE_NAME%.obj %ntoskrnl% ​
 +
 +GOTO end
 +
 +:err
 +
 +ECHO -ERR!
 +
 +:end
 +
 +Листинг 2 командный файл для сборки драйвера-убийцы CALL-the-BSOD.sys
 +
 +===== первый бой — он трудный самый! =====
 +
 +В консоли syser'​а находим пункт "​Tools",​ а в нем — "Quick Driver Loader"​. В появившимся диалоговом окне указываем путь к драйверу CALL-the-BSOD.sys (Driver File Name). Имя сервиса (Service Name) загрузчик подставит самостоятельно. Нажимаем "​Install"​ (установка) и "​Start"​ (внимание:​ установку драйвера достаточно выполнить всего один раз и затем просто давить Start, а когда нам надоест с ним экспериментировать — сказать "​Uninstall"​ для удаления сервиса из системы,​ впрочем,​ можно и не говорить,​ т.к. это всего лишь запись в реестре,​ которая никому не мешает).
 +
 +{{life-after-bsod_Image_0.png}}
 +
 +Рисунок 1 загрузка драйвера-убийцы через Quick Driver Loader отладчика Syser
 +
 +Но мы сильно забегаем вперед. После нажатия кнопки "​Start"​ отладчик появляется на экране,​ послушно остановившись на функции KeBugCheckEx,​ как мы и ожидали (см. рис. 2):​
 +
 +{{life-after-bsod_Image_1.png}}
 +
 +Рисунок 2 syser перехватил вызов KeBugCheckEx,​ предотвращая появление голубого экрана смерти
 +
 +Если теперь нажать "​x<​ENTER>"​ для выхода из отладчика,​ передавая управления функции KeBugCheckEx,​ система немедленно рухнет,​ отображая следующий BSOD (см. рис. 3),​ то есть, свершится то, что произошло бы если отладчик не был бы установлен и сконфигурирован.
 +
 +{{life-after-bsod_Image_2.png}}
 +
 +Рисунок 3 голубой экран смерти,​ вызванный нашим драйвером-убийцей CALL-the-BSOD.sys
 +
 +Обратившись к NTDDK, мы узнаем,​ что номер 1Eh принадлежит BugCheck-коду KMODE_EXCEPTION_NOT_HANDLED,​ сигнализирующем об ошибке доступа к памяти. Первый аргумент функции KeBugCheckEx содержит код исключения,​ в данном случае равный C0000005h (STATUS_ACCESS_VIOLATION – нарушение доступа),​ второй аргумент (равный F75DF2AFh) – адрес дефективной машинной инструкции,​ до которой можно "​дотянуться"​ командой "​u *(esp+(4*3))"​ – дизассемблировать содержимое указателя,​ лежащего в третьем двойном слове относительно регистра-указателя вершины стека.
 +
 +Если команда введена правильно,​ мы увидим код драйвера-убийцы,​ который мы только что компилировали,​ линковали и загружали через "Quick Driver Loader"​ (см. рис. 4):​
 +
 +{{life-after-bsod_Image_3.png}}
 +
 +Рисунок 4 вот она — машинная инструкция,​ вызвавшая исключение,​ обрушившее систему в экран голубой смерти
 +
 +Все ясно! Машинная команда MOV EAX,​ [EAX] (где EAX, как мы помним,​ равен нулю) обращается к нулевой ячейке памяти,​ процессор генерирует исключение,​ подхватываемое ядром и после непродолжительных мытарств попадающее под трибунал KeBugCheckEx.
 +
 +На регистры,​ отображаемые отладчиком в левом верхнем окне, лучше не смотреть. EAX там равен не нулю, а черт знает вообще чему, а все потому,​ что с момента вызова исключения прошло слишком много времени и регистровый контекст был изменен. А потому,​ возвращается назад в драйвер нам нельзя. Точнее — можно, но для этого потребуется совершить слишком большое количество телодвижений,​ а мы тут не акробатикой занимаемся,​ а хакерством. Не будем крутить попой!!! Будем думать головой!!!
 +
 +===== универсальные способы реанимации системы =====
 +
 +Начинаем мозговой штурм. Какие будут предложения?​ Э, нет, суицид не предлагать. И покурить мы еще успеем. А тем временем syser, забывающий о том, что процессор надо охлаждать,​ разогревает его так, что вентиляторы просто взлетают и для борьбы с посторонним шумом мешающим нам думать надеваем наушники и включаем Transilvanian Beat Club, срывающий крышу и высаживающий нас на нереальный креатив.
 +
 +Сознание очищается и в голове появляется множество умный мыслей. Что мы вообще делаем на ядерном уровне,​ когда можем просто совершить нуль-транспортировку на прикладной,​ на котором никакие BSOD'​ы вообще не возникают и самое худшее,​ что может случится — это критическая ошибка,​ вызывающая аварийное завершение _текущего_ приложения,​ но никак не падение всей системы целиком.
 +
 +А что же ядро? Как там со стеком и прочими структурами данных?​ В каком состоянии мы их оставим?​ Ну, что касается ядра, то при вызове ядерных функций с прикладного уровня оно заново подготавливает регистровый контекст и все будет ОК. Тоже самое происходит и при генерации аппаратных прерываний,​ механизм диспетчеризации которых заслуживает отельной статьи,​ но мы не будем так глубоко углубляться в дебри теоретических изысканий. Самая большая опасность,​ которая нам грозит — это прерывание функции драйвера,​ оставляющий свои собственные данные в хаотичном состоянии и при последующем обращении к ним, BSOD с высокой степенью вероятности вспыхнет вновь. Хм, а может быть и не вспыхнет. Это уж как повезет или не повезет.
 +
 +Ладно, рискнем (а что нам еще остается делать?​) и воспользуемся легальной функций возвращения на прикладной уровень (которая,​ между прочим,​ недокументированна и варьируется от системы к системы). А других вариантов нет? Почему же?! Ядро работает с кодовым селектором 08h, прикладной уровень — 1Bh, следовательно,​ для нуль транспортировки нам достаточно изменить регистр CS с 08h на 1Bh, но syser отказывается воспринимать команду "​r CS 1B",​ ругаясь на ошибку синтаксиса,​ хотя с синтаксисом все нормально,​ а вот syser определенно еще недоделан и чтобы изменить CS приходится щелкать мышью по окну с регистрами и модифицировать CS вручную,​ посредством графического интерфейса (хвост бы его побрал). После чего можно со спокойной совестью выйти из отладчика по <​CTRL-F12>​ и… тут же попасть под артобстрел голубых экранов смерти,​ падающих один за другим,​ но если не сдаваться и мужественно возвращаться каждый раз на прикладной режим путем модификации CS, то (при определенной степени везения) дождаться относительного затишья и продолжить работать на прикладном уровне как ни в чем небывало.
 +
 +А вот другое решение. Вместо того, чтобы нуль транспортироваться на прикладной режим, оставляя ядро в аварийном состоянии,​ попробуем модифицировать функцию KeBugCheckEx,​ воткнув в ее начало машинную команду "​RETN 14h",​ соответствующую машинному коду: C2h 14h 00h. Находясь в начале KeBugCheckEx просто дадим команду "​d eip"​ (отобразить в дампе памяти содержимое по адресу,​ на который указывает регистр EIP), и, щелкнув по мышью по верхнему окну, заменим три первых байта на "​C2h 14h 00h"​. Поскольку,​ syser – сырой продукт,​ синхронизация дампа памяти с окном кода отсутствует,​ и чтобы увидеть проделанные изменения,​ щелкнем мышью по кодовому окну, нажав <​PageUp>/<​PageDown>,​ ага, теперь,​ команда "​RETN 14h"​ появилась в самом начале функции KeBugCheckEx (см. рис. 5):​
 +
 +{{life-after-bsod_Image_4.png}}
 +
 +Рисунок 5 устраиваем "​короткое замыкание"​ в функции KeBugCheckEx
 +
 +Ну, и чего мы добились?​! Некоторые (между прочим,​ достаточно многие) виды исключений,​ при попытке их игнорирования таким варварским путем, будут вызывать BSOD вновь и вновь, пускай он уже никогда не появится на экране (ведь своим RETN 14h мы фактически устроили короткое замыкание внутри функции-палача).
 +
 +Однако,​ на многопроцессорных системах (включая HT- и многоядерные процессоры) все будет работать,​ хоть и сильно тормозить,​ поскольку "​зацикливание"​ одного ядерного "​потока"​ практически никак не повлияет на все остальные. Поток взят в кавычки потому,​ что в ядре NT никаких потоков нет, но для объяснения происходящего на пальцах,​ такая трактовка ситуации вполне сойдет.
 +
 +А вот другой вариант. Вместо возврата из KeBugCheckEx,​ просто зациклим ее, воткнув в ее начало команду JMP SHORT $-2,​ которой соответствует следующий машинный код: EBh FEh, внедряемый по прежней схеме: "​d eip"​ и дальше запись EBh FEh поверх существующего кода (см. рис. 6).
 +
 +{{life-after-bsod_Image_5.png}}
 +
 +Рисунок 6 циклим функцию KeBugCheckEx
 +
 +Зациклившая KeBugCheckEx на однопроцессорной машине мы сильно рискуем получить глухой завис, однако,​ двухпроцессорные тачки еще какое-то время успеют проработать,​ прежде чем оба процессора вызовут KeBugCheckEx и войдут в бесконечный цикл, из которого их вывести может только аппаратное прерывание,​ сгенерированное таймером или иными внешними устройствами,​ правда,​ при этом существует реальная угроза переполнения стека. Если KeBugCheckEx многократно вызывается,​ выпадая в бесконечный цикл и оставляя переданные аргументы на вершине стека, то стеку рано или поздно наступит конец, а ловить исключение уже некому и система уйдет в перезагрузку безо всяких голубых экранов. Впрочем,​ это можно исправить путем изменения JMP SHORT $-2 на ADD ESP,​14h/​JMP SHORT $-2,​ что соответствует машинному коду: 83h C4h 14h/​EBh FEh.
 +
 +Вероятность выживания системы существенно повышается,​ впрочем,​ все универсальные приемы преодоления BSOD далеки от совершенства,​ ведь если бы хоть одно надежное универсальное решение существовало,​ то его уже давно бы реализовали:​ если не сама Microsoft, то сторонние разработчики.
 +
 +Так что без изучения ассемблера и анатомических особенностей NT-подобных систем нам все равно не уйти.
 +
 +===== заключение =====
 +
 +Хакерские навыки не приобретаются в одночасье. Начиная с простых экспериментов,​ и употребления различных "​рецептурных справочников"​ мы постепенно въезжаем в суть вещей, постигая устройство мира, а мир устроен одновременно и просто,​ и сложно. Любая решенная задача кажется простой и вызывает удивление,​ что же нам мешало решить ее раньше?​
 +
 +Термоядерные отладчики _действительно_ позволяют разрулить огромное количество мелких и крупных проблем,​ однако,​ технику их использования нельзя передать словами. Тут необходима практика,​ интуиция… но надо же с чего-то начинать?​ Мыщъх искренне надеяться,​ что эта статья и будет тем пинком (гм, толчком) который подвигнет читателей на эксперименты и исследования.
 +
 +