网络安全 频道

在Linux下用gdb检测内核rootkit

我们注意到第一行最后的0xc88ab11a这个地址明显不正常,这是系统调用号为3的系统调用,即sys_read (系统调用从0开始) 。

我们说它不正常的显著标志是它的地址高于0xc8xxxxxx,Linux默认4GB线性地址,其中最高1GB0x00000000-0xffffffff为内核保留,当一个模块被插入内核时,vmalloc函数为其分配一段地址空间,这个地址通常从0xc8800000开始...到这里已经很明显了吧?

系统调用劫持

劫持系统调用与上一种方法不同之处在于:它并不直接修改系统调用表中的入口地址,即指向每个系统调用的跳转指针,而是在想要hook的系统调用之前加一段跳转代码,使执行流重定向到入侵者自己的内核态函数,这些被hook的系统调用前部通常有call,jmp之类的汇编指令。

要检测这种攻击,同样使用gdb加vmlinux-2.4.*及/proc/kcore两个参数,然后反汇编系统调用:

#gdb /boot/vmlinux-2.4.* /proc/kcore 

(gdb) disass sys_read 

Dump of assembler code for function sys_read: 

0xc013fb70 <sys_read>: mov $0xc88ab0a6,%ecx 

0xc013fb73 <sys_read+3>: jmp *%ecx <<-- 

0xc013fb77 <sys_read+7>: mov %esi,0x1c(%esp,1) 

0xc013fb7b <sys_read+11>: mov %edi,0x20(%esp,1) 

0xc013fb7f <sys_read+15>: mov $0xfffffff7,%edi 

...

我们注意"mov $0xc88ab0a6,%ecx -- jmp *%ecx"这两条指令,他跳转到了其他的地方去执行了。

然后再来看一下被hook之前的系统调用指令:

#gdb /boot/vmlinx-2.4.* 

(gdb) disass sys_read 

Dump of assembler code for function sys_read: 

0xc013fb70 <sys_read>: sub $0x28,%esp 

0xc013fb73 <sys_read+3>: mov 0x2c(%esp,1),%eax 

0xc013fb77 <sys_read+7>: mov %esi,0x1c(%esp,1) 

0xc013fb7b <sys_read+11>: mov %edi,0x20(%esp,1) 

0xc013fb7f <sys_read+15>: mov $0xfffffff7,%edi 

...

看到了吧,不一样的。

更改系统调用处理例程

入侵者可能修改一些重要的内核函数,比如系统调用处理例程system_call函数,顾名思义,这个函数对用户请求的系统调用作出响应,在系统调用表中寻找对应的入口地址,然后跳转到那里执行,这个函数中保存了系统调用表的地址。攻击者能做什么呢?另辟一块内存空间,在那里攻击者伪造自己的系统调用表,然后修改system_call函数中的系统调用表地址指向那里就可以了。

通过反汇编system_call函数可以找出系统调用表的地址:

(gdb) disass system_call 

Dump of assembler code for function system_call: 

0xc01090dc <system_call>: push %eax 

0xc01090dd <system_call+1>: cld 

0xc01090de <system_call+2>: push %es 

0xc01090df <system_call+3>: push %ds 

0xc01090e0 <system_call+4>: push %eax 

0xc01090e1 <system_call+5>: push %ebp 

0xc01090e2 <system_call+6>: push %edi 

0xc01090e3 <system_call+7>: push %esi 

0xc01090e4 <system_call+8>: push %edx 

0xc01090e5 <system_call+9>: push %ecx 

0xc01090e6 <system_call+10>: push %ebx 

0xc01090e7 <system_call+11>: mov $0x18,%edx 

0xc01090ec <system_call+16>: mov %edx,%ds 

0xc01090ee <system_call+18>: mov %edx,%es 

0xc01090f0 <system_call+20>: mov $0xffffe000,%ebx 

0xc01090f5 <system_call+25>: and %esp,%ebx 

0xc01090f7 <system_call+27>: testb $0x2,0x18(%ebx) 

0xc01090fb <system_call+31>: jne 0xc010915c <tracesys> 

0xc01090fd <system_call+33>: cmp $0x100,%eax 

0xc0109102 <system_call+38>: jae 0xc0109189 <badsys> 

0xc0109108 <system_call+44>: call *0xc0302c30 (,%eax,4) <<--系统调用表地址 

0xc010910f <system_call+51>: mov %eax,0x18(%esp,1) 

0xc0109113 <system_call+55>: nop 

End of assembler dump.

注意:上面的输出中显示的是一个正常的系统调用表地址。

实用工具

一种方法是使用基于主机的入侵检测系统HIDS实时监控重要的内核结构,比如使用Samhain工具,可以监视系统调用表、IDT等,在“Host Integrity Monitoring: Best Practices for Deployment”一文中有相关描述。

译者注

本文提及的方法在kstat2.4版中都有代码的实现,可以参阅kstat/2.4/src/syscall.c,使用gdb是一种手工检测方法,它能解决的问题是检测系统是否被更改,至于如何找出内核rootkit还需要一些工具,比如madsys在phrack60上的module_hunter.c,有2.4和2.6的版本,grip2、coolq对其做了一些修改,并且该代码不断完善中。

http://www.cnxhacker.com/Article/OS/Unix/200611/6734.html

0
相关文章