sys_call_table undefined 及 如何替换系统调用

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值