Руткит - это программа или набор программ для скрытого взятия под контроль взломанной системы. На платформе Windows скрытность обеспечивается тем, что руткиты перехватывают системные функции и структуры данных, подменяя их своим кодом и данными. Благодаря этой подмене, руткит может замаскировать присутствие в системе посторонних процессов, файлов, сетевых соединений, ключей реестра и т. п., выступая таким образом в роли своеобразной программной шапки-невидимки. Описанию руткитных технологий и программированию руткитов как раз и посвящена данная книга. В первой главе книги рассмотрено несколько популярных руткитов.
Вообще говоря, наличие символической ссылки необязательно. Тогда о вашем устройстве будет знать только ваш же руткит. Устройство регистрируется в ходе выполнения функции . Для его создания служит вызов функции . Имена устройства и символической ссылки должны быть в кодировке . При выгрузке драйвера из памяти . Объект устройства известен в функции . Чтобы передать туда же указатель на . Представим себе, что пользовательская программа пишет что. Куда на самом деле попадзт записываемые данные. Чтобы взаимодействовать с программой пользовательского уровня, драйвер устройства . Тут ничего сложного нет — это всего лишь структуры данных, содержащие буферы для хранения. Для аналогии можно привести пример с обычной записью файла. В случае успешного открытия она получает дескриптор файла, который она будет использовать в операциях ввода. После этого программа может, например, записать данные в файл или прочитать данные из файла. На уровне ядра операция записи воспринимается как . Предположим, что пользовательский компонент записал в дескриптор строку . Тогда компонент уровня ядра получит буфер, содержащий эту строку. Для обработки каждого . Диспетчер проверяет, не зарегистрировали ли драйверы более высокого уровня своих процедур . Посмотрите на рис. Пользовательская программа просит драйвер выполнить определенные действия . Но драйвер переадресовывает все действия одной и той же функции. Такую модель мы сейчас рассматриваем для простоты, чтобы вы поняли, что и как происходит. На практике же для каждого действия должна быть своя собственная функция. Давайте реализуем правильную модель, в которой для каждого действия будет реализована своя функция. Предположим, что наш драйвер позволяет выполнять над устройством следующие операции. Тогда вместо регистрации функции . Сначала определим управляющие коды . СХЕМА ДВУХУРОВНЕВОГО РУТКИТА Вы научились создавать и регистрировать драйвер устройства и поняли, как с ним может взаимодействовать программа пользовательского уровня. Очевидно, что можно написать такой руткит, компоненты которого работали бы на обоих уровнях. Например, пользовательский компонент мог бы выполнять большинство функций сетевое взаимодействие, удаленное управление и т. А компоненту ядра достались бы низкоуровневые, сугубо . Схема взаимодействия компонентов двух уровней . На данной схеме компонент ядра будет производить модификацию таблиц ядра, а также сниффинг клавиатуры и пакетов, передающихся по сети . Двухуровневый руткит Использование двухуровневой схемы имеет следующие преимущества. Не переписывать же заново всю . Так зачем включать код с ошибками в состав руткита ядра. Администратор же будет думать, что все нормально он ведь удалил руткит. Для взаимодействия пользовательского компонента и компонента уровня ядра можно использовать несколько способов. Основные его функции — это предоставление удаленного доступа . Каждое сообщение несет в себе определенную команду, например команду установить соединение с темто узлом или открыть порт для удаленного доступа. Например, на первый день порт открывается через . Так будет сложнее вычислить ваш руткит. И, завершая тему инструментов разработчика руткита, скажем пару слов о структуре проекта руткита. Я рекомендую не держать все исходные файлы в одном каталоге, а создать для руткита структуру каталогов — вам же потом проще будет во всем этом ориентироваться. Например, такую, как изображенная на рис. Структура каталогов для проекта руткита . Для удобства я создал в этом каталоге еще пять подкаталогов. Все это можно перехватывать, и сейчас мы покажем как. Сначала продемонстрируем обещанный в п. Выводить будем в окно утилиты . Добавим этот код в функцию . Для чтения таблицы . Она возвращает адрес структуры . Для преобразования этих полей в полный адрес определим макрос . Мы знаем, что прерывания бывают аппаратными . Нас интересуют не все прерывания, а только одно. Данное прерывание используется для осуществления системного вызова. Давайте вспомним, как осуществляется системный вызов. В ЕАХ помещается номер функции . Мы можем создать ловушку, которая бы перехватывала это прерывание. Исходный обработчик прерывания . Затем проверяет, соответствуют ли параметры, указанные в . Если все нормально, то он вызывает . Как будет работать наш обработчик. Это означает, что он не сможет отфильтровать результат. Однако руткит может попытаться идентифицировать приложение, которое инициировало системный вызов. Так, можно попытаться обнаружить брандмауэр или установленную . Но об этом поговорим чуть позже. Было сказано, что в ЕАХ помещается номер системного вызова, а в . Кто же заполняет регистры ЕАХ и . Правда, в последних версиях . Заменим строки, которые выводили в окно отладки текущие адреса векторов прерываний, на вызов функции . Эта функция установит вместо стандартного обработчика прерываний . Функция использует следующие глобальные переменные. Обратное действие, то есть восстановление исходного обработчика, выполняет функция . Указатель на текущий процесс . Как видите, ничего не изменилось, кроме последней инструкции. Сейчас мы напишем небольшой драйвер, который записывает в регистр . Драйвер сохраняет адрес оригинального обработчика прерывания в переменной . Я же говорил, что . Адреса системных сервисов этих систем хранятся в таблице . Если быть предельно точным, то в этой таблице хранятся номера системных вызовов и соответствующие им адреса . Ядро также работает с другой таблицей . В этой таблице описываются параметры системных сервисов. Ядром экспортируется таблица . Данная таблица содержит указатели на часть таблицы . ехе главной части ядра. Для осуществления системного вызова используется диспетчер системных сервисов — . Он просто по номеру системного вызова находит в таблице . Нужно отметить, что все адреса в . Также нужно помнить, что для того, чтобы по определенному . Каждый элемент этой таблицы занимает один байт в нем содержится количество .