Прятки в linux


Модуль раз, модуль два…


Подавляющее большинство методик стелсирования работает на уровне ядра, пристыковываясь к нему в виде загружаемого модуля (Loadable Kernel Module или сокращенно LKM). В программировании модулей нет ничего сложного, особенно для старых ядер с версией2.4.

Исходный текст простейшего модуля выглядит так:

// сообщаем компилятору, что это модуль режима ядра

#define MODULE

#define __KERNEL__

// подключаем заголовочный файл для модулей

#include <linux/module.h>

// на многоЦП'шных машинах подключаем еще и smp_lock

#ifdef __SMP__

       #include <linux/smp_lock.h>

#endif

// функция, выполняющая при загрузке модуля

int init_module(void)

{

       // свершилось! мы вошли в режим ядра

       // и теперь можем делать _все_ что угодно!

       // с порога ссать, с балкона мусор кидать,

       // баб е…

      

       …

      

       // мяукнем что-нибудь

       printk("\nWOW! Our module has been loaded!\n");

      

       // успешная инициализация

       return(0);

}

// функция, выполняющаяся при выгрузке модуля

void cleanup_module(void)

{

       // мяукнем что-нибудь

       printk("\nFuck! Our module has been unloaded\n");

}

// пристыковываем лицензию, по которой распространяется

// данный файл, если этого не сделать, модуль успешно

// загрузится, но операционная система выдаст warring,

// сохраняющийся в логах и привлекающий внимание админов

MODULE_LICENSE("GPL");

Листинг 1 скелет простейшего модуля для ядер с версией 2.4

Начиная с версии 2.6 в ядре произошли значительные изменения и теперь программировать приходится так:

#ifdef LINUX26

       static int __init my_init()

#else

       int init_module()

#endif

#ifdef LINUX26

       static void __exit my_cleanup()

#else

       int cleanup_module()

#endif

#ifdef LINUX26

       module_init(my_init);

       module_exit(my_cleanup);

#endif

Листинг 2 скелет простейшего модуля для ядер с версией 2.6


За подробностями обращайтесь к man'у ("man –k module"), официальной документации (/usr/src/linux/Documentation/modules.txt) или книге "Linux kernel internails", которую легко найти в Осле. Как бы там ни было, только что написанный модуль необходимо откомпилировать: "gcc –c my_module.c –o my_module.o" (настоятельно рекомендуется задействовать оптимизацию, добавив ключ –O2 или -O3), а затем загрузить внутрь ядра: "insmod my_module.o". Загружать модули может только root. Не спрашивайте меня, как его получить — это тема отдельного разговора. Чтобы модуль автоматически загружался вместе с операционной системой — добавьте его в файл /etc/modules.

Команда "lsmod" (или "dd if=/proc/modules bs=1") отображает список загруженных модулей, а "rmmod my_module" выгружает модуль из памяти. Обратите внимание на отсутствие расширения в последней случае.

Module                  Size  Used by    Tainted: P 

my_module                240   0  (unused)

parport_pc             25128   1  (autoclean)

lp                      7460   0

processor               9008   0  [thermal]



fan                     1600   0  (unused)

button                  2700   0  (unused)

rtc                     7004   0  (autoclean)

BusLogic               83612   2  (autoclean)

ext3                   64388   1  (autoclean)

Листинг 3 список модулей, выданный командой lsmod, строка с нашим модулем выделена полужирным шрифтом

Неожиданное появление новых модулей всегда настораживает админов, поэтому прежде чем приступать к боевым действиям, мы должны как следует замаскироваться. Автору известно три способа маскировки: а) исключение модуля из списка модулей (метод J.B., см. файл modhide1.c) – крайне ненадежен, препятствует нормально работе ps, top и других подобных утилит, часто роняет систему; б) перехват обращений к /proc/modules



(метод Runar'a Jensen'а, опубликованный на Bugtraq и реализующийся так же, как и перехват остальных обращений к файловой системе) — довольно громоздкий и ненадежный метод, бессильный против команды "dd if=/proc/modules bs=1"; в) затирание структуры module info (метод Solar'a Designer'a, описанный в статье "Weakening the Linux Kernel", опубликованной в 52 номере PHRACK'a) — элегантный и довольно надежный. Расскажем о нем поподробнее.

Вся информация о модуле хранится в структуре module info, содержащейся внутри системного вызова sys_init_module(). Подготовив модуль к загрузке и заполнив module info

надлежащим образом, он передает управление нашей функции init_module

(см. "man init_module"). Любопытная особенность ядра — безымянные модули без референсов не отображаются! Чтобы удалить модуль из списка достаточно обнулить поля name и refs. Это легко. Определить адрес самой module info намного сложнее. Ядро не заинтересовано сообщать его первому встречному хакеру и приходится действовать исподтишка. Исследуя мусор, оставшийся в регистрах, на момент передачи управления init_module, Solar Dsigner обнаружил, что в одним из них содержится указатель на… module info! В его версии ядра это был регистр EBX, в иных версиях он может быть совсем другим или даже вовсе никаким. К тому же существует специальная заплатка для старых ядер, затыкающая эту лазейку, правда, далеко не у всех она установлена. Впрочем, эффективный адрес module info

легко установить дизассемблированием, точнее не адрес module info (память под него выделяется динамически), а адрес машинной инструкции, ссылающейся на module info. Правда, в каждой версии ядра он будет своим…

Простейший пример маскировки выглядит так (кстати, в PHRACK'e опечатка: "ref" вместо "refs"):

int init_module()

{

       register struct module *mp asm("%ebx");  // подставьте

сюда регистр,



                                                // в котором ваше ядро держит

                                                // адрес module info

      

       *(char*)mp->name=0;                      // затираем имя модуля

       mp->size=0;                              // затираем размер

       mp->refs=0;                              // затираем референсы

}

Листинг 4 маскировка модуля методом Solar'a Dsigner'a

Неправильное определение адреса module  info скорее всего уронит ядро системы или заблокирует просмотр списка модулей, что сразу же насторожит администратора (см. рис 2). Но у нас есть в запасе еще один вариант.

Просматриваем список установленных модулей, находим из них самый ненужный, выгружаем его из памяти, и загружаем свой — с таким же точно именем. Если нам повезет, администратор ничего не заметит…



Рисунок 2 последствия маскировки модуля методом Solar'a Dsigner'a — команды insmod/lsmod/rmmod больше не работают


Содержание раздела