# Форум на русском языке  > Угрозы информационной безопасности  > Сетевые атаки  >  Руткит - принципы и механизмы работы

## Зайцев Олег

Термин RootKit исторически пришел из мира Unix, и под этим термином понимается набор утилит, которые хакер устанавливает на взломанном им компьютере после получения первоначального доступа. Этот набор, как правило, включает в себя разнообразные утилиты для заметания следов вторжения в систему, хакерский инструментарий (снифферы, сканеры) и троянские программы, замещающие основные утилиты Unix. RootKit позволяет хакеру закрепиться во взломанной системе и сокрыть следы своей деятельности.

В системе NT/W2K/XP RootKit принято считать программу, которая внедряется в систему и перехватывает системные функции (API). Перехват и модификация низкоуровневых API функций в первую очередь позволяет такой программе достаточно качественно маскировать свое присутствие в системе. Кроме того, как правило, RootKit может маскировать присутствие в системе любых описанных в его конфигурации процессов, папок и файлов на диске, ключей в реестре. Многие RootKit устанавливают в систему свои драйвера и сервисы (они, естественно, также являются «невидимыми»).
Перед рассмотрением принципов работы руткита на платформе Windows нужно кратко и упрощенно рассмотреть принцип вызова API функций, размещенных в DLL. Для этого существует два базовых способа:
1.*Раннее связывание* (статически импортируемые функции). Этот метод основан на том, что на стадии компиляции программы компилятор “знает”, какие функции и из каких библиотек использует программа. Опираясь на эту информацию, он формирует так называемую таблицу импорта EXE файла. Таблица импорта – это особая структура (ее местоположение и размер описываются в заголовке EXE файла), которая содержит список используемых программой библиотек и список импортируемых из каждой библиотеки функций (для каждой функции в таблице имеется поле для хранения адреса, но на стадии компиляции адрес неизвестен). В процессе загрузки EXE файла система анализирует его таблицу импорта, поочередно загружает все перечисленные там DLL и производит занесение в таблицу импорта реальных адресов функций этих DLL. У раннего связывания есть существенный плюс – на момент запуска программы все необходимые DLL оказываются загруженными, таблица импорта заполнена – и все это делается системой, без участия программы. Но отсутствие в процессе загрузки указанной в его таблице импорта DLL (или отсутствие в DLL требуемой функции) приведет к ошибке загрузки программы. Кроме того, очень часто нет необходимости загружать все используемые программой DLL в момент запуска программы. На рисунке 1 показан процесс раннего связывания – в момент загрузки происходит заполнение адресов в таблице импорта (шаг 1), в момент вызова функции из таблицы импорта берется адрес функции (шаг 2) и происходит собственно вызов функции (шаг 3).; 

Рис. 1 Механизмы вызова API функции из программы
2.*Позднее связывание.* Отличается от раннего тем, что загрузка DLL производится динамически при помощи функции API LoadLibrary (LoadLibrary находится в kernel32.dll, поэтому, если не прибегать к хакерским приемам, то kernel32.dll придется загружать статически). При помощи LoadLibrary программа может загрузить интересующую ее библиотеку в любой момент времени. Соответственно для получения адреса функции применяется функция kernel32.dll GetProcAddress. На рисунке шаг 4 соответствует загрузке библиотеки при помощи LoadLibrary и определению адресов при помощи GetProcAddress. Далее можно вызывать функции DLL (шаг 5), но, естественно, при этом таблица импорта не нужна. Чтобы не вызывать GetProcAddress для каждого вызова функции из DLL, программист может однократно определить адреса интересующих его функций и сохранить их в массиве или некоторых переменных.

Независимо от метода связывания системе необходимо знать, какие функции экспортирует DLL. Для этого у каждой DLL имеется таблица экспорта – таблица, в которой перечислены экспортируемые DLL функции, их номера (ординалы) и адреса функций.
Теперь вернемся к нашему руткиту. Его задача – перехватить функции API и исказить их работу. Даже поверхностный анализ рисунка, показывающий принцип работы программы с DLL можно выделить минимум 5 методов:
1.*Модификация машинного кода прикладной программы.*

Схема метода показана на рис. 2. В этом случае модифицируется машинный код, отвечающий в прикладной программе за вызов той или иной функции API. Это очень сложно реализовать, т.к. существует множество языков программирования и версий компиляторов. Но теоретически подобное возможно (при условии, что внедрение будет идти в заранее заданную программу известной версии – можно проанализировать ее машинный код и написать перехватчик) 
2.*Модификация таблицы импорта.*  

Данная операция описана в книге Рихтера и является одним из классических методов. Идея метода проста – руткит находит в памяти таблицу импорта программы и исправляет адреса интересующих его функций на адреса своих перехватчиков (естественно, он предварительно где-то запоминает правильные адреса). Программе, естественно, все равно – в момент вызова API функции она считывает ее адрес из таблицы и передает по этому адресу управление. Схема этого метода показана на рис. 3. Но у методики есть существенный минус (и его хорошо видно на схеме) -  перехватываются только статически импортируемые функции. Но есть плюс – методика очень проста в реализации и есть масса примеров, демонстрирующих ее реализацию. Единственная сложность – это поиск таблицы импорта в памяти – но тут на помощи приходит фирма Microsort, которая поставляет набор API для работы с образом программы в памяти – поэтому перехватчик получается очень простым (исходный текст rootkit  на C занимает несколько листов печатного текста);
3.Перехват функций LoadLibrary и GetProcAddress. 

