版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://chenm.blogbus.com/logs/52077181.html
2.4的内核中可以通过修改sys_call_table来很轻松的替换系统调用,来完成很多trick,很多的rootkit就是基于这种手法来完成的。到了2.6的内核,事情就不是这么回事了,sys_call_table不再被export出来了,这样的话就不能简单通过修改sys_call_table来替换系统调用了。如果再继续使用sys_call_table的话,将会得到类似如下的警告:
WARNING: "sys_call_table" [/mnt/linux/km/lpp/src/lpp.ko] undefined!
当然,加载模块也会失败的。
# insmod lpp.ko
insmod: error inserting 'lpp.ko': -1 Unknown symbol in module
# dmesg
lpp: Unknown symbol sys_call_table
当然,虽然没有export出来,但我们还是有办法获取到sys_call_table的,在C语言中,sys_call_table也就是一个地址而已,我们的问题就是如何获取这个地址。有如下2种方法:
方法1:
unsigned long **find_sys_call_table(void) {
unsigned long **sctable;
unsigned long ptr;
extern int loops_per_jiffy;
sctable = NULL;
for (ptr = (unsigned long)&loops_per_jiffy;
ptr < (unsigned long)&boot_cpu_data; ptr += sizeof(void *)){
unsigned long *p;
p = (unsigned long *)ptr;
if (p[__NR_close] == (unsigned long) sys_close){
sctable = (unsigned long **)p;
return &sctable[0];
}
}
return NULL;
}
unsigned long **sys_call_table = find_sys_call_table();
一个完整的示例:
http://www.gnome.org/~lcolitti/gnome-startup/linux-iolog/readlog.c
注意,在最新的内核2.6.30的内核它不能编译,需要对第52行进行修改:
52c52
< f = current->files->fd[fd];
---
> f = current->files->fd_array[fd];
方法2:去System.map中找。
grep sys_call_table /boot/System.map
方法3:当然你也可以直接修改内核代码,然后再重新编译内核。
2.6的内核之所以不把sys_call_table给export出来,不仅仅是因为安全的原因,另外一个原因是我们真正的需要在内核去替换系统调用吗?在用户空间,我们照样也能完成系统调用的替换,为什么不在用户空间去做呢?
使用Linux的LD_PRELOAD特性,Linux的man page对LD_PRELOAD的解释:
LD_PRELOAD
A whitespace-separated list of additional, user-specified, ELF
shared libraries to be loaded before all others. This can be
used to selectively override functions in other shared
libraries. For setuid/setgid ELF binaries, only libraries in
the standard search directories that are also setgid will be
loaded.
借助LD_PRELOAD我们可以在用户空间轻松完成所有系统调用的替换,比如我们写一个动态库,源代码如下
:
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
ssize_t (*readfn)(int, void*, size_t);
static void init(void) __attribute__((constructor));
static void init(void)
{
fprintf(stderr, "Preloaded/n");
readfn = dlsym(RTLD_NEXT, "read");
}
ssize_t read(int fd, void *buf, size_t nbytes)
{
fprintf(stderr, "My read/n");
return readfn(fd, buf, nbytes);
}
编译:
gcc -shared -g -o libcm.so -ldl
然后再这样使用LD_PRELOAD:
LD_PRELOAD=./libcm.so ./a.out
这样a.out里面所有的read调用都会进入到我们read函数中去了。
参考:
- http://kerneltrap.org/node/5802
- http://kerneltrap.org/node/5793
- http://en.wikipedia.org/wiki/Dynamic_linker