malware-discover

контрразведка с soft-ice в руках

крис касперски ака мыщъх, no-email

антивирусы (даже со всеми апдейтами) далеко не всегда распознают малварь и опытные хакеры доверяют только своим собственному хвосту, отладчику soft-ice и другому низкоуровневому инструментарию, позволяющему пробурить нору до самого ядра и разоблачить зловредные программы, где бы они ни скрывались!

Во времена MS-DOS ручная чистка компьютера — была обычном делом. Количество исполняемых файлов измерялось десятками, и существовало не так уж и много мест, пригодных для внедрения малвари (под малварью — от английского malware — здесь и далее по тексту подразумевается вредоносное программное обеспечение — вирусы, черви, шпионы и т. д.). С приходом Windows все изменилось. Из крохотного поселка операционная система превратилась в огромный, стремительно разрастающийся мегалополис, среди сотен тысяч файлов которого может спрятаться и слонопотам.

Обнаружить качественно спроектированную и грамотно заложенную закладку усилиями одного человека за разумное время — навряд ли возможно. Намного проще (да и быстрее) переустановить Windows с нуля. К счастью, качественная малварь — огромная редкость, практически не встречающая в живой природе и в основном приходится сталкиваться с пионерскими поделками, оставляющими после себя кучу следов и легко различимыми с помощью soft-ice и сопутствующих ему утилит.

Весь вопрос в том — как правильно ими пользоваться. Ну, установили мы soft-ice, нажали <CTRL-D>, увидели черный экран… Дальше-то что?! А вот дальше на сцене появился мыщъх и, плюхнувшись в кресло (предварительно подтолкнув под себя хвост), начинает делиться хакерскими секретами.

malware-discover_image_0.jpg

Рисунок 1 Windows – это целый мегалополис

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

Допустим, мы запустили файл сомнительного происхождения и хотим узнать – не натворил ли он чего в системе? Пуск  Найти  Файлы и Паки  Параметры Поиска  Файлы созданные за xxx последних дней (в нашем случае за один). Все изменения, произошедшие за последние сутки в системе становятся видны как на ладони! Как вариант: в FAR'е устанавливаем режим сортировки по дате создания (<CTRL-F8>) и заходим во все «злачные» каталоги типа WINNT, System32 и т. д. Файлы, созданные последними, будут в самом верху. Прием простой, как паровой котел, но чрезвычайно эффективный!

Рисунок 2 поиск файлов, созданных за последнее время, с помощью штатных средств Windows – в данном случае мы видим файл hldrrr.exe, «окопавшийся» в каталоге C:\WINNT\system32

Конечно, чем позже мы спохватимся, тем сложнее отличить «легальные» файлы от «нелегальных», особенно если на компьютер ставится большое количество самого разнообразного программного обеспечения. Но все файлы, устанавливаемые инсталлятором (где он их не размещал — в ProgramFiles, WINNT или System32), имеют одну и ту же дату создания с небольшим разбросом по времени (ведь файлы создаются не параллельно, а последовательно), поэтому их стразу можно исключить из списка подозреваемых. А оставшиеся — подвергнуть тщательному допросу (см. врезку «признаки вредоносных файлов»).

Рисунок 3 исследование даты создания файлов при помощи FAR'а

Естественно, дата создания файла элементарно изменяется средствами win32-API и малвари при желании ничего не стоит замаскироваться. Однако, на NTFS-разделах каждый файл обладает множеством «невидимых» атрибутов, до которых нельзя дотянуться через API. В частности, атрибут 30h ($FILE_NAME) помимо стандартных времен создания/модификации/последнего обращения, хранит время последней модификации данной записи MFT (MasterFileTable – специального мастерфайла, содержащего информацию обо всех остальных объектах файловой системы). У «честных» файлов время создания и время последней модификации MFT всегда совпадает, а если это не так — мы имеем дело с подделкой. Еще существует атрибут 10h ($STANDARD_INFORMATION), так же хранящий информацию о времени создания/модификации/последнего доступа файла и времени последней модификации MFT, однако, в отличии, от атрибута 30h, здесь время последней модификации MFT автоматически обновляется всякий раз, когда файлу выделяется новая порция кластеров, а потому со временем его создания оно может и не совпадать.

