Различия

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

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

Предыдущая версия справа и слева Предыдущая версия
Следующая версия Следующая версия справа и слева
articles:after-bsod.lite [2017/03/09 22:49]
91.108.183.162
articles:after-bsod.lite [2017/03/09 23:01]
91.108.183.162
Строка 1: Строка 1:
 ====== after-BSOD.lite ====== ====== after-BSOD.lite ======
  
-<​sub>​{{:​articles:​after-bsod.lite.odt|Original file}} </​sub>​+<​sub>​{{:​articles:​after-bsod.lite.odt|Original file}} ​ </​sub>​
  
 ====== жизнь после BSOD ====== ====== жизнь после BSOD ======
Строка 29: Строка 29:
  
 **Рисунок 2 обычно после BSOD наступает смерть…** **Рисунок 2 обычно после BSOD наступает смерть…**
- 
 ===== >>>​ врезка чего не умеет NTFS ===== ===== >>>​ врезка чего не умеет NTFS =====
  
Строка 41: Строка 40:
  
 **Рисунок 3 голубой экран на платном Интернет-телефоне** **Рисунок 3 голубой экран на платном Интернет-телефоне**
- 
 ===== что нам понадобиться ===== ===== что нам понадобиться =====
  
Строка 125: Строка 123:
 Рисунок 6 soft-ice показывает инструкцию,​ возбудившую исключение,​ которые при обычных обстоятельствах ведет к голубому экрану,​ затем наступает чернота… в которой ни черта не видно. смерть! Рисунок 6 soft-ice показывает инструкцию,​ возбудившую исключение,​ которые при обычных обстоятельствах ведет к голубому экрану,​ затем наступает чернота… в которой ни черта не видно. смерть!
  
