Chinaunix首页 | 论坛 | 博客
  • 博客访问: 288173
  • 博文数量: 67
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 802
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-14 16:23
文章分类
文章存档

2011年(4)

2010年(18)

2009年(32)

2008年(13)

我的朋友

分类: LINUX

2009-12-02 13:47:53

Linux内核版本2.6中已经不再导出系统调用符号表了。因此,如果想实现劫持系统调用,就得想办法找到系统调用表的地址。网上应该可以搜到相关的实现。我这里找到了albcamus兄的精华文章,并在内核版本2.6.18.3上实践了其中的代码。这里总结一下。

本文欢迎自由转载,但请标明出处和本文链接,并保持本文的完整性。

CU Godbach
     Blog
http://blog.chinaunix.net/u/33048/index.html

Dec 2, 2009

 

一、代码及实现


(一) 劫持open系统调用的代码

内核态实现劫持系统调用的代码如下,来自参考链接1,即albcamus兄提供的代码。我这里屏蔽了一些代码,仅实现了劫持open系统调用。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

MODULE_DESCRIPTION("Intercept the system call table in Linux");

MODULE_AUTHOR("alert7 (alert7@xfocus.org) \n\t\talbcamus ");

MODULE_LICENSE("GPL");

 

 

/* comment the following line to shut me up */

#define INTERCEPT_DEBUG

 

