Руткит

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

Руткит (англ. rootkit, то есть «набор root-а») — набор программных средств (например, исполняемых файлов, скриптов, конфигурационных файлов), обеспечивающих:

  • маскировку объектов (процессов, файлов, каталогов, драйверов);
  • управление (событиями, происходящими в системе);
  • сбор данных (параметров системы).

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

В систему руткит может быть установлен различными способами: загрузка посредством эксплойта, после получения шелл-доступа (в таком случае, может использоваться средство типа wget или исходный FTP-клиент для загрузки руткита с удалённого устройства), в исходном коде или ресурсах программного продукта.

Классификация руткитов

[править | править код]
  • По уровню привилегий
    • Уровень пользователя (user-mode)
    • Уровень ядра (kernel-mode)
  • По принципу действия
    • изменяющие алгоритмы выполнения системных функций (Modify execution path)
    • изменяющие системные структуры данных (Direct kernel object manipulation)[1]

Основные методы реализации

[править | править код]

В Microsoft Windows

[править | править код]

Существуют разнообразные технологии руткитов, наиболее распространённые: захват таблиц вызовов (IAT, IDT, SSDT, GDT), перехват функций (например, модификацией начальных байт), непосредственное изменение системных объектов (DKOM), методы использования драйверов.

Захват таблиц вызовов

[править | править код]

Таблица вызовов представляет собой массив, в котором каждый элемент хранит адрес соответствующей процедуры. Такие таблицы существуют и в режиме ядра (IDT, CPU MSRs, GDT, SSDT, IRP dispatch table), и в режиме пользователя (IAT).

Import Address Table (IAT) — основная таблица вызовов модулей пользовательского режима. Большинство исполняемых файлов имеет одну или несколько встроенных IAT, содержащих адреса библиотечных процедур, импортируемых из DLL[2].

На многопроцессорной машине существуют несколько экземпляров таблиц вызовов (например, IDT, GDT, MSR). Так как каждый процессор имеет собственные системные регистры (в частности, GDTR — регистр глобальной таблицы дескрипторов (GDT), IDTR — регистр дескриптора таблицы прерываний (IDT) и IA32_SYSENTER_EIP — содержит виртуальный адрес входной точки в режим ядра (MSR)), он также имеет собственные системные структуры[3].

При изменении записи в таблице вызовов контролируется исполнение программ и, при необходимости, перенаправляется на требуемые функции. Перехваченная процедура может[4]:

  • блокировать вызовы, производимые определёнными приложениями (например, антивирус)
  • замещать исходную процедуру
  • производить мониторинг системы путём перехвата вводимых параметров
  • фильтровать выходные параметры

Общая идея захвата состоит в следующем:

  • Идентифицировать таблицу вызовов, получить её адрес
  • Сохранить существующую в таблице запись
  • Подменить запись новым адресом
  • Восстановить исходную запись

Общий вид функции перехвата

Если функция перехвата предполагает вызов исходной процедуры, то блокирование и мониторинг производятся до вызова, фильтрация параметров после.

IAT — таблица вызовов, размещаемая в файловой структуре приложения. IAT сохраняет адрес процедур, экспортируемых определённой DLL. Каждая DLL, с которой сцепляется приложение во время загрузки, имеет собственную IAT. Для захвата IAT необходимо выполнить следующие действия:

  • Получить доступ к адресному пространству процессора
  • Локализировать IAT в образе памяти процессора
  • Модифицировать требуемую IAT
Перехват таблицы вызовов (IAT)

Для манипуляции IAT, требуется доступ к адресному пространству приложения, которому таблица принадлежит. Одним из способов является внедрение DLL. Среди методов внедрения DLL в адресное пространство процесса можно указать[5]:

  • Модификация значения реестра AppInit_DLL
  • API-вызов SetWindowsHookEx()
  • Использование удаленных потоков

Перехват модификацией кода функции

[править | править код]

Принцип работы основан на том, что первые байты перехватываемых функций замещаются на код перехватчика. Необходимо подчеркнуть, что при установке перехватчика не производится анализ кода перехватываемой функции: изменяется N первых байт, а не первые N машинных команд. Следствием этого факта является[6]:

  1. код перехватчика может быть установлен только в начале функции;
  2. для каждого вызова перехваченной функции перехватчику необходимо восстановить её машинный код до вызова и повторно перехватить после завершения вызова.

