codingАлександр Эккерт/ aleksandr-ehkkert@rambler.ru /С фильтром,пожалуйста!Азбука руткит-кодера: фильтры в ядре WindowsСкажи мне, часто ли бессонными ночами ты мечтал накодить программу,которая бы без ведома пользователя тихонечко отфильтровывала сетевойтрафик? Или обращения к файловой системе? И при этом модифицировалабы файлы на лету? Или подменяла веб-страницы?егодня мы поговорим об основополагающей руткит-техникеC — фильтрах в режиме ядра Windows. Тема эта очень захватывающаяи при прямых руках может принести неслыханныедивиденды. Тебе покорятся и невидимая модификация файловна диске, и подмена содержимого веб-страниц и редирект сетевоготрафика, в общем — полный список!Немного теории, или «устройство» в WindowsЧто такое объект «устройство»? В OC Windows диспетчер ввода-выводаопределяет «устройство» (\Device\) для представлениятого физического, логического или виртуального устройства, чей драйвербыл загружен в систему. Согласно существующим правилам, каждый драйвердолжен создавать объект «устройство» для того физического (виртуального)устройства, которым он управляет. Формат объекта «устройство» определяетсяструктурой DEVICE_OBJECT, описание которой ты без труда найдешь в DDK.Если быть очень кратким, то объекты устройства в ядре Windows могут быть:• Physically Device Object (PDO) — представляет собой какое-то физическоеустройство в системе;• Functional Device Object (FDO) — реализует всю функциональную нагрузкукакого-то устройства;096• Filtering Device Object (FiDO) — объект устройства, создаваемый фильтрдрайвером.Из вышеописанного следует, что утверждение «мышь имеет драйвер»— неоднозначно. Открою страшную тайну — мышью PS/2 управляет неодин драйвер, а целых три: mouclass.sys i8042.sys ACPI.sys. И так совсеми устройствами в Windows! Рассказывать про организацию стека устройствв ядре Windows мы сейчас не будем — уж больно обширная тема,да и в интернете информации можно добыть предостаточно (например,поищи книгу Уолтера Оуни «Programming the Microsoft Windows DriverModel»). В программировании драйверов под Win есть интересная особенность— можно написать драйвер-фильтр, который реально встроитьв указанную цепочку драйверов и получить неограниченный контрольнад любым устройством в Windows, будь то клавиатура, файловая системаили сетевая карта!IRP-пакетыПодсистема ввода-вывода в ядре Windows управляется путем пересылкимежду устройствами своеобразных пакетов — так называемых IRP-пакетов(I/O Request Packet).При вызове какого-либо системного сервиса (например, чтении или за-xàêåð 07 /115/ 08
codingФильтруем сетевойтрафикписи файла) диспетчер ввода-вывода создает IRP-пакет и отправляет егов путешествие вниз по стеку устройств, отвечающих за файловую систему.Результат обработки запроса также вернется инициатору в виде IRP-пакета,который будет содержать всю необходимую информацию.Глубоко не вдаваясь в назначение полей IRP-пакета, отметим, что онсостоит из двух логических частей: фиксированной, неизменяемой припрохождении стека, и динамичной — так называемого стека ввода-вывода,где хранится информация, изменяемая при прохождении от устройствак устройству.В состав структуры DRIVER_OBJECT входит массив MajorFunction. Внего помещаются «dispatch routines» — указатели на процедуры,предназначенные для обработки разных типов IRP-пакетов. Каждыйэлемент этого массива соответствует своему типу IRP. Если, например,драйверу необходимо обрабатывать запрос типа IRP_MJ_READ, уведомляющийо чтении файла, то он должен поместить в соответствующуюпозицию массива MajorFunction указатель на функцию, которой ибудут направляться запросы этого типа. Если такая функциональностьдрайверу не нужна, то этот элемент массива MajorFunction можнооставить равным NULL. Всего в массиве 28 элементов. Опишем самыеважные из них:1) IRP_MJ_CREATE — код передается при создании нового объекта либопри обращении к уже существующему объекту-файлу и соответствуетWin32‐функции CreateFile.2) IRP_MJ_READ — код передается при чтении существующего объектафайлаи соответствует Win32‐функции ReadFile().3) IRP_MJ_WRITE — код передается по стеку устройств при записи всуществующий файл и соответствует, как ты уже понял, Win32‐функцииWriteFile().4) IRP_MJ_DEVICE_CONTROL — код будет передан в стек в тех случаях,когда устройство контролируется посредством IOCTL-запросов. Это, кстати,один из способов «общения» драйвера с юзермодным приложением.Соответствует Win32‐функции DeviceIoControl().5) IRP_MJ_INTERNAL_DEVICE_CONTROL — код пересылается при взаимодействиидрайверов друг с другом.Здесь важно уяснить, что в IRP-пакете содержится указатель на структуруIO_STATUS_BLOCK, которая, в свою очередь, содержит в себе MJкод.При прохождении IRP-пакета через стек устройств I/O диспетчерпросматривает, какой MJ-код содержит пакет и вызывает в драйвере тупроцедуру, которая должна обрабатывать такие IRP-пакеты.Окончательный список кодов ввода-вывода можно без труда найти взаголовочных файлах в пакете WDK (DDK). Но наибольший интерес представляютсобой не MJ-коды ввода-вывода. Как ты, наверное, успел догадаться,управлять всеми устройствами в Windows двадцатью восьмьювариантами кодов невозможно. В структуре IRP-пакета существуют такназываемые MinorFunction, которые детализируют управление устройством,указывая, что ему нужно сделать конкретно. Самое обидное, чтокодов обработчиков минорных функций много, но они мало документированыи их описание приходится по крупицам собирать в Сети. К примеру,при подключении новых устройств драйвер шины в ядре Win вызовомядерной функции IoInvalidateDevicerelations создает IRP-пакетс Major-кодом IRP_MJ_PNP и с Minor-кодом IRP_MN_QUERY_DEVICE_RELATIONS, который вернет PnP-диспетчеру список специфических дляданного устройства «зависимостей» от шины. Тебе, как разработчику руткитов,должна быть понятна одна вещь: самое интересное при перехватеIRP-пакетов — это коды минорных функций, потому что они конкретизируютуправление устройством.Перехват IRP-пакетовС теорией хватит, настало время перейти к главному блюду — организацииперехвата IRP-пакетов в ядре.Существует два прямых способа для организации перехвата IRP-пакетов вядре — заменить функции обработчики IRP на свои или же создать виртуальноеустройство и присоединить его к интересующему нас.Первый метод прост. Мы просто ставим хуки на функции обработки IRP-пакетовв массиве MajorFunction, который имеется в структуре DRIVER_OBJECT. Найти ее не составит труда.typedef struct _DRIVER_OBJECT {...PDRIVER_DISPATCH MajorFunctionxàêåð 07 /115/ 08097