Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #91, ИЮЛЬ 2006 г.

Ядерные шалости

Крис Касперски ака мыщъх

Хакер, номер #091, стр. 091-082-4


КОД

Листинг 3: перехват отладочного вывода

.text:00010646 B8 D4 0A 01+ mov eax, offset loc_10AD4 ; jmp:DbgPrint

.text:0001064B 8B 40 02 mov eax, [eax+2] ; операнд jmp:[DbgPrint]

.text:0001064E A3 A8 0B 01+ mov _pDbgPrn, eax

.text:00010653 8B 08 mov ecx, [eax] ; DbgPrint

.text:00010655 89 0D A8 0B+ mov _pDbgPrn, ecx

.text:0001065B 8A 41 01 mov al, [ecx+1] ; второй байт DbgPrint

.text:0001065E 3C 8D cmp al, 8Dh ; PUSH EBP/MOV EBP,ESP

.text:00010660 75 04 jnz short loc_10666

Перехват отладочного вывода, осуществляемый «врезкой» jump'а поверх пролога системной функции.

Поскольку функция DbgPrint не относится к числу интенсивно вызываемых, то вероятность, что какой-то поток вызовет ее одновременно с установкой перехватчика, достаточно невелика, и все «как бы» работает. Однако, если один или несколько драйверов начнут злоупотреблять отладочным выводом, вероятность краха системы значительно возрастет, поэтому в последующих версиях Руссинович отказался от небезопасного способа перехвата и перешел на модификацию таблицы экспорта ntoskrnl.exe. Новичкам, похитившим этот пример и попытавшимся употребить его для перехвата функций ввода/вывода, пришлось намного хуже, и голубые экраны выпрыгивали только так!

Off-line patch

Кромсать ядро статическим способом очень просто. Открываем файл ntoskrnl.exe функций CreateFile, а затем действуем через ReadFile/WriteFile. Проблема синхронизации потоков отпадает сама собой, поскольку правка осуществляется еще до загрузки образа в память, однако техника перехвата от этого ничуть не упрощается. Ведь, чтобы записать jump поверх ядерной функции или подменить таблицу экспорта, необходимо явным образом указать адрес нашего обработчика (расположенного, как правило, в драйвере), но на момент статической правки ядра местоположение драйвера в памяти еще неизвестно!!! Приходится шаманить. Например, можно поступить так: найти в ядре свободное место и внедрить туда крошечный перехватчик-диспетчер, определяющий, был ли загружен «наш» драйвер. Если нет — управление возвращается оригинальным ядерным функциям, в противном случае — нашему драйверу. При перехвате нескольких функций диспетчер должен смотреть, откуда приходит вызов и какой процедуре драйвера их следует передавать. Вот почему вместо jmp'а программисты используют call. Диспетчер стягивает с вершины стека адрес возврата, смотрит, откуда пришел вызов, и все понимает.

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

Назад на стр. 091-082-3  Содержание  Вперед на стр. 091-082-5