Алгоритм работы руткита:

  1. В теле перехватчика создаётся массив, в который записываются первые N байт каждой из перехваченных функций (обычно размер модифицированного кода не превышает 20 байт)
  2. Массив заполняется эталонным машинным кодом перехватываемых функций.
  3. В начало каждой перехватываемой функции записывается код, осуществляющий передачу управления перехватчику.

Алгоритм работы перехватчика:

  1. Последовательность действий, определённая злоумышленником.
  2. Восстановление первых N байт перехваченной функции.
  3. Вызов перехваченной функции.
  4. Повторная модификация машинного кода перехваченной функции: перезапись кода, передающего управление перехватчику, в первые байты.
  5. Анализ и, при необходимости, модификация результатов выполнения исходной функции.
  6. Выполнение операции ret, возвращение управления вызвавшей функцию программе.

Перехват функции модификацией первых байт

Для перехвата достаточно модификации пяти первых байт функции, на место которых записывается операция jmp, передающая управление перехватчику руткита.

Следует отметить, что простейшие системы защиты от атак такого типа проверяют первый байт вызываемых функций на предмет наличия в них машинного кода операции jmp. В качестве меры противодействия разработчики руткитов применяют методики «маскировки» кода, записываемого в начало функции перехватчика(применения команд вида PUSH/RET, размещения нескольких операторов NOP или мусорного кода типа PUSH AX/POP АХ, а также элементы полиморфизма).

Метод модификации первых байт функций имеет ряд недостатков, связанных, в основном, с необходимостью восстановления машинного кода перехваченных функций перед их вызовом и повторного перехвата после вызова. Данные операции снижают быстродействие системы и могут послужить причиной сбоев в работе многопоточных приложений.

DKOM (Direct Kernel Object Manipulation, прямая манипуляция объектами ядра)

[править | править код]

Операционные системы семейства Windows NT используют стандартные модели объектов. Различные компоненты исполняющей системы определяют один или несколько типов объектов. Каждый компонент экспортирует в режиме ядра набор поддерживаемых функции и свойств, называемый COM-интерфейсом, для манипуляции этим типом объектов. Ни один компонент не может напрямую получить доступ к другому объекту компонента. Типичными объектами режима ядра являются[7]:

  • объект типа устройства (определяемый менеджером ввода-вывода тип объекта привилегированного режима, используемый для представления физического, логического или виртуального устройства)
  • файловый объект
  • символьные ссылки
  • ключи реестра
  • потоки и процессы
  • диспетчерский объект (класс типов объектов привилегированного режима, используемый для управления процессами диспетчеризации и синхронизации)

Такой дизайн обеспечивает гибкость и портируемость (переносимость), например, будущие релизы операционной системы могут содержать компоненты ядра, определяющие аналогичные объекты, но имеющие совершенно другую внутреннюю структуру. Если такие компоненты будут экспортировать функции с сохранившимися названиями и параметрами, изменение не возымеет эффекта[3].

Непосредственное манипулирование объектами ядра является достаточно мощной технологией, трудной для обнаружения. Однако при этом существует ряд недостатков, таких как нестабильность метода, зависимость от версий, сложность реализации в силу отсутствия документированного описания структур и свойств объектов. Несмотря на эти ограничения, данный метод позволяет скрывать процессы, драйверы устройств, порты, повышать уровень привилегий потоков (следовательно, процессов).

EPROCESS — это структура, служащая для внутреннего представления процесса (объект процесса). Windows использует круговой двусвязный список структур EPROCESS для отслеживания процесса исполнения. Ссылки, связывающие объекты EPROCESS, содержатся в поле ActiveProcessLink, структура которого LIST_ENTRY[8]:

typedef struct _LIST_ENTRY {
  struct _LIST_ENTRY  *Flink;
  struct _LIST_ENTRY  *Blink;
} LIST_ENTRY, *PLIST_ENTRY;

Простейший алгоритм скрытия процесса:

  1. Получение указателя на процесс, к которому принадлежит текущий поток, с помощью вызова PsGetCurrentProcess()
  2. Получение PID процесса
  3. При несовпадении PID с искомым осуществляется переход по двусвязному списку (поле ActiveProcessLinks, тип LIST_ENTRY)
  4. Изменение полей ActiveProcessLinks. В частности, ссылка на следующий блок EPROCESS блока А устанавливается на блок С, аналогично ссылка на предыдущий блок в блоке С. Ссылки блока B замыкаются на своей записи. Таким образом, создаются два списка, один из которых состоит из одного элемента

