ESPNNHL Tonight: Previews, stats and betting intel for Thursday's dozen gamesThe Jerusalem PostIran's floating oil reserves, used for China sales, completely depleted, analysts say - reportPunchOsun APC rejects short notice for Tinubu campaign councilBollywood HungamaEXCLUSIVE: Ramayana's South distributors locked; Dil Raju bags Andhra-Telangana, Yash takes Karnataka, Think Studios gets Tamil Nadu, E4 Entertainment acquires KeralaDaily MaverickBusiness Reflection: Loaded for Bear: MTBPS shines light on lost revenue from missed mining opportunitiesRTP DesportoProva de Supertubos suspensaInquirerEl Niño response must draw on scientific, historical data – expertZDF heuteAktuelle Pressemitteilungen des ZDFAntara NewsGovt taps SEZ-integrated vocational training for industry alignmentWirtualna PolskaSikorski uderza w prezydenta. "Nie ma na to zgody"SCMP ChinaChina urges opening Bab al-Mandeb in first joint statement with regional nationESPN CricinfoSamson, Iyer and Dhir take India home in nervy chase against NZ
The Daily Newsstand · Free, Always
Thursday, October 22, 2026

Руткиты изнутри: типы, работа, защита

Translate

Я расскажу, как устроены руткиты в живую: не в теории, а на уровне того, как они прячутся в ядре, что делают с системными вызовами и почему обычные утилиты их не видят. Плюс разберём, как от них защищаться, какие есть инструменты и как написать простую проверку на C, которая покажет, включён ли Secure Boot.

Что такое руткит

Руткит это набор техник, цель которых: остаться незаметным в системе и при этом иметь контроль над тем, что система говорит о себе. Это не вирус в классическом смысле, это инструмент сокрытия. Он может быть установлен как легитимная программа, может прийти вместе с другим ПО, может быть внедрён атакующим после получения доступа. Важно понять одну вещь, с которой начинается вся тема. Руткит не пытается «быть сильнее» системы. Он пытается стать частью системы, чтобы система сама его скрывала.

Уровни привилегий и типы руткитов

Прежде чем перейти к технике, вспомним про кольца защиты. В x86 это ring 0 и ring 3, промежуточные почти не используются.

Ring

Что там работает

Уровень доступа

Ring 0

Ядро ОС, драйверы

Полный доступ ко всему

Ring 3

Приложения

Только через системные вызовы

Отсюда и типы руткитов, от наименее привилегированных к наиболее.

Тип

Уровень

Что делает

Насколько глубоко прячется

Application

ring 3

Подменяет приложения, внедряется в процессы

Легко обнаруживается

User‑mode

ring 3

Хуки API, перехват библиотек

Средне

Bootkit

загрузчик

Заражение MBR или UEFI

Очень глубоко

Kernel‑mode

ring 0

LKM, драйвер ядра

Глубоко

Memory

ring 0

Патч ядра в памяти, без модуля

Очень глубоко

Hardware

прошивка

Живёт в железе, не в ОС

Практически не ловится

Вот эта схема ниже показывает то же самое визуально. Кольца, от внешнего к внутреннему, и типы руткитов, которые живут на каждом уровне.

схема колец

схема колец

види руткитов

види руткитов

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

Как руткит работает в ядре

Дальше про Linux, потому что там проще показать механику, а Windows разберём отдельно в разделе про WinAPI.

Загрузка модуля

В Linux ядро монолитное, но расширяемое. Есть Loadable Kernel Module, LKM. Это объектный файл, который можно подгрузить в работающее ядро.

#include <linux/module.h>
#include <linux/kernel.h>

static int __init rk_init(void) {
    printk(KERN_INFO "loaded\n");
    return 0;
}

static void __exit rk_exit(void) {
    printk(KERN_INFO "unloaded\n");
}

module_init(rk_init);
module_exit(rk_exit);
MODULE_LICENSE("GPL");

Что тут важно. init и exit это не просто пометки, это указание ядру, что код можно выгрузить из памяти после выполнения. module_init и module_exit связывают наши функции с механизмом загрузки. MODULE_LICENSE обязателен, иначе ядро загрузит модуль, но пометит его как проприетарный и откажет в доступе к некоторым символам. Собирается это через Makefile:

obj-m += rootkit.o           # говорим kbuild, что мы собираем модуль

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

После загрузки модуль виден в lsmod. Значит, первая задача руткита: спрятаться.

