Linux高级内核Inline HOOK
高级Linux Kernel Inline Hook技术分析与实现
[目录]
1. 简述
2. 更改offset实现跳转
3. 补充
4. 如何查杀
5. 实例
一、简述
目前流行和成熟的kernel inline hook技术就是修改内核函数的opcode,通过写入jmp或push ret等指令跳转到新的内核函数中,从而达到修改或过滤的功能。这些技术的共同点就是都会覆盖原有的指令,这样很容易在函数中通过查找jmp,push ret等指令来查出来,因此这种inline hook方式不够隐蔽。本文将使用一种高级inline hook技术来实现更隐蔽的inlinehook技术。
二、更改offset实现跳转
如何不给函数添加或覆盖新指令,就能跳转到我们新的内核函数中去呢?我们知道实现一个系统调用的函数中不可能把所有功能都在这个函数中全部实现,它必定要调用它的下层函数。如果这个下层函数也可以得到我们想要的过滤信息等内容的话,就可以把下层函数在上层函数中的offset替换成我们新的函数的offset,这样上层函数调用下层函数时,就会跳到我们新的函数中,在新的函数中做过滤和劫持内容的工作。原理是这样的,具体来分析它该怎么实现, 我们去看看sys_read的具体实现:
linux-2.6.18/fs/read_write.c
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
EXPORT_SYMBOL_GPL(sys_read);
我们看到sys_read最终是要调用下层函数vfs_read来完成读取数据的操作,所以我们不需要给sys_read添加或覆盖指令, 而是要更改vfs_read在sys_read代码中的offset就可以跳转到我们新的new_vfs_read中去。如何修改vfs_read的offset呢?先反汇编下sys_read看看:
[root@xsec linux-2.6.18]# gdb -q vmlinux
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) disass sys_read
Dump of assembler code for function sys_read:
0xc106dc5a <sys_read+0>: push %ebp
0xc106dc5b <sys_read+1>: mov %esp,%ebp
0xc106dc5d <sys_read+3>: push %esi
0xc106dc5e <sys_read+4>: mov $0xfffffff7,%esi
0xc106dc63 <sys_read+9>: push %ebx
0xc106dc64 <sys_read+10>: sub $0xc,%esp
0xc106dc67 <sys_read+13>: mov 0x8(%ebp),%eax
0xc106dc6a <sys_read+16>: lea 0xfffffff4(%ebp),%edx
0xc106dc6d <sys_read+19>: call 0xc106e16c <fget_light>
0xc106dc72 <sys_read+24>: test %eax,%eax
0xc106dc74 <sys_read+26>: mov %eax,%ebx
0xc106dc76 <sys_read+28>: je 0xc106dcb1 <sys_read+87>
0xc106dc78 <sys_read+30>: mov 0x24(%ebx),%edx
0xc106dc7b <sys_read+33>: mov 0x20(%eax),%eax
0xc106dc7e <sys_read+36>: mov 0x10(%ebp),%ecx
0xc106dc81 <sys_read+39>: mov %edx,0xfffffff0(%ebp)
0xc106dc84 <sys_read+42>: mov 0xc(%ebp),%edx
0xc106dc87 <sys_read+45>: mov %eax,0xffffffec(%ebp)
0xc106dc8a <sys_read+48>: lea 0xffffffec(%ebp),%eax
0xc106dc8d <sys_read+51>: push %eax
0xc106dc8e <sys_read+52>: mov %ebx,%eax
0xc106dc90 <sys_read+54>: call 0xc106d75c <vfs_read>
0xc106dc95 <sys_read+59>: mov 0xfffffff0(%ebp),%edx
0xc106dc98 <sys_read+62>: mov %eax,%esi
0xc106dc9a <sys_read+64>: mov 0xffffffec(%ebp),%eax
0xc106dc9d <sys_read+67>: mov %edx,0x24(%ebx)
0xc106dca0 <sys_read+70>: mov %eax,0x20(%ebx)
0xc106dca3 <sys_read+73>: cmpl $0x0,0xfffffff4(%ebp)
0xc106dca7 <sys_read+77>: pop %eax
0xc106dca8 <sys_read+78>: je 0xc106dcb1 <sys_read+87>
0xc106dcaa <sys_read+80>: mov %ebx,%eax
0xc106dcac <sys_read+82>: call 0xc106e107 <fput>
0xc106dcb1 <sys_read+87>: lea 0xfffffff8(%ebp),%esp
0xc106dcb4 <sys_read+90>: mov %esi,%eax
0xc106dcb6 <sys_read+92>: pop %ebx
0xc106dcb7 <sys_read+93>: pop %esi
0xc106dcb8 <sys_read+94>: pop %ebp
0xc106dcb9 <sys_read+95>: ret
End of assembler dump.
(gdb)
0xc106dc90 <sys_read+54>: call 0xc106d75c <vfs_read>