Исключение процесса из списка процессов не влияет на его исполнение. В Windows планирование кода на исполнение производится на уровне потоков, процессы определяют контекст, в котором запускаются потоки. Скрытие процесса производится на внешнем уровне в инструментах, опирающихся на объекты процессов EPROCESS, таких как Диспетчер задач. Диспетчер ядра использует другую схему учёта ресурсов, полагающуюся на другие структуры данных (главным образом объекта ETHREAD). Данный метод позволяет скрывать процессы без потери функциональности[9].

Модель драйверов Microsoft поддерживает многоуровневую архитектуру, поэтому запрос ввода/вывода (I/O request, обмен данными между приложениями и драйверами) может обслуживаться серией подключённых драйверов, каждый из которых выполняет свою задачу. Цепь драйверов, обслуживающих физическое устройство, называется стеком. Такой модульный подход позволяет новым драйверам быть включёнными в стек для увеличения функциональности. При этом изменяется или добавляется только отдельный участок цепи. Также некоторые периферийные устройства используют одинаковые контроллеры (и, соответственно, шины ввода/вывода). Модульность позволяет оптимизировать использование одинаковых блоков кода, вместо написания отдельного драйвера для каждого устройства.

В модели WDM определено три типа драйверов: драйвер шины, функциональные драйверы и драйверы-фильтры. Драйверы-фильтры обычно располагаются между другими модулями и захватывают проходящие через них IRPs. Перед отправлением IRP смежному драйверу фильтр может просмотреть содержимое или изменить его для воздействия на дальнейшее поведение системы. Пример, при снятии образа диска с сервера, критичного к простою, драйвер-фильтр может использоваться для изменения потока данных с целью скрытия некоторых файлов.

IRP пакет (I/O request packet) — структура данных ядра Windows, обеспечивающая обмен данными между приложениями и драйвером, а также между драйвером и драйвером. При поступлении запроса от приложения менеджер ввода/вывода формирует соответствующий IRP, который локализует и пересылается верхнему объекту в стеке драйверов. Если верхний драйвер смог самостоятельно обработать поступивший IRP, он завершает запрос и возвращает IRP менеджеру ввода/вывода. В противном случае, драйвер выполняет частичную обработку, локализует нижележащий объект в стеке и запрашивает менеджер ввода/вывода на передачу IRP следующему драйверу

При создании IRP менеджер ввода/вывода резервирует область памяти, находящуюся после заголовка. Выделенная память используется для записи массива структур IO_STACK_LOCATION, выделяемых для каждого драйвера стека:

Размер памяти соответствует количеству драйверов в стеке. Массив нумеруется с 1, соответствующую нижнему драйверу стека. Структура содержит информацию о вызываемой менеджером ввода/вывода функции управления драйвера (поля MajorFunction и MinorFunction), передаваемые функции параметры (поле Parameters, содержимое варьируется в зависимости от функции), указатель на объект драйвера (DeviceObject), указатель на функцию завершения (поле CompletionRoutine, данная функция находится в драйвере верхнего уровня).

Функция управления драйвера при первом получении IRP восстанавливает из соответствующей позиции стека ввода/вывода параметры, производя вызов IoGetCurrentIrpStackLocation(). Далее выполняются предписанные действия, после чего, в случае пересылки IRP нижнему драйверу стека, происходит:

  • установка позиции стека ввода/вывода в IRP
  • регистрация функции завершения (опционально)
  • отправка IRP нижнему драйверу
  • возврат кода статуса (NTSTATUS)

Существуют два стандартных способа установить позицию стека для следующего драйвера[10]:

  • Текущая позиция пересылается без изменений, функция:
VOID IoSkipCurrentIrpStackLocation (IN PIRP Irp);

Функция уменьшает на единицу указатель на массив IO_STACK_LOCATION. Таким образом, при пересылке IRP произойдёт восстановление указателя (автоматически увеличится на единицу), в итоге, будет использован тот же участок стека. При использовании данного способа, возникнет неиспользуемый участок в конце стека.

  • Если необходимо передать содержимое текущей позиции стека, за исключением указателя на функцию завершения (поле CompletionRoutine), используется:
VOID IoCopyCurrentIrpStackLocationToNext (IN PIRP Irp);

Пересылка IRP следующему драйверу производится с помощью функции:

NTSTATUS IoCallDriver (IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);

Первым аргументом является указатель на нижележащий объект драйвера. Способ получения такого адреса определяется конкретной функцией управления, стандартного метода не существует.