-Вот она! Инструкция,​ вызвавшая сбой! А давайте ее "​перепрыгнем",​ продолжив выполнение с RET 08h? Сказано — сделано. Но для начала нужно выйти из обработчика исключения. Для этого в soft-ice необходимо выполнить следующие команды:​+Вот она! Инструкция,​ вызвавшая сбой! А давайте ее "​перепрыгнем",​ продолжив выполнение с RET 08h? Сказано — сделано. Но для начала нужно выйти из обработчика исключения. Для этого в soft-ice необходимо выполнить следующие команды:​ 
 + 
 +    * r eip = *esp + sizeof(mov eax,​[0]);​ %%//​%% устанавливаем регистр EIP на RET 
 +    * r cs = *(esp + 4);​ %%//​%% устанавливаем селектор CS (не обязательно) 
 +    * r FL = I;​ %%//​%% разрешаем прерывания;​ 
 +    * r esp = esp + C %%//%% снимаем со стека 3 дв.слова,​ заброшенные туда CPU 
 +    * x %%//%% выходим из отладчика 
 + 
 +После выполнения этой "​магической"​ последовательности команд,​ система продолжит свою нормальную работу и синий экран уже не появится. Фантастика! Невероятно! Мы только что избежали гибели,​ которая еще мгновение назад казалась неотвратимой! 
 + 
 +После выполнения этой "​магической"​ последовательности команд,​ система продолжит свою нормальную работу и синий экран уже не появится. Фантастика! Невероятно! Мы только что избежали гибели,​ которая еще мгновение назад казалась неотвратимой! 
 +Один маленький нюанс. Моя (и возможно ваша) версия soft-ice не умеет восстанавливать регистр ESP в обработчике исключения. Отладчик игнорирует команду r esp=esp +C,​ на самом деле только имитируя ее выполнение! А это значит,​ что стек остается несбалансированным и несмотря ни на какие усилия "​медиков",​ система все-таки грохается. Приходится хитрить. Мы видим, что за RET 08h расположена длинная цепочка NOP'​ов. А что если воткнуть сюда команду "​ADD ESP,​0Ch",​ чтобы стек сбалансировал сам процессор?​ 
 + 
 +Говорим отладчику '​A BE67C008'​ (ассемблировать начиная с адреса BE67C008) и вводим следующие ассемблерные инструкции:​ ADD ESP,​0C<​ENTER>​JMP BE67C005<​ENTER>​ и еще один <​ENTER>​ для завершения ввода. Переустанавливаем EIP на начало нашей "​заплатки"​ — r eip =BE67C008 и... выходим из soft-ice. На этот раз у нас все получается! 
 +На всякий случай,​ вот последовательность команд по реанимации системы. Напоминаю,​ что она применима только в данном частном случае:​ 
 + 
 +u *esp 
 +r eip = *esp 
 +r eip = eip + 9 
 +a eip 
 +add esp,0c 
 +jmp BE67C005h ; адрес команды RET 8, в вашем случае будет другим 
 +<​ENTER>​ 
 +r fl=I 
 +
 + 
 +**Листинг 4 реанимация системы в условиях,​ приближенных к боевым** 
 + 
 +**Рисунок 7 лучшая реклама товару — синий экран** 
 + 
 +===== автоматизируем нашу работу ===== 
 + 
 +Способ "​ручного"​ восстановления,​ только что описанный выше, хорошо сочетается с духом системных программистов,​ постоянно пасущих soft-ice и умеющих фехтовать регистрами как рапирой. А вот простым смертным такой подход смерти подобен. Но почему бы нам не написать утилиту,​ зацикливающую сбойный поток или накоротко замыкающую KeBugCheckEx?​ 
 +Написать-то такую штуку несложно (и мы действительно напишем ее), но… это все равно, что подложить полено под аварийный клапан. Если система пойдет в разнос,​ ее уже ничего не остановит и… как рванет! Хвост даже по запчастям не соберешь! Может пострадать даже файловая система (пусть это будет хоть NTFS). Пускай вероятность такой трагедии крайне мала, она все-таки возможна — имейте это ввиду. Тем не менее, рискнуть все-таки стоит, особенно в тех случаях,​ когда вы уверены,​ что это можно сделать. 
 +Вот, например,​ возник у меня как-то конфликт между криво написанным драйвером DSL-модема и драйвером видеокарты из-за чего при просмотре видео иногда выскакивает BSOD. Поскольку,​ нормальных дров найти не удалось,​ я временно ограничился тем, что "​закоротил"​ KeBugCheckEx перемычкой,​ изготовленной из команды JMP и… это "​прижилось"​! 
 +Проведем следующий эксперимент. Нажмем <​Ctrl-D>​ для вызова soft-ice, установим точку останова на KeBugCheckEx и… запустим наш драйвер-убийцу. Причем,​ точка останова обязательно должна быть аппаратной ("​bpm KeBugCheckEx X"​),​ а не программной (bpх KeBugCheckEx) иначе ничего не получится. 
 +На этот раз вместо сообщения о ошибке страничного доступа,​ soft-ice всплывает по срабатыванию точки останова,​ высвечивая курсором первую команду функции KeBugCheckEx,​ которая в нашем случае располагается по адресу 8042BF14h. 
 + 
 +**Рисунок 8 перехват голубого экрана командой bpm "​KeBugCheckEx X" в soft-ice** 
 + 
 +Прокручивая окно дизассемблера вниз, находим первую инструкцию "​RET 14h"​ (в нашем случае она располагается по адресу 8042C1E9h). Это и есть команда выхода из функции на которую нужно сделать jmp. Для быстрого поиска можно попросить soft-ice сделать search ("​s eip l -1 C2,​14,​00"​). 
 +Говорим отладчику "​r eip = 8042C1E9"​ (у вас адрес скорее всего будет другим) и давим на <​Ctrl-D>​ для выхода. Отладчик всплывает повторно,​ в той же самой функции. У нас ничего не получилось?​! Не торопитесь с выводами! Все идет по плану! Игнорирование критических ошибок вызывает целый каскад вторичных исключений,​ что в данном случае и происходит. Повторяем нашу команду "​r eip = 8042C1E9"​ (для этого достаточно нажать стрелку вверх/<​ENTER>​) и… система возвращается в нормальный режим! Третий раз отладчик уже не всплывает. Мышь немного тормозит,​ однако,​ гонять ее по коврику вполне возможно. 
 +Приступаем к созданию драйвера,​ который будет все это делать за нас. Для начала нам понадобится скелет. Выглядит он так: 
 + 
 +<code asm> 
 +.386 ; использовать команды .386 ЦП 
 +.model flat, stdcall ; плоская модель памяти,​ stdcall-вызовы по умолчанию 
 + 
 + ​.code ;​ секция кода 
 + 
 +DriverEntry proc ; точка входа в драйвер 
 + 
 + ; код "​драйвера"​ 
 +
 + … 
 + … 
 + … 
 + ; возвращаем ошибку конфигурации 
 + mov eax, 0C0000182h; STATUS_DEVICE_CONFIGURATION_ERROR 
 + ret ; выходим 
 +DriverEntry endp 
 +end DriverEntry 
 +</​code>​ 
 + 
 +**Листинг 5 скелет "​псевдодрайвера",​ не управляющий никакими устройствами,​ но позволяющий нам выполнять код на уровне ядра** 
 + 
 +На самом деле это не совсем драйвер. Он не принимает никаких IRP-пакетов,​ не обслуживает никаких устройств и вообще не делает ничего,​ только загружается и выгружается. Но для нашей затеи этого будет вполне достаточно! 
 +Весь код сосредоточен внутри процедуры DriverEntry – своеобразном аналоге функции main языка Си, которая выполняется при попытке загрузки драйвера,​ инициализируя все, что необходимо. Отсюда можно "​дотянуться"​ до функции KeBugCheckEx и модифицировать ее по своему усмотрению. 
 +Несмотря на то, что процедура DriverEntry выполняется на уровне ядра с максимальными привилегиями,​ попытка "​правки"​ машинного кода приводит к нарушению доступа. Это срабатывает защита от непреднамеренного хака ядра некорректным драйвером. Как ее отключить?​ 
 +Путь первый — через реестр. Создаем в разделе HKLM\SYSTEM\CurrentControlSet\Control\SessionManager\Memory Management значение типа REG_DWORD с именем EnforceWriteProtection и значением 0 (это можно делать и с прикладного уровня). Все! Запись в ядро открыта! Кстати говоря,​ soft-ice именно так и работает. 
 +Путь второй — репаминг страниц. Отображаем физический адрес страницы,​ в которой лежит KeBugCheckEx на виртуальное адресное пространство своего процесса посредством вызова функции NtMapViewOfSection,​ назначая все необходимые нам права. Репаминг осуществляется исключительно на уровне ядра, но к отображенной странице можно обращаться даже из прикладного уровня. Красота! По этой схеме работают многие брандмауэры и другие программы,​ нуждающиеся в перехвате ядерных функций,​ например,​ rootkit'​ы. Подробности здесь: http://​www.stanford.edu/​~stinson/​misc/​curr_res/​nt_hooking.txt. 
 +Путь третий — сброс флага WP в регистре Cr0. Это достаточной грязный трюк с целой свитой противопоказаний и рекламаций,​ однако,​ для наших целей он вполне подходит. Используем его как самый простой и быстрый вариант,​ умещающийся всего в 3 (!) машинных команды:​ 
 + 
 +<code asm> 
 +mov eax, cr0 ; грузим управляющий регистр cr0 в регистр eax 
 +and eax, 0FFFEFFFFh; сбрасываем бит WP, запрещающий запись 
 +mov cr0, eax ; обновляем управляющий регистр cr0 
 +</​code>​ 
 + 
 +**Листинг 6 код, отключающий защиту ядра от записи** 
 + 
 +Соответственно,​ чтобы включить защиту,​ этот самый бит WP нужно установить,​ что и делают следующие машинные команды:​ 
 + 
 +<code asm> 
 +mov eax, cr0 ; грузим управляющий регистр cr0 в регистр eax 
 +or eax, 10000h ; сбрасываем бит WP, запрещающий запись 
 +mov cr0, eax ; обновляем управляющий регистр cr0 
 +</​code>​ 
 + 
 +**Листинг 7 код, включающий защиту ядра** 
 + 
 +"​Политически корректная"​ программа должна не просто отключать/​включать защиту от записи,​ а запоминать текущее состояние бита WP перед его изменением,​ а затем восстанавливать его обратно "​как було",​ иначе можно непроизвольно включить защиту в самый неподходящий момент,​ серьезно навредив,​ вирусу или rootlit'​у. Только он ее того, как вдруг она! 
 +"​Закоротить"​ функцию KeBugCheckEx можно разными путями. Самое правильное (и надежное!) определить ее адрес путем разбора таблицы импорта,​ но это слишком долго, муторно,​ нудно, да еще и утомительно. Гораздо проще подставить готовые адреса,​ жестко прописав их в своей программе. Минус этого решения в том, что на других компьютерах она работать не будет. Стоит установить (или удалить) какой-то ServicePack,​ перейти на другую версию системы как все адреса тут же изменятся и произойдет сплошной завис. Тем не менее, имея исходные тексты драйвера под рукой его всегда можно исправить и перекомпилировать. Так что для "​домашнего использования"​ такое решение вполне допустимо. 
 +Главная тонкость в том, что мы не должны трогать первый байт функции KeBugChekEx,​ поскольку его уже потрогал soft-ice и весь вытрогал. Так же поступают и другие хакерские программы (например,​ API-шпионы),​ помещая сюда команду INT 03 (опкод CCh), предварительно сохранив прежнее содержимое где-то в другом месте. 
 +ОК, пропустим первую команду (в нашем случае это PUSH EBP) к едреням и начнем внедрение со второй. Чтобы сбалансировать стек в противовес PUSH EBP говорим POP EAX, а затем либо jmp на RET 14h, либо непосредственно сам RET 14h. Последний вариант короче,​ да к тому же элегантнее. Реализуется он так: 
 + 
 +<code asm> 
 +mov dword ptr DS:​[8042BF14h+1],​ 14C258h 
 +</​code>​ 
 + 
 +**Листинг 8 код, "​закорачивающий"​ KeBugCheckEx** 
 + 
 +Здесь: 8042BF14h — адрес начала функции KeBugCheckEx (на всех машинах разный),​ 1 – длина инструкции PUSH EBP, а 14C258h — машинный код, представляющий собой последовательность двух команд:​ POP EAX (58h)/​RET 14h (C2h 14h 00h). 
 +Объединив все компоненты воедино мы получаем следующий папелац:​ 
 + 
 +<code asm> 
 +.386 
 +.model flat, stdcall 
 +.code 
 +DriverEntry proc 
 + mov eax, cr0 ; грузим управляющий регистр cr0 в регистр eax 
 + mov ebx, eax ; сохраняем бит WP в регистре ebx 
 + and eax, 0FFFEFFFFh ;​ сбрасываем бит WP, запрещающий запись 
 + mov cr0, eax ; обновляем управляющий регистр cr0 
 +  
 + mov dword ptr DS:​[8042BF14h+1],​ 14C258h 14C258 
 + ; "​закорачиваем"​ KeBugCheckEx 
 +  
 + mov cr0, ebx ; восстанавливаем бит WP 
 + mov eax, 0C0000182h; STATUS_DEVICE_CONFIGURATION_ERROR 
 + ret 
 +DriverEntry endp 
 +end DriverEntry 
 +</​code>​ 
 + 
 +**Листинг 9 средство против BSOD, перед употреблением встряхнуть** 
 + 
 +Вот такой маленький драйвер,​ а сколько данных он может спасти! Остается только откомпилировать его и можно приступать к испытаниям:​ 
 + 
 +<code asm> 
 +ml /nologo /c /coff nobsod.asm 
 +link /driver /​base:​0x10000 /align:32 /​out:​nobsod.sys /​subsystem:​native nobsod.obj 
 +</​code>​ 
 + 
 +**Листинг 10 ключи ассемблирования и линковки (используется пакет MASM из NT DDK)** 
 + 
 +Если все было сделано правильно,​ на диске образуется файл nobsod.sys, который мы загрузим с помощью динамического загрузчика w2k_load. Загрузчик конечно,​ заругается матом, что мол ERROR и драйвер ни хрена не грузится,​ но так и должно быть. Все нормально! Мы же возвратили код STATUS_DEVICE_CONFIGURATION_ERROR! 
 +Внимание:​ под VM Ware такой трюк не срабатывает,​ поскольку она не полностью эмулирует регистр cr0 и таких шуток в упор не понимает,​ вызывая завис гостевой оси. В этом случае можно закомментировать все строки,​ относящиеся к регистру cr0 и отключить защиту через реестр,​ создав соответствующий ключ "​Редактором Реестра"​. Кстати говоря,​ если на целевой машине установлен soft-ice – такой ключ уже создан и ничего делать не надо. 
 + 
 +**Рисунок 9 предсмертное сообщение VM Ware, которая она часто выдает,​ когда над ней проводят разные эксперименты,​ на которые она не была рассчитана** 
 + 
 +Загрузим драйвер-убийцу,​ чтобы проверить справиться ли с ним наше средство против BSOD или нет… soft-ice (если он установлен) несколько раз всплывает. Вот зануда! Гоните его прочь, нажимая x или <​Ctrl-D>​. Но так или иначе, голубой экран уже не появляется! Система жутко тормозит,​ но все-таки работает. И это — главное! 
 +Плохо то, что теперь NT никак не может сигнализировать,​ что произошел системный сбой и что нужно побыстрее сматывать ласты, совершая shutdown. То есть почему это не может сигнализировать?​! Самое простое — добавить в нашу "​заплатку"​ на KeBugCheckEx несколько ассемблерных строк, которые "​бибикнут"​ спикером или сыграют "​семь-сорок"​ (как вариант "во поле береза стояла"​) на динамике. В принципе,​ можно даже разделить BugCheck коды на категории,​ каждой из которой будет соответствовать свое число гудков. За примерами далеко ходить не надо. Их можно выдрать из любого DOS-вируса. Техника программирования спикера на уровне ядра та же самая и она ничуть не изменилась. 
 +Да много что можно сделать! Главное — фантазию иметь! 
 + 
 +===== >>>​ врезка исключение и наказание ===== 
 + 
 +Всегда ли помогает "​шунтирование"​ KeBugCheckEx?​ Насколько это безопасно?​ Это очень, очень опасно и помогает далеко не всегда. Вот, например,​ рассмотрим следующий пример кода, позаимствованный из ядра: 
 + 
 +<code asm> 
 +00565201 call ExAllocatePoolWithTag ;​ выделение памяти из лужи 
 +00565206 cmp eax,​ ebx ; проверка успешности выделения памяти 
 +00565208 mov ds:​dword_56BA84,​ eax 
 +0056520D jnz short loc_56521C ;​ -> нам дали память! живем, мужики! 
 +0056520F push ebx ;​ \  
 +00565210 push ebx ;​ + 
 +00565211 push 6 ;​ +- с памятью вышел облом 
 +00565213 push 5 ;​ +- отправляемся на небеса 
 +00565215 push 67h ;​ + 
 +00565217 call KeBugCheckEx ;​ / 
 +0056521C loc_56521C:​ ;​ CODE XREF: sub_5651C1+4Cj 
 +0056521C lea eax,​ [ebp+var_C] ;​ продолжаем нормальное выполнение 
 +0056521F push ebx 
 +00565220 push eax 
 +</​code>​ 
 + 
 +**Листинг 11 фрагмент кода, при котором "​шунтирование"​ KeBugCheckEx заканчивается очень печально** 
 + 
 +Система выделяет память из общего пула (в шутку называемого лужей) и если с памятью не облом, происходит нормальное продолжение,​ в противном случае вспыхивает голубой экран и хана. Допустим,​ мы "​закоротили"​ KeBugCheckEx,​ что тогда? Нас обломали на память,​ а мы продолжаем нормальное выполнение как ни в чем не бывало,​ обращаясь по указателю,​ который указывает в никуда. Возникает целый каскад вторичных исключений,​ а все структуры данных превращается в труху и система рушится окончательно. Вот так. 
 + 
 +===== заключение ===== 
 + 
 +Мы пережили самую страшную систему катастрофу — BSOD, после которой нам все по плечу! Конечно,​ неразумно практиковать такой подход на сервере,​ но для рабочих станций он вполне приемлем. Проверено на мыщъх'​иной шкуре! Кстати говоря,​ некоторые вирусы,​ черви и rootkit'​ы используют схожую технику для маскировки своего присутствия в системе. Некорректно написанный вирус может вызвать синий экран и в системном журнале появится соответствующая запись,​ помогающая администратору разобраться с проблемой. Если же "​перемкнуть"​ KeBugCheckEx,​ то компьютер будет просто беспричинно тормозить (или виснуть),​ но в журнале ничего не появится!
  