Существует не так уж и много утилит, отображающих содержимое MFT в удобочитаемом виде. Однаизних — NtExplorerот Runtime Software. Грубо говоря, это NortonDiskEditor, но только под NTFS. К сожалению, NtExplorer не поддерживает ни плагинов, ни скриптов, поэтому, быстро вывести список файлов с поддельными датами создания не получается и каждый из них приходится перебирать «руками», что очень сильно напрягает, но… NTFS совсем несложная (по нынешним меркам) файловая система, а все ее основные структуры давным-давно реконструированы, документированы и выложены в Сеть: http://linux-ntfs.sourceforge.net. Создание программы, выполняющий автоматизированный поиск «поддельных» файлов у не займет много времени, тем более, что подробное описание NTFS можно найти в моей книге «la technique de la restitution des données». В общем, дорогу осилит идущий!

Рисунок 4 обнаружение файла с поддельным временем создания при помощи RuntimeNtExplorer — FAR утверждает, что файл создан 07.05.2004, в то время как соответствующая ему запись в MFT модифицировалась 18.07.2006

Обычно, малварь создает свой собственный процесс (реже — внедряется в чужие), при этом у нее возникает вполне естественное желание скрыть этот процесс, убрав его из «Диспетчера Задач» и прочих системных утилит. Как это она делает? Для предоставления информации о процессах NT поддерживает два механизма: набор документированных процедур TOOLHELP32 (доставшийся в «наследство» от 9x), реализованных в KERNEL32.DLL, и недокументированная функция NtQuerySystemInformation, экспортируемая NTDLL.DLL, и представляющую собой тонкую «обертку» вокруг системного сервиса 97h, реализованного в NTOSKRNL.EXE. На самом деле, главная функция TOOLHELP32 — CreateToolhelp32Snapshot — полностью опирается на NtQuerySystemInformation, так что фактически механизм у нас один, только интерфейсы разные.

