|
Root поселение на Unix тачке
Xakep, номер #049, стр. 049-086-1 Разработка своего lkm-руткита под FreeBSD и Linux maravan (maravan@real.xakep.ru) В последнее время довольно популярными стали lkm-руткиты. Аббревиатура lkm расшифровывается как loadable kernel module. Как следует из названия, lkm - это программа, предназначенная для подгрузки в ядро операционной системы. Почему использование модулей ядра настолько популярно при написании руткитов? Ответ на этот вопрос не вызывает сомнений: получив возможность работать напрямую с ядром, можно делать с системой фактически все, что заблагорассудится. Это и полный контроль надо всем, что происходит в системе, и новые возможности, недоступные для "обычного" софта, работающего вне ядра, и многое другое. Следовательно, грамотно написанный lkm-руткит после своего подгружения в ядро может поставить администратора системы в тупик. Единственным способом избавления от руткита будет полная переустановка системы. Как это делается По своей структуре и по способу загрузки lkm очень напоминает обычную библиотеку. Когда юзерский процесс инициирует процесс загрузки модуля в ядро, т.е. "сообщает" ядру посредством некоторого системного вызова желание подгрузить код этого лкм (lkm), ядро мапит код и данные модуля в свое адресное пространство, ищет в таблице символов модуля точку входа и передает на нее управление. В принципе, все. Вот что представляет собой простейший модуль для FreeBSD, который не выполняет никаких операций, а просто подгружается в ядро и сидит там, пока мы его не выгрузим: /* headers skipped */ static moduledata_t dummy_module = {"dummy_module", module_ops, NULL}; static struct sysinit sys_init = { SI_SUB_DRIVERS, SI_ORDER_MIDDLE, module_register_init, &dummy_module }; static int module_ops (struct module *module, int cmd, void *args) { switch (cmd) { case MOD_LOAD: break; case MOD_UNLOAD: break; default: return (EINVAL); } return (0); } static void const * const __set_sysinit_set_sym_syscall_sys_init = &sys_init; __asm(".section .set.""sysinit_set"",\"aw\""); __asm(".long " "sys_init"); __asm(".previous"); Небольшое пояснение: module_ops() - это своего рода функция main() нашего модуля. На нее передается управление при загрузке модуля в ядро, а также при его выгрузке. При загрузке ей передается параметр cmd, имеющий значение MOD_LOAD, а при выгрузке, соответственно, MOD_UNLOAD. sys_init - структура, при помощи которой ядро и ведет свой "диалог" с модулем. На данный момент единственным интересным для нас элементом этой структуры является поле udata, в котором содержится указатель на структуру dummy_module. dummy_module - структура, описывающая наш модуль. В ней содержится непосредственно название модуля ("dummy_module"), указатель на module_ops и указатель на дополнительные данные о нашем модуле, имеющий значение NULL. А вот то же самое, но только для линукса: /* headers skipped */ int init_module (void) { return (0); } void cleanup_module (void) { return; } Структура модуля в линуксе гораздо проще, чем во Free BSD - здесь есть только init_module(), который запускается при загрузке модуля, и cleanup_module, запускающийся при его удалении из ядра. |