-  - reip = *esp + sizeof(moveax,​[0]);//​ устанавливаем регистр EIP на RET - rcs = *(esp + 4);//  устанавливаем селектор CS (не обязательно) +**Рисунок 10 дзенский сад голубых экранов смерти**
-  - rFL = I;// разрешаем прерывания;​ - resp = esp + C//  снимаем со стека 3 дв.слова,​ заброшенные туда CPU +
-  - x// выходим из отладчика После выполнения этой "​магической"​ последовательности команд,​ система продолжит свою нормальную работу и синий экран уже не появится. Фантастика! Невероятно! Мы только что избежали гибели,​ которая еще мгновение назад казалась неотвратимой! Один маленький нюанс. Моя (и возможно ваша) версия soft-ice не умеет восстанавливать регистр ESP в обработчике исключения. Отладчик игнорирует команду r esp=esp +C, на самом деле только имитируя ее выполнение! А это значит,​ что стек остается несбалансированным и несмотря ни на какие усилия "​медиков",​ система все-таки грохается. Приходится хитрить. Мы видим, что за RET 08h расположена длинная цепочка NOP'​ов. А что если воткнуть сюда команду "ADD ESP,​0Ch",​ чтобы стек сбалансировал сам процессор?​ Говорим отладчику 'A BE67C008'​ (ассемблировать начиная с адреса BE67C008) и вводим следующие ассемблерные инструкции:​ ADD ESP,​0C<​ENTER>​JMP BE67C005<​ENTER>​ и еще один <​ENTER>​ для завершения ввода. Переустанавливаем EIP на начало нашей "​заплатки"​ — r eip =BE67C008 и… выходим из soft-ice. На этот раз у нас все получается! На всякий случай,​ вот последовательность команд по реанимации системы. Напоминаю,​ что она применима только в данном частном случае:​ u *esp r eip = *esp r eip = eip + 9 a eip add esp,0c jmpBE67C005h;​ адрес команды RET 8, в вашем случае будет другим <​ENTER>​ r fl=I x Листинг 4 реанимация системы в условиях,​ приближенных к боевым ​Рисунок ​7 лучшая реклама товару — синий экран =====  автоматизируем нашу работу ===== Способ "​ручного"​ восстановления,​ только что описанный выше, хорошо сочетается с духом системных программистов,​ постоянно пасущих soft-ice и умеющих фехтовать регистрами как рапирой. А вот простым смертным такой подход смерти подобен. Но почему бы нам не написать утилиту, ​зацикливающую сбойный поток или накоротко замыкающую KeBugCheckEx?​ Написать-то такую штуку несложно (и мы действительно напишем ее), но… это все равно, что подложить полено под аварийный ​клапан. Если система пойдет в разнос, ее уже ничего не остановит и… как рванет! Хвост ​даже по запчастям не соберешь! Может пострадать даже файловая система (пусть это будет хоть NTFS). Пускай вероятность такой трагедии крайне мала, ​она все-таки возможна — имейте это ввиду. Тем не менее, рискнуть все-таки стоит, особенно в тех случаях, когда вы уверены,​ что это можно сделать. Вот, например,​ возник у меня как-то конфликт между криво написанным драйвером DSL-модема и драйвером видеокарты из-за чего при просмотре видео иногда выскакивает BSOD. Поскольку,​ нормальных дров найти не удалось,​ я временно ограничился тем, что "​закоротил"​ KeBugCheckEx перемычкой,​ изготовленной из команды JMP и… это "​прижилось"​! Проведем следующий эксперимент. Нажмем <​Ctrl-D>​ для вызова soft-ice, установим точку останова на KeBugCheckEx и… запустим наш драйвер-убийцу. Причем,​ точка останова обязательно должна ​быть аппаратной ("bpm KeBugCheckEx X"), а не программной (bpх KeBugCheckEx) иначе ничего не получится. На этот раз вместо сообщения о ошибке страничного доступа,​ soft-ice ​всплывает по срабатыванию точки останова,​ высвечивая курсором первую команду функции KeBugCheckEx,​ которая в нашем случае располагается по адресу 8042BF14h. Рисунок 8 перехват голубого экрана командой bpm "​KeBugCheckExX"​ в soft-ice Прокручивая окно дизассемблера вниз, находим первую инструкцию "RET 14h" (в нашем случае она располагается по адресу 8042C1E9h). Это и есть команда выхода из функции на которую нужно сделать jmp. Для быстрого поиска можно попросить soft-ice сделать search ("s eip l -1 C2,​14,​00"​). Говорим отладчику "r eip = 8042C1E9"​ (у вас адрес скорее всего будет другим) и давим на <​Ctrl-D>​ для выхода. Отладчик всплывает повторно,​ в той же самой функции. У нас ничего не получилось?​! Не торопитесь с выводами! Все идет по плану! Игнорирование критических ошибок вызывает целый каскад вторичных исключений,​ что в данном случае и происходит. Повторяем нашу команду "r eip = 8042C1E9"​ (для этого достаточно нажать стрелку вверх/<​ENTER>​) и… система возвращается в нормальный режим! Третий раз отладчик уже не всплывает. Мышь немного тормозит,​ однако,​ гонять ее по коврику вполне возможно. Приступаем к созданию драйвера,​ который будет все это делать за нас. Для начала нам понадобится скелет. Выглядит он так: .386; использовать команды .386 ЦП .modelflat, stdcall; плоская модель памяти,​ stdcall-вызовы по умолчанию .code; секция кода DriverEntryproc;​ точка входа в драйвер ; код "​драйвера"​ ; … … … ; возвращаем ошибку конфигурации mov eax, 0C0000182h; STATUS_DEVICE_CONFIGURATION_ERROR ret; выходим DriverEntry endp end DriverEntry Листинг 5 скелет "​псевдодрайвера",​ не управляющий никакими устройствами,​ но позволяющий нам выполнять код на уровне ядра На самом деле это не совсем драйвер. Он не принимает никаких IRP-пакетов,​ не обслуживает никаких устройств и вообще не делает ничего,​ только загружается и выгружается. Но для нашей затеи этого будет вполне достаточно! Весь код сосредоточен внутри процедуры DriverEntry – своеобразном аналоге функции main языка Си, которая выполняется при попытке загрузки драйвера,​ инициализируя все, что необходимо. Отсюда можно "​дотянуться"​ до функции KeBugCheckEx и модифицировать ее по своему усмотрению. Несмотря на то, что процедура DriverEntry выполняется на уровне ядра с максимальными привилегиями,​ попытка "​правки"​ машинного кода приводит к нарушению доступа. Это срабатывает защита от непреднамеренного хака ядра некорректным драйвером. Как ее отключить?​ Путь первый — через реестр. Создаем в разделе HKLM\SYSTEM\CurrentControlSet\Control\SessionManager\MemoryManagement значение типа REG_DWORD с именем EnforceWriteProtection и значением 0 (это можно делать и с прикладного уровня). Все! Запись в ядро открыта! Кстати говоря,​ soft-ice именно так и работает. Путь второй — репаминг страниц. Отображаем физический адрес страницы,​ в которой лежит KeBugCheckEx на виртуальное адресное пространство своего процесса посредством вызова функции NtMapViewOfSection,​ назначая все необходимые нам права. Репаминг осуществляется исключительно на уровне ядра, но к отображенной странице можно обращаться даже из прикладного уровня. Красота! По этой схеме работают многие брандмауэры и другие программы,​ нуждающиеся в перехвате ядерных функций,​ например,​ rootkit'​ы. Подробности здесь: ​ http://​www.stanford.edu/​~stinson/​misc/​curr_res/​nt_hooking.txt . Путь третий — сброс флага WP в регистре Cr0. Это достаточной грязный трюк с целой свитой противопоказаний и рекламаций,​ однако,​ для наших целей он вполне подходит. Используем его как самый простой и быстрый вариант,​ умещающийся всего в 3 (!) машинных команды:​ moveax, cr0; грузим управляющий регистр cr0 в регистр eax andeax, 0FFFEFFFFh; сбрасываем бит WP, запрещающий запись movcr0, eax; обновляем управляющий регистр cr0 Листинг 6 код, отключающий защиту ядра от записи Соответственно,​ чтобы включить защиту,​ этот самый бит WP нужно установить,​ что и делают следующие машинные команды:​ moveax, cr0; грузим управляющий регистр cr0 в регистр eax oreax, 10000h; сбрасываем бит WP, запрещающий запись movcr0, eax; обновляем управляющий регистр cr0 Листинг 7 код, включающий защиту ядра "​Политически корректная"​ программа должна не просто отключать/​включать защиту от записи,​ а запоминать текущее состояние бита WP перед его изменением,​ а затем восстанавливать его обратно "​как було",​ иначе можно непроизвольно включить защиту в самый неподходящий момент,​ серьезно навредив,​ вирусу или rootlit'​у. Только он ее того, как вдруг она! "​Закоротить"​ функцию KeBugCheckEx можно разными путями. Самое правильное (и надежное!) определить ее адрес путем разбора таблицы импорта,​ но это слишком долго, муторно,​ нудно, да еще и утомительно. Гораздо проще подставить готовые адреса,​ жестко прописав их в своей программе. Минус этого решения в том, что на других компьютерах она работать не будет. Стоит установить (или удалить) какой-то ServicePack,​ перейти на другую версию системы как все адреса тут же изменятся и произойдет сплошной завис. Тем не менее, имея исходные тексты драйвера под рукой его всегда можно исправить и перекомпилировать. Так что для "​домашнего использования"​ такое решение вполне допустимо. Главная тонкость в том, что мы не должны трогать первый байт функции KeBugChekEx,​ поскольку его уже потрогал soft-ice и весь вытрогал. Так же поступают и другие хакерские программы (например,​ API-шпионы),​ помещая сюда команду INT 03 (опкод CCh), предварительно сохранив прежнее содержимое где-то в другом месте. ОК, пропустим первую команду (в нашем случае это PUSH EBP) к едреням и начнем внедрение со второй. Чтобы сбалансировать стек в противовес PUSH EBP говорим POP EAX, а затем либо jmp на RET 14h, либо непосредственно сам RET 14h. Последний вариант короче,​ да к тому же элегантнее. Реализуется он так: mov dword ptr DS:​[8042BF14h+1],​ 14C258h Листинг 8 код, "​закорачивающий"​ KeBugCheckEx Здесь: 8042BF14h — адрес начала функции KeBugCheckEx (на всех машинах разный),​ 1 – длина инструкции PUSH EBP, а 14C258h — машинный код, представляющий собой последовательность двух команд:​ POP EAX (58h)/RET 14h (C2h 14h 00h). Объединив все компоненты воедино мы получаем следующий папелац:​ .386 .model flat, stdcall .code DriverEntry proc moveax, cr0; грузимуправляющийрегистр cr0 врегистр eax mov ebx, eax; сохраняем бит WP в регистре ebx andeax, 0FFFEFFFFh; сбрасываем бит WP, запрещающий запись movcr0, eax; обновляем управляющий регистр cr0 mov dword ptr DS:​[8042BF14h+1],​ 14C258h 14C258 ; "​закорачиваем"​ KeBugCheckEx movcr0, ebx; восстанавливаембит WP mov eax, 0C0000182h; STATUS_DEVICE_CONFIGURATION_ERROR ret DriverEntry endp end DriverEntry Листинг 9 средство против BSOD, перед употреблением встряхнуть Вот такой маленький драйвер,​ а сколько данных он может спасти! Остается только откомпилировать его и можно приступать к испытаниям:​ ml /nologo /c /coff nobsod.asm link /driver /​base:​0x10000 /align:32 /​out:​nobsod.sys /​subsystem:​native nobsod.obj Листинг 10 ключи ассемблирования и линковки (используется пакет MASM из NT DDK) Если все было сделано правильно,​ на диске образуется файл nobsod.sys, который мы загрузим с помощью динамического загрузчика w2k_load. Загрузчик конечно,​ заругается матом, что мол ERROR и драйвер ни хрена не грузится,​ но так и должно быть. Все нормально! Мыжевозвратиликод STATUS_DEVICE_CONFIGURATION_ERROR! ​**Внимание:​ под ****VM Ware**** такой трюк не срабатывает,​ поскольку она не полностью эмулирует регистр ****cr****0 и таких шуток в упор не понимает,​ вызывая завис гостевой оси. В этом случае можно закомментировать все строки,​ относящиеся к регистру ****cr****0 и отключить защиту через реестр,​ создав соответствующий ключ "​Редактором Реестра"​. Кстати говоря,​ если на целевой машине установлен ****soft****-****ice ****– такой ключ уже создан и ничего делать не надо**. Рисунок 9 предсмертное сообщение VM Ware, которая она часто выдает,​ когда над ней проводят разные эксперименты,​ на которые она не была рассчитана Загрузим драйвер-убийцу,​ чтобы проверить справиться ли с ним наше средство против BSOD или нет… soft-ice (если он установлен) несколько раз всплывает. Вот зануда! Гоните его прочь, нажимая x или <​Ctrl-D>​. Но так или иначе, голубой экран уже не появляется! Система жутко тормозит,​ но все-таки работает. И это — главное! Плохо то, что теперь NT никак не может сигнализировать,​ что произошел системный сбой и что нужно побыстрее сматывать ласты, совершая shutdown. То есть почему это не может сигнализировать?​! Самое простое — добавить в нашу "​заплатку"​ на KeBugCheckEx несколько ассемблерных строк, которые "​бибикнут"​ спикером или сыграют "​семь-сорок"​ (как вариант "во поле береза стояла"​) на динамике. В принципе,​ можно даже разделить BugCheck коды на категории,​ каждой из которой будет соответствовать свое число гудков. За примерами далеко ходить не надо. Их можно выдрать из любого DOS-вируса. Техника программирования спикера на уровне ядра та же самая и она ничуть не изменилась. Да много что можно сделать! Главное — фантазию иметь! ​ ===== >>>​ врезка исключение и наказание ===== Всегда ли помогает "​шунтирование"​ KeBugCheckEx?​ Насколько это безопасно?​ Это очень, очень опасно и помогает далеко не всегда. Вот, например,​ рассмотрим следующий пример кода, позаимствованный из ядра: 00565201callExAllocatePoolWithTag;​ выделение памяти из лужи 00565206cmpeax,​ ebx; проверка успешности выделения памяти 00565208movds:​dword_56BA84,​ eax 0056520Djnzshortloc_56521C;​ → нам дали память! живем, мужики! 0056520Fpushebx;​ \ 00565210pushebx;​ + 00565211push6;​ +- с памятью вышел облом 00565213push5;​ +- отправляемся на небеса 00565215push67h;​ + 00565217callKeBugCheckEx;​ / 0056521C loc_56521C:;​ CODE XREF: sub_5651C1+4Cj 0056521Cleaeax,​ [ebp+var_C];​ продолжаем нормальное выполнение 0056521Fpushebx 00565220pusheax Листинг 11 фрагмент кода, при котором "​шунтирование"​ KeBugCheckEx заканчивается очень печально Система выделяет память из общего пула (в шутку называемого лужей) и если с памятью не облом, происходит нормальное продолжение,​ в противном случае вспыхивает голубой экран и хана. Допустим,​ мы "​закоротили"​ KeBugCheckEx,​ что тогда? Нас обломали на память,​ а мы продолжаем нормальное выполнение как ни в чем не бывало,​ обращаясь по указателю,​ который указывает в никуда. Возникает целый каскад вторичных исключений,​ а все структуры данных превращается в труху и система рушится окончательно. Вот так. ===== заключение ===== Мы пережили самую страшную систему катастрофу — BSOD, после которой нам все по плечу! Конечно,​ неразумно практиковать такой подход на сервере,​ но для рабочих станций он вполне приемлем. Проверено на мыщъх'​иной шкуре! Кстати говоря,​ некоторые вирусы,​ черви и rootkit'​ы используют схожую технику для маскировки своего присутствия в системе. Некорректно написанный вирус может вызвать синий экран и в системном журнале появится соответствующая запись,​ помогающая администратору разобраться с проблемой. Если же "​перемкнуть"​ KeBugCheckEx,​ то компьютер будет просто беспричинно тормозить (или виснуть),​ но в журнале ничего не появится! Рисунок 10 дзенский сад голубых экранов смерти //+