Скрытие модуля из списка

Ядро хранит список модулей в двусвязном списке. Можно вырезать себя из этого списка.

static struct list_head *prev;

static void hide(void) {
    prev = THIS_MODULE->list.prev;
    list_del(&THIS_MODULE->list);
}

Смысл в одной строке: list_del разрывает связи нашего узла с соседями, и список модулей больше про нас не знает. После этого lsmod и /proc/modules тебя не покажут, потому что они читают именно этот список. Но модуль остаётся в /sys/module/, поэтому хороший руткит чистит и это.

Перехват системных вызовов

Спрятаться мало, надо ещё управлять тем, что система рассказывает о себе. Для этого перехватывают системные вызовы. В старых ядрах было просто: есть таблица sys_call_table, меняешь указатель на свой обработчик.

// сохраняем указатель на оригинальную функцию open
static asmlinkage long (*orig_open)(const char __user *, int, umode_t);

// наш обработчик, который встанет вместо оригинала
static asmlinkage long hook_open(const char __user *path, int flags, umode_t mode) {
    // если в имени файла есть слово secret, притворяемся что файла нет
    if (strstr(path, "secret"))
        return -ENOENT;   // ENOENT это «файл не найден»

    // во всех остальных случаях отдаём работу оригиналу
    return orig_open(path, flags, mode);
}

Ключевое здесь asmlinkage и __user. Первое говорит компилятору, что аргументы приходят через стек, как это принято в системных вызовах. Второе это маркер для анализатора, что указатель пришёл из пользовательского пространства и его нельзя просто так разыменовывать. Без этих пометок код либо не соберётся, либо упадёт в рантайме.

Сейчас таблица read‑only, и просто так её не переписать. Современные методы другие.

Метод

Работает на новых ядрах

Заметность

Запись в cr0

Нет

Высокая

kprobes

Да

Средняя

ftrace

Да

Низкая

Хуки на VFS

Да

Низкая

Скрытие файлов и процессов

Когда перехват есть, скрывать файлы и процессы становится делом техники. Для файлов перехватывают iterate_dir или filldir, для процессов вырезают запись из списка задач.

static int hook_filldir(struct dir_context *ctx, const char *name, int namlen,
                        loff_t offset, u64 ino, unsigned d_type) {
    // если имя совпадает с тем, что мы прячем, не отдаём запись пользователю
    if (strncmp(name, "hidden", namlen) == 0)
        return 0;   // 0 значит «пропустить эту запись»

    // всё остальное отдаём оригинальному обработчику
    return orig_filldir(ctx, name, namlen, offset, ino, d_type);
}

filldir это функция, которую ядро вызывает для каждой записи в директории, когда пользователь делает ls или читает через getdents. Мы вклиниваемся в этот процесс и выкидываем то, что не хотим показывать. return 0 здесь означает «запись обработана, идём дальше», а не ошибка.

Windows и WinAPI

На Windows картина другая, но идея та же. Рутткит работает через драйвер ядра, который загружается как сервис. Дальше он может:

  • Перехватывать Native API, это функции Nt* и Zw*, которые лежат ниже WinAPI

  • Патчить SSDT, System Service Descriptor Table, аналог sys_call_table

  • Ставить inline‑хуки прямо в код функций ядра

  • Фильтровать то, что возвращают функции перечисления процессов и файлов

WinAPI это верхний уровень, то, чем пользуются приложения. Но под ним есть нативный уровень, и именно там работают руткиты. Если руткит перехватил NtQuerySystemInformation, то любые программы, которые через WinAPI смотрят список процессов, увидят то, что руткит решил им показать.

Уровень

Что там

Кто вызывает

WinAPI

CreateFile, ReadFile

Приложения

Native API

NtCreateFile,NtReadFile

WinAPI внутри

Ядро

Драйверы, SSDT

Native API внутри

Именно поэтому современные антируткиты не доверяют WinAPI и лезут напрямую в ядро или сравнивают то, что им говорит система, с тем, что видно через другие каналы.

Методы защиты

Защита от руткитов строится на одной идее: перекрёстная проверка. Если руткит спрятал что‑то в одном месте, оно осталось в другом, и на этом его ловят.

Утилиты

Платформа

Что делает

rkhunter

Linux

Сравнивает хеши, ищет подозрительные файлы и модули

chkrootkit

Linux