Малварь может легко перехватить процедуры Process32First/Process32Next из TOOLHELP32, только это ей ничего не даст, поскольку, практически все утилиты («Диспетчер Задач», FAR и даже примитивный tlist.exe из SDK) работают исключительно через NtQuerySystemInformation (что легко подтверждается установкой точки останова в soft‑ice). Однако, перехватить NtQuerySystemInformation с прикладного уровня ничуть не сложнее, чем процедуры из набора TOOLHELP32! Существует множество путей как это сделать:

  1. модифицировать NTDLL.DLL на диске, установив в начало функции NtQuerySystemInformation команду перехода на свой обработчик (расположенный где-нибудь в свободном месте внутри NTDLL.DLL), и «вычищающий» из выдаваемой ею информации всякое упоминание о себе. способ простой как барабан, но грязный и легко обнаруживаемый путем дизассемблирования NTDLL.DLL или сравнением ее с оригиналом; так же малвари придется противостоять SFC и установке ServicePack'ов некоторые из которых обновляют NTDLL.DLL;
  2. модифицировать NTDLL.DLL!NtQuerySystemInformation в памяти; поскольку NT поддерживает механизм copy-on-write, автоматически «расщепляющий» страницы памяти при записи, модификация NTDLL.DLL приобретает локальный характер, ограниченный контекстом процесса-писателя. то есть, чтобы воздействовать на «Диспетчер Задач», в него прежде необходимо внедриться! вот один из возможных сценариев: малварь создает свою DLL и прописывает ее в следующую ветку системного реестра: HKLM\Software\Microsoft\Windows NT\CurrentVersion\windows\AppInit_DLLs, в результате чего эта DLL после будет отображаться на _все_ процессы. для большей скрытности можно модифицировать NTDLL.DLL только в контексте тех процессов, которые используются для вывода списка задач (taskmng.exe, far.exe, tlist.exe и т. д.). в этом случае, заглянув отладчиком внутрь NtQuerySystemInformation, мы не найдем никаких следов малвари! можно, конечно, проверить AppInit_DLLs, но это не единственный способ внедрения, так что задача выявления малвари резко усложняется;
  3. модифицировать таблицу импорта taskmng.exe(«Диспетчер Задач»), proclist.dll (плагин FAR'а, ответственный за вывод списка процессов), tlist.exe на диске (или в памяти), подменив вызовов NtQuerySystemInformation своей собственной функций оберткой. такой перехват легко обнаруживается путем сравнения исполняемых файлов с их образом памяти, который может быть получен путем снятия дампа утилитой типа PE‑TOOLS или старым добрым Procdump'ом, к тому же малвари придется дополнительно перехватывать GetProcAddress, чтобы отслеживать динамическую загрузку NTDLL.DLL;

При наличии прав администратора, малварь может проникнуть в NTOSKRNL.EXE и подменить сервис 97h своим собственным обработчиком. Тогда с прикладного уровня обнаружить зловредный процесс уже не удастся и придется спускаться на уровень ядра, подробно рассмотренное в разделе. «восстановление SST».

Soft-Ice – единственная из всех известных мыщъх'у программ, которая не использует NtQuerySystemInformation и для отображения списка процессов самостоятельно разбирает базовые структуры операционной системы, а потому легко выявляет скрытые процессы.

Рисунок 5 soft-ice показывает процесс sysrtl, отсутствующий в «Диспетчере Задач»

Теоретически, малварь может внедриться в soft-ice и перехватить любую из его команд (например, команду «PROC»), действуя по той же схеме, что и IceExt/IceDump (благо, что обе утилиты распространяются в исходных текстах), но в живой природе такие «монстры» пока что не встречались. Можно надеяться, что IceExt, скрывающий soft-ice от большинства защит, скроет его и от малвари, однако, при этом остается угроза сигнатурного поиска отладчика в памяти; к тому же на хакерских форумах не первый год обсуждается гипотетический алгоритм скрытия, перехватывающий функции переключения контекста и «вытирающий» себя в промежутках между ними. Однако, реализация такого проекта упирается в непреодолимые практические трудности. Формат процессорных структур непостоянен и меняется от одной версии системы к другой, к тому же с ними взаимодействуют множество недокументированных функций, вызываемых в разное время из различных мест и малварь, пытающаяся замаскироваться, постоянно обрушивает систему в BSOD, чем сразу себя и разоблачает.

Таким образом, будем считать, что связки из soft-ice + IceExt для просмотра всех процессов (включая скрытые) вполне достаточно.

В последнее время все чаще и чаще малварь не создает для себя отдельный процесс (который очень легко заметить), а предпочитает внедряться в один из уже существующих. Для этого используются два механизма. В первом малварь выделяет в целевом процессе блок памяти функцией VirtualAllocEx, копирует себя через WriteProcessMemory и создает удаленный поток посредством CreateRemoteThread. Второй механизм начинается так же, как и первый, только вместо создания удаленного потока, малварь останавливает текущий поток процесса, и изменяет регистр EIP функцией SetThreadContext (естественно, предварительно сохранив его оригинальное значение через GetThreadContext), передавая управление своей собственной процедуре, вызывающей CreateThread, восстанавливающей EIP и «размораживающей» ранее остановленный поток. Первый механизм работает только на NT, второй — на всех 32-разядных системах семейства Windows.

Как обнаружить такой метод вторжения? Естественно, количество потоков атакуемого процесса увеличивается на единицу, однако, это еще не показатель. Никогда и никогда не может сказать точно сколько у приложения должно быть потоков. Даже его непосредственный разработчик! Проведем простой эксперимент. Запустим «Блокнот» и, переключившись на Диспетчер Задач, увидим один-единственный поток. Теперь зайдем в меню «файл» и скажем «открыть». Количество потоков внезапно подскакивает аж по пяти! Закрываем окно открытия файла — один поток исчезает, остаются четыре. Что это за ерунда такая?! Оказывается, все дело в динамических библиотеках SHLWAPI.DLL, RPCRT4.DLL и OLE32.DLL, «обслуживающих» окно и порождающих свои собственные, дочерние, потоки. Некоторые драйвера так же могут порождать потоки в чужих приложениях (как правило, с целью вызова прикладных API). Нам необходимо как-то научиться отличать «легальные» потоки от «нелегальных», иначе наша борьба с малварью обречена.

Вот идея, простая как 3х дюймовая дискета: стартовый адрес легального потока лежит в пределах страничного имиджа (в секции .code и .text), а нелегального — в куче, т. е. области динамической памяти, выделенной функцией VirtualAllocEx. Чтобы разоблачить нелегалов, нам, прежде всего, понадобится карта адресного пространства. soft-ice отображает ее не в самом наглядном виде и лучше воспользоваться OllyDbg или PE-TOOLS.

В OllyDbg в меню «file» выбираем «attach» и указываем процесс, чьи потоки мы будем исследовать. После успешного присоединения к процессу говорим «view»  «memory» или давим <ALT-M>. Получаем карту следующего вида:

Рисунок 6 карта памяти «Блокнота», отображенная отладчиком OllyDbg

Регионы, помеченныекак «Priv» (сокращениеот «private») принадлежатблокамдинамическойпамяти, «map» (сокращениеот «mapping») – проекциямфайлов, созданныхфункциямиCreateFileMapping/MapViewOfFile), «Imag» (сокращениеот «imaging») – страничнымимиджамисполняемыхфайловилидинамическихбиблиотек.

