目前流行和成熟的kernel inline hook技术就是修改内核函数的opcode,通过写入jmp或push ret等指令跳转到新的内核函数中,从而达到修改或过滤的功能。这些技术的共同点就是都会覆盖原有的指令,这样很容易在函数中通过查找jmp,push ret等指令来查出来,因此这种inline hook方式不够隐蔽。本文将使用一种高级inline hook技术来实现更隐蔽的inline hook技术。
二、更改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
0xc106dc5b
0xc106dc5d
0xc106dc5e
0xc106dc63
0xc106dc64
0xc106dc67
0xc106dc6a
0xc106dc6d
0xc106dc72
0xc106dc74
0xc106dc76
0xc106dc78
0xc106dc7b
0xc106dc7e
0xc106dc81
0xc106dc84
0xc106dc87
0xc106dc8a
0xc106dc8d
0xc106dc8e
0xc106dc90
0xc106dc95
0xc106dc98
0xc106dc9a
0xc106dc9d
0xc106dca0
0xc106dca3
0xc106dca7
0xc106dca8
0xc106dcaa
0xc106dcac
0xc106dcb1
0xc106dcb4
0xc106dcb6
0xc106dcb7
0xc106dcb8
0xc106dcb9
End of assembler dump.
(gdb)
0xc106dc90
通过call指令来跳转到vfs_read中去。0xc106d75c是vfs_read的内存地址。所以只要把这个地址替换成我们的新函数地址,当sys_read执行这块的时候,就会跳转到我们的函数来了。
下面给出我写的一个hook引擎,来完成查找和替换offset的功能。原理就是搜索sys_read的opcode,如果发现是call指令,根据call后面的offset重新计算要跳转的地址是不是我们要hook的函数地址,如果是就重新计算新函数的offset,用新的offset替换原来的offset。从而完成跳转功能。
参数handler是上层函数的地址,这里就是sys_read的地址,old_func是要替换的函数地址,这里就是vfs_read, new_func是新函数的地址,这里就是new_vfs_read的地址。
unsigned int patch_kernel_func(unsigned int handler, unsigned int old_func,
&n