2015-01-16 3 views
0

주소가 sys_call_table from /boot/System.map- uname-r이고 커널이 2.6.32 (x86-32)입니다.리눅스 커널에서 read syscall을 가로 챌 때 버그가 발생했습니다

syscalls except 'read'을 가로 챌 때 내 모듈이 훌륭하게 작동했습니다. 그러나 'read' syscall,을 가로 챌 때 버그가 발생했습니다. 다음과 같은 다시 :

#include <linux/kernel.h> 
#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/kprobes.h> 
#include <linux/kallsyms.h> 
#include <linux/sched.h> 
#include <linux/ptrace.h> 
#include <linux/mm.h> 
#include <linux/fs.h> 
#include <linux/file.h> 
#include <linux/smp.h> 
#include <linux/user.h> 
#include <linux/errno.h> 
#include <linux/cpu.h> 
#include <asm/uaccess.h> 
#include <asm/fcntl.h> 
#include <asm/unistd.h> 

MODULE_DESCRIPTION("Intercept the system call table in Linux"); 
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_read) (unsigned int fd, char __user *buf, size_t count); 


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)); 



/** 
* * 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 appearance 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\n", (unsigned int)idtr.base); 

    /* 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 
}/*}}}*/ 

asmlinkage long new_read(unsigned int fd, char __user *buf, size_t count) 
{ 
    long ret; 
    struct file *file; 
    ret = old_read(fd, buf, count); 
    /*0 represent EOF 
    * We just collect a 'read' when finished 
    */ 
    if(ret == 0){ 
     file = fget(fd); 
     if(file){ 
      fput(file); 
     } 
    } 
    return ret; 
} 

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(read); 

#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(read); 
    setback_cr0(orig_cr0); 

#undef RESTORE 
} 

module_init(this_init); 
module_exit(this_fini); 

PS :

(tty1)       (tty2) 

insmod mymodule.ko    opened 
read some files 
rmmod mymodule   
insmod mymodule.ko  
           switch to this tty* 

후 커널 패닉이 발생하고 시스템 충돌 ... 여기 내 코드입니다 new_read 함수 블록 내 모듈이 잘 어울리는 경우 삭제 .

누구나 그 이유와 수정 방법을 알고 있습니다. 내가 원했던 것은 파일 구조의 정보를 얻는 것뿐입니다.

답변

0

읽은 syscall이 실제 읽기에 대한 완벽한 대체품 이었습니까? 그것은 재귀 적으로 뭔가를 했습니까? read라는 함수를 호출하는 것? 읽기는 상당히 낮은 수준의 시스템 호출입니다. 거의 모든 것들이 어떤 일을하기 위해 파일 기술자로부터 정보를 읽어야합니다 ...

+0

실제 읽기를 대체 할 수 있는지 확실하지 않습니다. 읽기 시스템 호출을 완벽하게 가로 채기위한 해결책이 있습니까? – wugj03

+0

저는 사실 c 프로그래머가 아니지만'fget'와'fput'을 올바르게 호출하고 있습니다. 사실'fgets'가 아닌가? http://www.techonthenet.com/c_language/standard_library_functions/stdio_h/fgets.php – aychedee

+0

fget와 fput은 커널 프로그래머가 fd에 의해 "파일 구조"를 얻거나 리눅스가 제공 한 파일 구조를 해제하는 한쌍의 함수입니다 핵심. – wugj03