Руткит

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

Руткит (англ. 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]

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

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

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

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

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-х годов под названием стелс-вирусов.

В UNIX и linux[править | править вики-текст]

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

Дополнительные возможности[править | править вики-текст]

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

Легальные руткиты[править | править вики-текст]

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

Антируткиты[править | править вики-текст]

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

Поиск расхождений

Против MEP-руткитов. Одна и та же информация получается несколькими способами с использованием API и «напрямую» и ищутся расхождения. В частности, обычно сканируются таблицы импорта, таблица Native API, файловая система.

Примечания[править | править вики-текст]

  1. Денис Колесниченко, с. 29
  2. Денис Колесниченко, Перезапись адреса функции
  3. Windows Inernals
  4. Bill Blunden
  5. Bill Blunden, Injecting a DLL
  6. Зайцев Олег, Перехват модификацией первых байтов функции
  7. Microsoft, Объекты режима ядра
  8. Windows Internals
  9. 1 2 Bill Blunden, Chapter 7 Altering Kernel Objects.
  10. MSDN, Обработка IRP запросов
  11. Зайцев Олег, Клавиатурный шпион на базе драйвера-фильтра

Ссылки[править | править вики-текст]

Литература[править | править вики-текст]

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