#ifdef INTERCEPT_DEBUG

    #define dbgprint(format,args...) \

        printk("intercept: function:%s-L%d: "format, __FUNCTION__, __LINE__, ##args);

#else

    #define dbgprint(format,args...)  do {} while(0);

#endif

 

 

/**

* the system call table

*/

void **my_table;

 

unsigned int orig_cr0;

 

/**

* the original syscall functions

*/

asmlinkage long (*old_open) (char __user *filename, int flags, int mode);

asmlinkage int  (*old_execve) (struct pt_regs regs);

 

 

 

/** do_execve and do_fork */

unsigned int can_exec_fork = 0;

int    (*new_do_execve) (char * filename,

            char __user *__user *argv,

            char __user *__user *envp,

            struct pt_regs * regs);

 

 

struct idtr {

    unsigned short limit;

    unsigned int base;

} __attribute__ ((packed));

 

 

struct idt {

    unsigned short off1;

    unsigned short sel;

    unsigned char none, flags;

    unsigned short off2;

} __attribute__ ((packed));

 

 

#if 0

/**

*  check if we can intercept fork/vfork/clone/execve or not

*

*  return : 0 for no, 1 for yes

*/

struct kprobe kp_exec;

unsigned int can_intercept_fork_exec(void)

{

    int ret = 0;

 

#ifndef CONFIG_KPROBES

    return ret;

#endif

 

    kp_exec.symbol_name = "do_execve";

 

    ret = register_kprobe(&kp_exec);

    if (ret != 0 ) {

        dbgprint("cannot find do_execve by kprobe.\n");

        return 0;

    }

    new_do_execve = ( int (*)

             (char *,

              char __user * __user *,

              char __user * __user *,

              struct pt_regs *

             )

            ) kp_exec.addr;

 

    dbgprint("do_execve at %p\n", (void *)kp_exec.addr);

    unregister_kprobe(&kp_exec);

 

 

    return 1;

}

 

#endif

 

/**

* clear WP bit of CR0, and return the original value

*/

unsigned int clear_and_return_cr0(void)

{

    unsigned int cr0 = 0;

    unsigned int ret;

 

    asm volatile ("movl %%cr0, %%eax"

              : "=a"(cr0)

              );

    ret = cr0;

 

    /* clear the 20 bit of CR0, a.k.a WP bit */

    cr0 &= 0xfffeffff;

 

    asm volatile ("movl %%eax, %%cr0"

              :

              : "a"(cr0)

              );

    return ret;

}

 

/** set CR0 with new value

*

* @val : new value to set in cr0

*/

void setback_cr0(unsigned int val)

{

    asm volatile ("movl %%eax, %%cr0"

              :

              : "a"(val)

              );

}

 

 

/**

* Return the first appearence of NEEDLE in HAYSTACK. 

* */

static void *memmem(const void *haystack, size_t haystack_len,

            const void *needle, size_t needle_len)

{/*{{{*/

    const char *begin;

    const char *const last_possible

        = (const char *) haystack + haystack_len - needle_len;

 

    if (needle_len == 0)

        /* The first occurrence of the empty string is deemed to occur at

           the beginning of the string.  */

        return (void *) haystack;

 

    /* Sanity check, otherwise the loop might search through the whole

       memory.  */

    if (__builtin_expect(haystack_len < needle_len, 0))

        return NULL;

 

    for (begin = (const char *) haystack; begin <= last_possible;

         ++begin)

        if (begin[0] == ((const char *) needle)[0]

            && !memcmp((const void *) &begin[1],

                   (const void *) ((const char *) needle + 1),

                   needle_len - 1))

            return (void *) begin;

 

    return NULL;

}/*}}}*/

 

 

/**

* Find the location of sys_call_table

*/

static unsigned long get_sys_call_table(void)

{/*{{{*/

/* we'll read first 100 bytes of int $0x80 */

#define OFFSET_SYSCALL 100       

 

    struct idtr idtr;

    struct idt idt;

    unsigned sys_call_off;

    unsigned retval;

    char sc_asm[OFFSET_SYSCALL], *p;

 

    /* well, let's read IDTR */

    asm("sidt %0":"=m"(idtr)

             :

             :"memory" );

 

    dbgprint("idtr base at 0x%X, limit at 0x%X\n", (unsigned int)idtr.base,(unsigned short)idtr.limit);

 

    /* Read in IDT for vector 0x80 (syscall) */

    memcpy(&idt, (char *) idtr.base + 8 * 0x80, sizeof(idt));

 

    sys_call_off = (idt.off2 << 16) | idt.off1;

 

    dbgprint("idt80: flags=%X sel=%X off=%X\n",

                 (unsigned) idt.flags, (unsigned) idt.sel, sys_call_off);

 

    /* we have syscall routine address now, look for syscall table

       dispatch (indirect call) */

    memcpy(sc_asm, (void *)sys_call_off, OFFSET_SYSCALL);

 

    /**

     * Search opcode of `call sys_call_table(,eax,4)'

     */

    p = (char *) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\x85", 3);

    if (p == NULL)

        return 0;

 

    retval = *(unsigned *) (p + 3);

    if (p) {

        dbgprint("sys_call_table at 0x%x, call dispatch at 0x%x\n",

             retval, (unsigned int) p);

    }

    return retval;

#undef OFFSET_SYSCALL

}/*}}}*/

 

 

 

/**

* new_open - replace the original sys_open when initilazing,

*           as well as be got rid of when removed

*/

asmlinkage long new_open(char *filename, int flags, int mode)

{

    dbgprint("call open()\n");

    return old_open (filename, flags, mode);

}

 

 

/**

* new_execve - you should change this function whenever the kernel's sys_execve()

* changes

*/

asmlinkage int new_execve(struct pt_regs regs)

{

    int error;

    char *filename;

 

    dbgprint("Hello\n");

 

    filename = getname( (char __user *) regs.ebx );

    error = PTR_ERR(filename);

    if ( IS_ERR(filename) )

        goto out;

    dbgprint("file to execve: %s\n", filename);

    error = new_do_execve(filename,

                  (char __user * __user *) regs.ecx,

                  (char __user * __user *) regs.edx,

                  ®s);

    if (error == 0) {

        task_lock(current);

        current->ptrace &= ~PT_DTRACE;

        task_unlock(current);

        set_thread_flag(TIF_IRET);

    }

    putname (filename);

out:

    return error;

}

 

static int intercept_init(void)

{

    my_table = (void **)get_sys_call_table();

    if (my_table == NULL)

        return -1;

 

    dbgprint("sys call table address %p\n", (void *) my_table);

 

#define REPLACE(x) old_##x = my_table[__NR_##x];\

    my_table[__NR_##x] = new_##x

 

  

    REPLACE(open);

#if 0

    can_exec_fork = can_intercept_fork_exec();

    if(can_exec_fork == 1)

        REPLACE(execve);

#endif

 

#undef REPLACE

    return 0;

}

 

 

 

 

 

static int __init this_init(void)

{

    int ret;

    printk("syscall intercept: Hi, poor linux!\n");

 

    orig_cr0 = clear_and_return_cr0();  

    ret = intercept_init();

    setback_cr0(orig_cr0);

 

    return ret;

}

 

static void __exit this_fini(void)

{

    printk("syscall intercept: bye, poor linux!\n");

 

#define RESTORE(x) my_table[__NR_##x] = old_##x

 

    orig_cr0 = clear_and_return_cr0();  

    RESTORE(open);

#if 0

    if(can_exec_fork == 1)

        RESTORE(execve);

#endif

    setback_cr0(orig_cr0);

 

#undef RESTORE

}

 

module_init(this_init);

module_exit(this_fini);


(二) 编译及实践

Makefile如下:

obj-m   :=hack_open.o

EXTRA_CFLAGS := -Dsymname=sys_call_table

KDIR   := /lib/modules/$(shell uname -r)/build

PWD   := $(shell pwd)

default:

    make -C $(KDIR) SUBDIRS=$(PWD) modules

clean:

    rm -rf .*.cmd *.mod.c *.o *.ko .tmp* *.symvers


编译之后,加载模块,然后查看日志信息

Sep 24 19:06:49 localhost kernel: intercept: function:get_sys_call_table-L220: sys_call_table at 0xc11f14e0, call dispatch at 0xcebeceaa
Sep 24 19:06:49 localhost kernel: intercept: function:intercept_init-L276: sys call table address c11f14e0
Sep 24 19:06:50 localhost kernel: intercept: function:new_open-L234: hello
Sep 24 19:07:00 localhost last message repeated 460 times

可见open系统调用执行次数之频繁。


--未完待续

阅读(4517) | 评论(6) | 转发(0) |
给主人留下些什么吧!~~

yzh071372015-04-06 14:40:45

你好,我在ubuntu12.04.5(内核是3.13.0.20-32-generic)上试验了,结果编译报错了,那个Makefile不能成功执行,提示下面这个错误:
error:'struct pt_regs' has no member named 'ebx';
error:'struct pt_regs' has no member named 'ecx';
error:'struct pt_regs' has no member named 'edx';
error:'TIF_IRET' undeclared(first used in this function);
这是error,还有一些警告,请问是什么原因啊,是因为内核版本的问题吗?

Godbach2010-04-20 20:28:22

snriyt兄奖了。 有问题多交流,大家共同进步。

snriyt2010-04-20 15:06:31

godbach兄台人才也! 不错不错,还得多多请教。嘿嘿

Godbach2009-12-28 00:07:45

记得内核版有人贴过64位系统上获取系统调用表的方法,可以搜一下。

Godbach2009-12-28 00:06:01

首先,我上面的代码是在32位机上测试的,不知道64为上是否可以正常工作。其次,记得一些高版本的确实限制的比较严格了。你可以搜一下如何获取64位系统上获取系统调用表的方式。