Проверяет известные сигнатуры руткитов

Volatility

Linux, Windows

Анализ дампа памяти

GMER

Windows

Поиск скрытых драйверов и хуков

Sysinternals Autoruns

Windows

Показывает всё, что грузится с системой

rkhunter стоит держать на любой машине, где ты что‑то тестируешь. Он не гарантирует, что найдёт всё, но ловит классические схемы.

Secure Boot и почему он важен

Secure Boot это механизм UEFI, который проверяет подпись загрузчика и ядра до того, как они запустятся. Если подпись не совпадает с доверенной, загрузка останавливается. Это закрывает целый класс руткитов на уровне загрузчика.

Проверить, включён ли Secure Boot, можно разными способами. Вот простой пример на Cи, который читает переменную EFI через sysfs.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    // открываем файл переменной SecureBoot в sysfs
    // GUID в имени файла это стандартный идентификатор переменной SecureBoot
    FILE *f = fopen("/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c", "rb");
    if (!f) {
        // если файла нет, значит система загрузилась в legacy режиме
        printf("EFI variable not accessible (legacy BIOS or no permission)\n");
        return 1;
    }

    // читаем первые 8 байт: 4 байта атрибутов и 4 байта значения
    unsigned char buf[8];
    size_t n = fread(buf, 1, sizeof(buf), f);
    fclose(f);

    if (n < 5) {
        printf("unexpected data size\n");
        return 1;
    }

    // пятый байт (индекс 4) это значение переменной
    // 1 значит Secure Boot включён, 0 значит выключен
    if (buf[4] == 1)
        printf("Secure Boot: enabled\n");
    else
        printf("Secure Boot: disabled\n");

    return 0;
}

Что тут важно. Формат переменной EFI такой: первые 4 байта это атрибуты, дальше идёт значение. В нашем случае значение однобайтовое, поэтому смотрим на buf[4]. Если бы переменная была другого типа, данные лежали бы дальше, но у SecureBoot это именно один байт. Сборка:

gcc check_sb.c -o check_sb
sudo ./check_sb

Права нужны, потому что efivars не читаются из‑под обычного пользователя.

Что проверяем

Где смотреть

Что означает

Secure Boot включён

efivars, байт 1

Загрузчик проверяется

Secure Boot выключен

efivars, байт 0

Загрузчику доверяют без проверки

Нет efivars

Файл отсутствует

Legacy BIOS, Secure Boot недоступен

Перекрёстная проверка

Помимо утилит, есть более надёжный подход: сравнивать то, что говорит система, с тем, что видно в обход системы. Например:

  • Список процессов из ps сравнить со списком в /proc напрямую через syscall

  • Список модулей из lsmod сравнить с содержимым /sys/module/

  • Список сетевых соединений из netstat сравнить с содержимым /proc/net/

  • Код функций ядра сравнить с эталоном через хеши

Разница между двумя источниками это и есть след руткита.

Проверка

Что сравниваем

Что ловим

Процессы

ps и прямой обход /proc

Скрытые процессы

Модули

lsmod и /sys/module

Скрытые модули

Сеть

netstat и /proc/net

Скрытые соединения

Сеть

Хеши функций

Inline‑хуки

Что делать, если руткит найден

Порядок действий примерно такой.

  1. Не выключать машину. Память содержит следы, которые на диске уже не останутся.

  2. Снять дамп памяти через LiME или AVML, если это Linux, или WinPmem, если Windows.

  3. Проанализировать дамп через Volatility, искать скрытые модули, хуки, аномалии.

  4. Снять образ диска, если нужно идти дальше.

  5. Сравнить хеши системных файлов с эталонными.

  6. Если машина боевая, изолировать от сети, дальше по процедуре инцидента.

Это стандартный порядок, который используют в DFIR. Без снятия дампа памяти ты теряешь большую часть следов.

Итог

Руткит это не что‑то магическое, а сумма конкретных техник: скрыть модуль, перехватить вызов, спрятать файл, спрятать процесс. Каждая техника оставляет след в другом месте, и на этом строится защита. Защита от руткитов это не одна утилита, а набор: Secure Boot закрывает загрузчик, rkhunter и chkrootkit ловят классику, Volatility разбирает дампы памяти, перекрёстные проверки находят несоответствия. По отдельности ни один метод не даёт гарантии. Вместе они дают шанс заметить то, что иначе осталось бы невидимым.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.