В PE-TOOLS для той же цели необходимо выделить процесс и в контекстом меню выбрать «dumpregion», при этом на экране появится диалоговое окно с картой памяти — не такой подробное как у OllyDbg, но для нашей задачи вполне удовлетворительное.

Рисунок 7 карта памяти «Блокнота», отображенная утилитой PE-TOOLS

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

#include <stdio.h>

#include <windows.h>

код потока, который ничего не делает, а только мотает цикл thread(){while(1);} main() { void *p; переменная многоцелевого назначения

создаем «честный» поток CreateThread(0,0,(void*)&thread,0x999,0,&p); создаем «нечестный» поток так, как это делает malware:

выделяем блок памяти из кучи, копируем туда код потока ивызываем CreateThread

p = VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

memcpy(p,thread,0x1000);CreateThread(0,0,p,0x666,0,&p);

ждем нажатия на ENTER gets(&p); } Листинг 1 исходный код демонстрационной программы va_thread.c, компилируемой с настройками по умолчанию Компилируем, запускаем, заходим в soft-ice, даем команду «THREAD -x» (вывод детальной информации о потоках) и смотрим полученный результат: :THREAD -x Extended Thread Info for thread 374 KTEB873CFDA0TID:374Process: va_thread(11C) Start EIP:KERNEL32!SetUnhandledExceptionFilter+001A (77E878C1) User Stack: 00030000 - 00130000Stack Ptr:0012FD24 Extended Thread Info for thread 238 KTEB:82007020TID:238Process: va_thread(11C) Start EIP:KERNEL32!CreateFileA+00C3 (77E92C50) User Stack:00420000 - 00520000Stack Ptr:FFFFFFFF Extended Thread Info for thread 30C KTEB:82007AC0TID:30CProcess: va_thread(11C) Start EIP:KERNEL32!CreateFileA+00C3 (77E92C50) User Stack:00530000 - 00630000Stack Ptr: FFFFFFFF Листинг 2 информация о потоках, сообщенная soft-ice (приводится в сокращенном виде) Вот так номер! soft-ice не смог определить истинные стартовые адреса потоков, заблудившись в недрах KERNEL32.DLL. Что ж, попробуем другой инструмент — ProcessExplorer от Марка Руссиновича, весьма нехило разбирающегося во внутренностях операционных систем от Microsoft (и даже участвующим в написании книги «WindowsNTInternals». Скачиваем (совершенно бесплатно) Process Explorer, запускаем, наводим курсор на «va_thread.exe», далее в контекстом меню выбираем пункт «Properties» и в открывшемся диалоговом окне переходим к вкладке «Threads». Рисунок 8 ProcessExplorer от Марка Руссиновича не смог определить стартовый адрес «нечестного» потока Что мы видим? Адреса двух потоков определены верно. Первый: va_thread.exe+0x1405, судя по адресу, представляет основной поток (адрес совпадает с точкой входа, что легко проверить в hiew'е). Второй: va_thread.exe+0x1000 — это «честно» созданный поток (что опять-таки проверяется по адресу в hiew'е), а вот третий – KERNEL32.DLL+0xB700 – это «нечестный» поток (а чем он еще может быть?!), только его стартовый адрес определен неправильно! Призываем на помощь OllyDbg и пытаемся разобраться в ситуации самостоятельно, без всех этих прелестей автоматизации и прочих чудес технического прогресса. Подключившись к процессу va_thread.exe, в меню «view» выбираем пункт «thread» и… обнаруживаем не три (как ожидалось), а целых четыре потока! IdentEntryData blockLast errorStatusPriority 050C7943B7007FFDB000ERROR_SUCCESSActive32 + 0 0558000000007FFDC000ERROR_SUCCESSSuspended32 + 0 055C000000007FFDE000ERROR_SUCCESSSuspended32 + 0 0578000000007FFDD000ERROR_SUCCESSSuspended32 + 0 Листинг 3 информация о четырех потоках, выданная OllyDbg Стартовый адрес (entry) определен только для одного из потоков — 50Ch да и тот, вероятно, служит для связки отлаживаемого процесса с OllyDbg. Стартовые адреса остальных потоков выставлены в ноль, но ведь это же не так!!! Щелкам мышью по потоку c идентификатором 558h (естественно, при следующем запуске программы, идентификаторы потоков будут другими) и получаем код следующего содержания, который (судя по карте памяти), принадлежит страничному имиджу, следовательно, это — легальный поток. 40100055PUSH EBP 4010018B ECMOV EBP,ESP 401003B8 01000000MOV EAX,1 40100885C0TEST EAX,EAX 40100A74 02JE SHORT va_threa.0040100E 40100CEB F5JMP SHORT va_threa.00401003 Листинг 4 код потока 558h, находящегося в пределах страничного имиджа Переходим к окну стека, перемещая ползунок на самый низ. На дне стека видим аргумент, переданный потоку (второе двойное слово, в данном случае равное — 999h) и… стартовый адрес потока, лежащий в третьем двойном слово, и в данном случае равный 401000h, что полностью согласуется с листингом 1. (На самом деле, в зависимости от способа создания потока стартовый адрес может лежать как в третьем, так и во втором слове, поэтому автоматические утилиты и путаются). 51FFDCFFFFFFFF End of SEH chain 51FFE079481F54 SE handler 51FFE479432B08 KERNEL32.79432B08 51FFE800000000 51FFEC00000000 51FFF000000000 51FFF400401000 va_threa.00401000; стартовый адрес потока 558h 51FFF800000999;  аргумент, переданный потоку 51FFFC00000000;  дно пользовательского стека потока Листинг 5 на дне пользовательского стека пока 558h лежит стартовый адрес, вместе с переданным ему аргументом Переходим к следующему потоку — 55Ch. Код выглядит точно так как и раньше (ведь мы запустили два экземпляра одной и той же функции!), а вот содержимое дна стека слегка изменилось: 62FFDCFFFFFFFF End of SEH chain 62FFE079481F54 SE handler 62FFE479432B08 KERNEL32.79432B08 62FFE800000000 62FFEC00000000 62FFF000000000 62FFF400520000; стартовый адрес потока 55Сh 62FFF800000666;  аргумент, переданный потоку 62FFFC00000000;  дно пользовательского стека потока Листинг 6 на дне пользовательского стека пока 55Ch лежит стартовый адрес, вместе с переданным ему аргументом Как мы помним, 666h – это аргументы, переданные «нечестной» копии потока, а 520000h – его стартовый адрес, принадлежащий (если верить карте памяти) блоку памяти, выделенному функцией VirtualAlloc: AddressSizeOwnerSectionContainsTypeAccessInitial 4000001000va_threaPE headerImagRRWE 4010004000va_threa.textcodeImagRRWE 4050001000va_threa.rdataimportsImagRRWE 4060002000va_threa.datadataImagRRWE 4100002000MapRR 51E0001000PrivRWGuarRW 51F0001000stack of thrPrivRWGuarRW 5200001000PrivRWERWE 62E0001000PrivRWGuarRW Листинг 7 карта памяти процесса va_thread (область памяти, принадлежащая потоку 55Ch, выделена полужирным) Последний поток — 578h представляет собой основной поток программы и хранит своей стартовый адрес _не_ в третьем, а во втором (!) двойном слове: 12FFE0FFFFFFFF End of SEH chain 12FFE479481F54 SE handler 12FFE879432B18 KERNEL32.79432B18 12FFEC00000000 12FFF000000000 12FFF400000000 12FFF800401405 va_threa.<ModuleEntryPoint>;  стартовый адрес потока 578h 12FFFC00000000;  дно пользовательского стека потока Листинг 8 поток 578h хранит свой стартовый адрес не в третьем, а во втором двойном слове! Свершилось! Мы научились быстро и просто определять стартовые адреса потоков, надежно отличая «левых» от «правых». Кстати, чтобы каждый раз не сверяться с картой памяти можно использовать следующий трюк. Если при нажатии стартового адреса в контекстом меню OllyDbg присутствует строчка «FollowinDisassembler» – он принадлежит страничному имиджу (т. е. легальному потоку) и, соответственно, наоборот. Рисунок 9 содержимое дна стека «честного» (слева) и «нечестного» потока (справа), у «нечестного» отсутствует пункт «FollowinDisassembler» в контекстом меню На самом деле, праздновать победу еще рано. Умная малварь может нас легко обмануть. Самое простое — подменить истинный стартовый адрес так, чтобы он указывал внутрь страничного имиджа целевого процесса (но в этом случае он должен совпадать с началом какой-нибудь процедуры, иначе мы тут же разоблачим обман). Более умная малварь использует хитрый способ внедрения — находит в целевом процессе функцию по стандартному прологу PUSH EBP/MOV EBP, ESP (55h/8Bh ECh), вставляет в ее начало jump на выделенный из кучи блок, где размещено ее тело, создает новый поток, начинающийся с jump, и тут же восстанавливает оригинальное содержимое хакнутой функции убирая jump и возвращая стандартный пролог. Еще остается вариант загрузить внутрь процесса динамическую библиотеку, принадлежащую малвари и запустить внутри нее новый поток. Во всех этих случаях анализ стартового адреса не даст никакого результата и внедрение зловредного кода останется незамеченным и чтобы быть уверенным на все 100% необходимо трассировать каждый из потоков на предмет проверки его лояльности. Потоки, порожденные малварью, либо шпионят за клавиатурой, либо открывают backdoor, либо рассылают спам. Проблема в том, что потоков (легальных) очень много, а современная малварь пишется уже не на ассемблере, а черт знает на чем (DELPHI, VisualBASIC) и полный анализ требует уймы времени, однако, как говорилось выше, умная малварь — большая редкость и подделкой стартовых адресов потоков никто не занимается. ===== восстановление SST ===== Для сокрытия своего присутствия в системе, малварь нередко внедряется в ядро системы и перехватывает один или несколько сервисов, например, функции NtQuerySystemInformation, про важность которой мы уже говорили. Ловить малварь на такой системе все равно, что бороться с ком. партией под ее руководством. Дизассемблирование NTDDLL.DLL показывает, что большинство низкоуровневых функций реализованы как «переходники» к функциям ядра, интерфейс с которым осуществляется либо посредством прерывания INT 2Eh (NT, W2K), либо машинной командой SYSENTER (XP и выше). .text:77F95BBDpublic ZwQuerySystemInformation .text:77F95BBDZwQuerySystemInformation proc near .text:77F95BBDarg_0= byte ptr 4 .text:77F95BBD .text:77F95BBD B8 97 00 00 00moveax, 97h; NtQuerySystemInformation .text:77F95BC2 8D 54 24 0leaedx, [esp+arg_0] .text:77F95BC6 CD 2Eint2Eh .text:77F95BC8 C2 10 00retn10h .text:77F95BC8ZwQuerySystemInformation endp Листинг 9 функция ZwQuerySystemInformation в действительности представляет «переходник» к системному сервису 97h Когда происходит вызов прерывания, процессор автоматически переключается с прикладного уровня (ring 3) в режим ядра (ring 0), передавая управление функции KiSystemService, реализованной внутри NTOSKRNL.EXE и опирающейся на Таблицу Системных Дескрипторов, она же SDT (SystemDescriptorTable). Собственно, дескрипторов в ней всего два — один для системных вызовов, другой — для драйвера win32k.sys, куда упрятали весь графический интерфейс. На серверах добавляется и третий дескриптор — IIS, назначение которого ясно из его названия. Рисунок 10 механизм реализации системных вызовов Дескриптор, отвечающий за системные вызовы, указывает на SystemServiceTable (Таблица Системных Вызовов), представляющую собой простой массив указателей на функции, которые _очень_ легко изменить (естественно, делать это нужно либо из режима ядра, либо с прикладного уровня, обратившись к псевдоустройству PhysicalMemory). Найти таблицу системных вызовов в памяти очень просто. «Скармливаем» NTOSKRNL.EXE функции LoadLibrary и, используя возвращенный ей дескриптор, определяем адрес экспортируемой переменной KeServiceDescriptorTable через GetProcAddress(или разбираем таблицу экспорта вручную). Первое же двойное слово содержит указатель на SST, поэтому эффективный адрес требуемого системного сервиса по его «магическому» номеру определяется так: addr == *(DWORD *)(KeServiceDescriptorTable[0] + N*sizeof(DWORD)), где N – номер сервиса, а addr – его эффективный адрес. Продемонстрируем эту технику на примере soft-ice: :dd :d KeServiceDescriptorTable 0008:8046AB80 804704D8 00000000 000000F8 804708BC ..G………..G. :d 804704D8 0008:804704D8 804AB3BF 804AE86B 804BDEF3 8050B034 ..J.k.J…K.4.P. 0008:804704E8 804C11F4 80459214 8050C2FF 8050C33F ..L…E…P.?.P. 0008:804704F8 804B581C 80508874 8049860A 804FC7E2 .XK.t.P…I…O. :u *(804704D8 + 97*4) ntoskrnl!NtQuerySystemInformation 0023:804BF933PUSHEBP 0023:804BF934MOVEBP, ESP 0023:804BF936PUSHFF 0023:804BF938PUSH804043A0 0023:804BF93DPUSHntoskrnl!_except_handler3 Листинг 10 протокол работы с soft-ice, демонстрирующий получение адреса системного сервиса 97h Как мы видим, в данном случае, функция NtQuerySystemInformation никем не перехвачена, что очень хорошо! Чтобы просмотреть содержимое SST в soft-ice достаточно дать команду «NTCALL». На «стерильной» машине _все_ вызовы указывают внутрь NTOSKRNL.EXE, а если это не так, то их кто-то перехватил. Это может быть как зловредная малварь, так и вполне безобидный драйвер какого-нибудь защитного механизма или, например, брандмауэр. Для восстановления SST можно использовать ее копию, хранящуюся внутри NTOSKRNL.EXE, правда, найти ее на диске значительно сложнее, чем в памяти. Проще всего использовать отладочные символы (которые можно бесплатно сгрузить с сервера http://msdl.microsoft.com/download/symbols с помощью библиотеки dbghelp.dll, входящей в состав бесплатного пакета DebuggingTools). Адресу SST соответствует метка _KiServiceTable и в моей версии системы она располагается по адресу 4704D8h (в файле): .data:004704D8 BF B3 4A 00 _KiServiceTabledd offset _NtAcceptConnectPort@24 .data:004704DC 6B E8 4A 00dd offset _NtAccessCheck@32 .data:004704E0 F3 DE 4B 00dd offset _NtAccessCheckAndAuditAlarm@44 Листинг 11 копия таблицы системных вызовом, хранящаяся внутри NTOSKRNL.EXE А если отладочных символов нет? Тогда находим все перекрестные ссылки к KeServiceDescriptorTable (т. е. просто ищем ее адрес, записанный с учетом обратного порядка байт на x86, задом наоборот). Одна из них ведет к инструкции типа «mov [mem], imm32» и представляет собой смещение оригинальной SST (imm32), записываемой в KeServiceDescriptorTable[0]. Как нетрудно убедиться дизассемблером, изначально SDT пуста и инициализируется на стадии загрузки ядра не экспортируемой функцией KiInitSystem. .data:0046AB80 ; Exported entry 516. KeServiceDescriptorTable .data:0046AB80 public _KeServiceDescriptorTable .data:0046AB80 _KeServiceDescriptorTable dd 0 Листинг 12 неинициализированная SDT-таблица, хранящаяся в NTOSKRNL.EXE Ниже, в качестве примера, продемонстрирован поиск SST в hiew'e: Рисунок 11 поиск SST в файле NTOSKRNL.EXE по перекрестным ссылкам Если лень восстанавливать SST вручную, можно воспользоваться бесплатной утилитой «Win2K/XP SDT Restore» от Tan Chew Keong, результат работы которой продемонстрирован ниже: Рисунок 12 официальный сайт утилиты SDTRestore… Рисунок 13 …и результат ее работы на зараженной малварью машине Пользуясь SDT Restore, следует иметь ввиду, что уже появились rootkit'ы, способные ее обходить. Во-первых, для поиска оригинальной SST, утилита SDT Restore использует простой, но ненадежный способ, обращаясь к KeServiceDescriptorTable[0], которую зловредная малварь может и подменить (см. http://hi-tech.nsys.by/35/), во-вторых, само восстановление SST происходит с прикладного уровня через псевдоустройство PhysicalMemory, отображаемое в память посредством native-API функции NtMapViewOfSection, легко перехватываемую как с прикладного, так и с ядреного уровней, после чего перехватчику остается проверить: не вызывается ли NtMapViewOfSection с дескриптором PhysicalMemory и если да, то либо заблокировать доступ, либо имитировать восстановление, не производя его в действительности (см. http://www.rootkit.com/newsread.php?newsid=200). Так же следует учитывать, что некоторые защиты «вешаются» на вектора прерываний, описанные в таблице IDT, и проверяют перехваченные сервисы, например, каждый тик таймера. В правильной IDT (просмотреть которую можно одноименной командой в soft-ice) все вектора указывают внутрь NTOSKRNL.EXE или HAL.DLL. :IDT IntTypeSel:OffsetAttributes Symbol/Owner IDTbase=80036400 Limit=07FF 0000IntG320008:804625E6DPL=0Pntoskrnl!Kei386EoiHelper+0590 0001IntG320008:80462736DPL=3Pntoskrnl!Kei386EoiHelper+06E0 0002IntG320008:0000144EDPL=0P 0003IntG320008:80462A0EDPL=3Pntoskrnl!Kei386EoiHelper+09B8 Листинг 13 просмотр IDT в soft-ice В дополнение к этому, малварь может устанавливать в начало (или даже середину!) некоторых ядерных функций jump на свой обработчик, контролирующий целостность перехваченной SST/IDT. Для выявления такого способа перехвата, необходимо сравнить образ ядра с файлом NTOSKRNL.EXE, что можно осуществить при помощи утилиты PE-TOOLS с плагином eXtremeDumper или сдампить ядро непосредственно из самого soft-ice (что намного надежнее) с установленным расширениями IceExt или IceDump. ===== заключение ===== Вот два основных пути проникновения малвари на компьютер — файлы, запускаемые самим пользователем и дырявое программное обеспечение (последнее преимущественно относится к IE и линейке NT). И если первое еще можно как-то предотвратить (не открывать никаких потенциально опасных вложений, полученных по почте; пользоваться приложениями только от проверенных поставщиков; не скачивать crack'и написанные непонятно кем и неизвестно для чего), то от дыр никуда не уйти. Даже если пересесть с IE на Lynx, останутся дефекты оси, коих в NT ну просто до фига и постоянно обнаруживаются все новые, ранее неизвестные. То есть, это _нам_ они неизвестные, а кому-то очень даже хорошо известные и эксплуатируемые. Никто не может чувствовать себя в безопасности, если не будет регулярно проверять все закоулки системы своими руками, хвостом, ну и конечно, могучим soft-ice со всей его свитой. ===== »> врезка выявление stealth-компонентов ===== ===== »> врезка сетевые соединения ===== ===== »> врезка ссылки на программы, упомянутые в статье ===== - NtExplorer: - http:www.runtime.org/gdbnt.zip; - OllyDbg: - http:www.ollydbg.de; - Process Explorer: - http://www.sysinternals.com/Utilities/ProcessExplorer.html__; - IceExt: - http://stenri.pisem.net__; - IceDump: - http://programmerstools.org/system/files?file=icedump6.026.zip__; - PE-TOOLS (base): - http://www.wasm.ru/baixado.php?mode=tool&id=124__; - PE-TOOLS (updates): - http://neox.iatp.by__; - eXtremeDumper: - http://neox.iatp.by/eXtremeDumper.zip__; - SDT Restore: - http://www.security.org.sg/code/sdtrestore.html__;