Методика перехвата этих двух функций может быть любой (в классической реализации применяется методика 2 – модификация таблицы импорта). Идея методики проста – если перехватить GetProcAddress, то при запросе адреса можно выдавать не реальные адреса функций, а адреса своих перехватчиков. Как и в методе 2, программе все равно – она получает адрес и выполняет вызов функции … У данного метода есть минус – он не перехватывает статически импортируемые функции;
4.***Метод, сочетающий методику 2 и 3. 

При этом модифицируется таблица импорта и кроме необходимых для работы RootKit функций обязательно перехватываются LoadLibrary и GetProcAddress – в результате при вызове статически импортируемых функций искаженные адреса берутся из таблицы импорта, при динамическом определении адреса вызывается перехваченная RootKit функция GetProcAddress, которая возвращает искаженные адреса. В результате у программы не остается шансов узнать правильный адрес функции и вызвать ее;
5.***Модификация программного кода API функции.

Данный метод ощутимо сложнее в реализации, чем подмена адреса. Идея методики состоит в том, что RootKit находит в памяти машинный код интересующих его функций API и модифицирует его. В результате этого уже нет надобности менять таблицы импорта, подсовывать программе искаженные адреса и т.п. – все остается «как есть» за одним исключением – теперь уже по «правильному» адресу внутри «правильной» DLL находится машинный код RootKit. Как правило, вмешательство в машинный код DLL минимально – в начале функции размещается не более 2-3 команд, передающих управление на основную функцию - перехватчик. Правда, при таком методе перехвата для вызова пораженной функции RootKit должен сначала восстановить ее машинный код, затем передать ему управление и после выполнения опять исказить его первые команды. Для выполнения данной операции RootKit должен сохранять исходный машинный код для каждой модифицированной им функции.

Перечисленные пять методик являются классическими и работают в любой операционной системе. Это важный момент, поскольку я неоднократно встречал в Интернет и литературе утверждения, что RootKit существуют только в NT. В данной статье не описан шестой метод – перехват на самом низком уровне (ниже функций API, с которыми общается программа). Подобные методики перехвата при штатной реализации требуют написания драйвера, и описанные в литературе методы рассчитаны в основном на NT. 
Типовая для NT методика основана на том, что в ней существует  KeServiceDescriptorTable – таблица адресов точек входа сервисов ядра NT. Через эту таблицу производится вызов всех функций ядра NT (я не буду вдаваться в теорию – по этому вопросу целые книги написаны, наиболее значимая и интересная – книга Свена Шрайбера Undocumented Windows 2000). Модификация этой таблицы позволяет подменить сервис ядра своим сервисом, т.е. по идеологии это метод 2, только вместо модификации таблицы импорта каждой загруженной программы происходит модификация KeServiceDescriptorTable. Этот метод часто называют перехватом Native API и, естественно, он работает только в NT (и соответственно W2K, XP, W2003).

----------

Надоело быть жертвой? Стань профи по информационной безопасности, получай самую свежую информацию об угрозах и средствах защиты от ведущего российского аналитического центра Anti-Malware.ru:

----------


## Geser

Короткии путь http://www.securinfo.ru/RootKit

----------


## Aizek

Я думаю тогда еще может стоит рассказать КАК работать с KiSystemService & ServiceDescriptorTable?  :Smiley:

----------


## Зайцев Олег

> Я думаю тогда еще может стоит рассказать КАК работать с KiSystemService & ServiceDescriptorTable?


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

----------


## Aizek