Завершение обработки IRP

Каждый запрос должен быть завершен либо последним драйвером в стеке (дальнейшая пересылка IRP невозможна), либо одним из вышестоящих.

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

Собственно, установленный таким образом драйвер-фильтр позволяет обрабатывать не только приходящие IRP-пакеты (например, блокировать чтение определенного сектора диска), но и управлять результатами обработки нижестоящих драйверов посредством инициализации функции завершения[11].

Еще одним методом реализации руткитов является модификация MBR и загрузка до ядра операционной системы — буткиты (например, BackDoor.MaosBoot).

Данный вид вредоносных кодов в среде Windows известен с начала 1990-х годов под названием стелс-вирусов.

  • реализуемые подменой основных системных утилит (очень легко обнаруживаются средствами контроля целостности, кроме того, легко блокируются средствами мандатного управления доступом типа SELinux или AppArmor);
  • реализованные в виде модуля ядра и основанные на патчинге VFS или перехвате таблицы системных вызовов (sys_call_table);
  • основанные на модификации физической памяти ядра.

Дополнительные возможности

[править | править код]

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

Руткиты для и против DRM

[править | править код]

Руткитами по сути является большинство программных средств защиты от копирования (и средств обхода этих защит — например, эмуляторы CD- и DVD-приводов)[источник не указан 2605 дней].

В 2005 году корпорация Sony BMG встраивала в свои аудиодиски защиту на основе руткита, который устанавливался без ведома пользователя.

Антируткиты

[править | править код]

Это утилиты или резидентные модули, обнаруживающие в системе присутствие руткитов и (в разной мере) удаляющие их. Существует множество конкурирующих средств для этого — как платных, так и бесплатных, но все они используют сходные принципы действия.

Методы детектирования руткитов

Известен алгоритм отлова MEP-руткитов. Его суть заключается в том, что одна и та же информация регистрируется несколькими способами - с использованием API и «напрямую», после чего полученные данные сравниваются в поисках расхождений. Наиболее часто сканируются таблицы импорта и таблицы вызовов Native API, а также структурно вся файловaя система.

Базовый арсенал средств отлова руткитов основывается на следующих методах.

  1. Сигнатурный поиск. Применяется ещё со времён первых антивирусов и представляет собой поиск в проверяемом файле уникальной цепочки байтов (сигнатуры), присущей вредоносной программе.
  2. Эвристический или поведенческий анализатор. Эта технология основывается на поиске отклонений в настройках системы, конфигурационных файлах Linux или реестре Windows, подозрительном поведении процессов и модулей и так далее.
  3. Контроль целостности. Этот тип поиска основан на сравнении контрольной суммы (MD5 и тому подобное) или цифровой подписи разнообразных системных файлов с базой, содержащей контрольную сумму оригинальных файлов. В случае несовпадения программа делает вывод, что файл был модифицирован или вовсе заменен.

Примечания

[править | править код]
  1. Колесниченко, 2006, с. 29.
  2. Колесниченко, 2006, Перезапись адреса функции.
  3. 1 2 Solomon, Russinovich, Ionescu, 2012.
  4. Blunden, 2009.
  5. Blunden, 2009, Injecting a DLL.
  6. Зайцев, 2006, Перехват модификацией первых байтов функции.
  7. Managing Kernel Objects : [англ.] : [арх. 24 июля 2014] // MSDN.
  8. Blunden, 2009, Chapter 7 Altering Kernel Objects.
  9. Blunden, 2009, Chapter 7. Altering Kernel Objects.
  10. Различные способы обработки IRP — краткий справочник : [англ.] : [арх. 13 декабря 2013] // MSDN.
  11. Зайцев, 2006, Клавиатурный шпион на базе драйвера-фильтра.

Литература

[править | править код]
  • Зайцев О. Rootkits, SpyWare_AdWare, Keyloggers & BackDoors. Обнаружение и защита / Олег Зайцев. — Санкт-Петербург: БХВ-Петербург, 2006.
  • Blunden B. The Rootkit Arsenal / Bill Blunden. — Plano, Texas: Wordware Publishing, Inc., 2009.
  • Колесниченко Д. ROOTKITS под Windows / Денис Колесниченко. — Санкт-Петербург: Наука и Техника, 2006.
  • Solomon D., Russinovich M., Ionescu A. Windows Internals / David Solomon, Mark Russinovich, Alex Ionescu. — Microsoft Press, 2012.