[quote author=Зайцев Олег link=board=9;threadid=587;start=0#msg5485 date=1106743165]
Я вообще про них говорить не хотел - это же базовый ликбез, а не техническая статья. Но видимо придется (а для этого нужно рисовать еще две картинки  :Smiley:  ) 
[/quote]
Зачем рисовать...  Вот готова статья  :Smiley: 
http://www.wasm.ru/article.php?article=tracknapi

----------


## Зайцев Олег

> Зачем рисовать...  Вот готова статья 
> http://www.wasm.ru/article.php?article=tracknapi


Статья эта понятна программисту-системщику, а нужно сделать так, чтобы понял обычный пользователь  :Smiley:  А это уже сложнее

----------


## pig

А надо ли обычному пользователю забивать голову многочисленными KeServiceDescriptorTable, LoadLibrary и прочее? По-моему, ему надо на пальцах и в один абзац, чтобы был общий смысл: руткит так внедряется в систему, что стандартными способами его не видно. Кто хочет знать больше, почитает статью Олега, из неё уже ясен принцип. Кто хочет достичь самых глубин (или вершин), пусть читает статьи для профи, всё равно от этого никуда не деться.

----------


## Aizek

[quote author=Зайцев Олег link=board=9;threadid=587;start=0#msg5500 date=1106745948]
Статья эта понятна программисту-системщику, а нужно сделать так, чтобы понял обычный пользователь  :Smiley:  А это уже сложнее 
[/quote]

Кстати Олег, а вот интересно, если я поправлю эту табличку, то какими способами можно будет это вычислить? Ведь есть вероятность, что я с помощью перехваченных сервисов даже доступа не дам к этим участкам памяти...  По сути можно даже таблицу-то и не трогать, а модифицировать уже сами сервисы, что несомненно сложнее, зато и шансов это расскрыть намного меньше... Причем как я понимаю в любом случае нужно будет писать свой драйвер анализатор. Стандартными средствами юзерское приложение в ядро не попадет... Или я в чем-то не прав?

----------


## Зайцев Олег

> Кстати Олег, а вот интересно, если я поправлю эту табличку, то какими способами можно будет это вычислить? Ведь есть вероятность, что я с помощью перехваченных сервисов даже доступа не дам к этим участкам памяти...  По сути можно даже таблицу-то и не трогать, а модифицировать уже сами сервисы, что несомненно сложнее, зато и шансов это расскрыть намного меньше... Причем как я понимаю в любом случае нужно будет писать свой драйвер анализатор. Стандартными средствами юзерское приложение в ядро не попадет... Или я в чем-то не прав?


Ну, метод есть один - хакерский. Идея его в том, что штатное устройство \\device\\physicalmemory доступно из user mode. Как следствие, можно замаппить соответствующую память в свой процесс и безобразничать с ней ... есть решения на базе этого метода. Но это конечно хулиганство - по науке нужно присать драйвер-анализатор, который из Kernel Mode будет делать проверки. 
Но AVZ этого пока не делает - поскольку есть три сдерживающие причины:
1. Трояны, SpyWare и распространенные "руткиты" ограничиваются только UserMode и первыми пятью типами перехвата. Я пока не видел трояна, который бы ставил драйвера для маскировки;
2. Установка своего драйвера (пусть даже а-ля FileMon - только на время работы, что вроде как никакого драйвера и нету) нарушит мою концепцию невмешательства в проверяемую систему в режиме проверки, а я считаю ее очень важной;
3. После "лечения" первых пути способов перехвата можно как максимум повесить самого AVZ, и его борьба с перехватчиками влияет в основном только на него самого. а вот лечение KeServiceDescriptorTable повлияет на всю систему. И вылечив KeServiceDescriptorTable можно оставить систему беззащитной - пример - "непробиваемая" защита от запуска троянцев ProcessGuard основана на KeServiceDescriptorTable - ее восстановление дает отличный эффект - все работает, но защита ProcessGuard полностью исчезает, он теряет контроль над системой ....

----------


## Aizek

[quote author=Зайцев Олег link=board=9;threadid=587;start=0#msg5556 date=1106810468]
Ну, метод есть один - хакерский. Идея его в том, что штатное устройство \\device\\physicalmemory доступно из user mode. Как следствие, можно замаппить соответствующую память в свой процесс и безобразничать с ней ... 
[/quote]
Забавно...  Все все можно делать? И изменять тоже? Это ж тогда просто огроменная дыра  :Smiley:  Я так думаю как минимум еще соответствующие права надо иметь...

А то что руткиты еще такое не делают, дак это всего-лишь вопрос времени. Скоро начнут  :Smiley:  Тем более статьи на эту тему есть и особых проблем написать такой метод скрытия я думаю нет.

----------


## Geser

[quote author=Зайцев Олег link=board=9;threadid=587;start=0#msg5556 date=1106810468]
2. Установка своего драйвера (пусть даже а-ля FileMon - только на время работы, что вроде как никакого драйвера и нету) нарушит мою концепцию невмешательства в проверяемую систему в режиме проверки, а я считаю ее очень важной;
[/quote]
Мне кажется что важнее всётаки умение обнаруживать вредоносные программы.

----------


## Aizek

[quote author=Зайцев Олег link=board=9;threadid=587;start=0#msg5556 date=1106810468]
2. Установка своего драйвера (пусть даже а-ля FileMon - только на время работы, что вроде как никакого драйвера и нету) нарушит мою концепцию невмешательства в проверяемую систему в режиме проверки, а я считаю ее очень важной;
[/quote]
Я думаю что по большому счету драйвер внедрять можно и ничего страшного в этом нет, другое дело что будет делать этот драйвер, ведь он может делать правки всего-лишь в контексте твоего процесса

----------


## Sanja

ja uze posilal Gaseru spyware kotorogo ja snal u druga...  :Smiley:  snimal dolgo ispolzuja toolsi drivernie  :Smiley:  t.k visel tak 1 driverok i 3 hooking dll  :Smiley:  driver pratal eti dll a eti dll pratali vse ostalnoe  :Wink:

----------

