Chinaunix

标题: Linux下实现劫持系统调用的总结 [打印本页]

作者: Godbach    时间: 2009-12-02 13:20
标题: Linux下实现劫持系统调用的总结
原文链接:http://blog.chinaunix.net/u/33048/showart_2109264.html


目录
一、代码及实现
(一) 劫持open系统调用的代码
(二) 编译及实践
二、实现原理分析
(一)中断向量表地址的获取
(二)系统调用中断向量地址的获取
(三)系统调用处理例程地址的获取
(四)系统调用表地址的获取
(五)系统调用的替换
参考链接



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系统调用。
  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/kprobes.h>
  5. #include <linux/kallsyms.h>
  6. #include <linux/sched.h>
  7. #include <linux/ptrace.h>
  8. #include <linux/mm.h>
  9. #include <linux/smp.h>
  10. #include <linux/user.h>
  11. #include <linux/errno.h>
  12. #include <linux/cpu.h>
  13. #include <asm/uaccess.h>
  14. #include <asm/fcntl.h>
  15. #include <asm/unistd.h>

  16. MODULE_DESCRIPTION("Intercept the system call table in Linux");
  17. MODULE_AUTHOR("alert7 ([email]alert7@xfocus.org[/email]) \n\t\talbcamus <[email]albcamus@gmail.com[/email]>");
  18. MODULE_LICENSE("GPL");


  19. /* comment the following line to shut me up */
  20. #define INTERCEPT_DEBUG

  21. #ifdef INTERCEPT_DEBUG
  22.     #define dbgprint(format,args...) \
  23.         printk("intercept: function:%s-L%d: "format, __FUNCTION__, __LINE__, ##args);
  24. #else
  25.     #define dbgprint(format,args...)  do {} while(0);
  26. #endif


  27. /**
  28. * the system call table
  29. */
  30. void **my_table;

  31. unsigned int orig_cr0;

  32. /**
  33. * the original syscall functions
  34. */
  35. asmlinkage long (*old_open) (char __user *filename, int flags, int mode);
  36. asmlinkage int  (*old_execve) (struct pt_regs regs);



  37. /** do_execve and do_fork */
  38. unsigned int can_exec_fork = 0;
  39. int    (*new_do_execve) (char * filename,
  40.             char __user *__user *argv,
  41.             char __user *__user *envp,
  42.             struct pt_regs * regs);


  43. struct idtr {
  44.     unsigned short limit;
  45.     unsigned int base;
  46. } __attribute__ ((packed));


  47. struct idt {
  48.     unsigned short off1;
  49.     unsigned short sel;
  50.     unsigned char none, flags;
  51.     unsigned short off2;
  52. } __attribute__ ((packed));


  53. #if 0
  54. /**
  55. *  check if we can intercept fork/vfork/clone/execve or not
  56. *
  57. *  return : 0 for no, 1 for yes
  58. */
  59. struct kprobe kp_exec;
  60. unsigned int can_intercept_fork_exec(void)
  61. {
  62.     int ret = 0;

  63. #ifndef CONFIG_KPROBES
  64.     return ret;
  65. #endif

  66.     kp_exec.symbol_name = "do_execve";

  67.     ret = register_kprobe(&kp_exec);
  68.     if (ret != 0 ) {
  69.         dbgprint("cannot find do_execve by kprobe.\n");
  70.         return 0;
  71.     }
  72.     new_do_execve = ( int (*)
  73.              (char *,
  74.               char __user * __user *,
  75.               char __user * __user *,
  76.               struct pt_regs *
  77.              )
  78.             ) kp_exec.addr;

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


  81.     return 1;
  82. }

  83. #endif

  84. /**
  85. * clear WP bit of CR0, and return the original value
  86. */
  87. unsigned int clear_and_return_cr0(void)
  88. {
  89.     unsigned int cr0 = 0;
  90.     unsigned int ret;

  91.     asm volatile ("movl %%cr0, %%eax"
  92.               : "=a"(cr0)
  93.               );
  94.     ret = cr0;

  95.     /* clear the 16 bit of CR0, a.k.a WP bit */
  96.     cr0 &= 0xfffeffff;

  97.     asm volatile ("movl %%eax, %%cr0"
  98.               :
  99.               : "a"(cr0)
  100.               );
  101.     return ret;
  102. }

  103. /** set CR0 with new value
  104. *
  105. * @val : new value to set in cr0
  106. */
  107. void setback_cr0(unsigned int val)
  108. {
  109.     asm volatile ("movl %%eax, %%cr0"
  110.               :
  111.               : "a"(val)
  112.               );
  113. }


  114. /**
  115. * Return the first appearence of NEEDLE in HAYSTACK.  
  116. * */
  117. static void *memmem(const void *haystack, size_t haystack_len,
  118.             const void *needle, size_t needle_len)
  119. {/*{{{*/
  120.     const char *begin;
  121.     const char *const last_possible
  122.         = (const char *) haystack + haystack_len - needle_len;

  123.     if (needle_len == 0)
  124.         /* The first occurrence of the empty string is deemed to occur at
  125.            the beginning of the string.  */
  126.         return (void *) haystack;

  127.     /* Sanity check, otherwise the loop might search through the whole
  128.        memory.  */
  129.     if (__builtin_expect(haystack_len < needle_len, 0))
  130.         return NULL;

  131.     for (begin = (const char *) haystack; begin <= last_possible;
  132.          ++begin)
  133.         if (begin[0] == ((const char *) needle)[0]
  134.             && !memcmp((const void *) &begin[1],
  135.                    (const void *) ((const char *) needle + 1),
  136.                    needle_len - 1))
  137.             return (void *) begin;

  138.     return NULL;
  139. }/*}}}*/


  140. /**
  141. * Find the location of sys_call_table
  142. */
  143. static unsigned long get_sys_call_table(void)
  144. {/*{{{*/
  145. /* we'll read first 100 bytes of int $0x80 */
  146. #define OFFSET_SYSCALL 100        

  147.     struct idtr idtr;
  148.     struct idt idt;
  149.     unsigned sys_call_off;
  150.     unsigned retval;
  151.     char sc_asm[OFFSET_SYSCALL], *p;

  152.     /* well, let's read IDTR */
  153.     asm("sidt %0":"=m"(idtr)
  154.              :
  155.              :"memory" );

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

  157.     /* Read in IDT for vector 0x80 (syscall) */
  158.     memcpy(&idt, (char *) idtr.base + 8 * 0x80, sizeof(idt));

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

  160.     dbgprint("idt80: flags=%X sel=%X off=%X\n",
  161.                  (unsigned) idt.flags, (unsigned) idt.sel, sys_call_off);

  162.     /* we have syscall routine address now, look for syscall table
  163.        dispatch (indirect call) */
  164.     memcpy(sc_asm, (void *)sys_call_off, OFFSET_SYSCALL);

  165.     /**
  166.      * Search opcode of `call sys_call_table(,eax,4)'
  167.      */
  168.     p = (char *) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\x85", 3);
  169.     if (p == NULL)
  170.         return 0;

  171.     retval = *(unsigned *) (p + 3);
  172.     if (p) {
  173.         dbgprint("sys_call_table at 0x%x, call dispatch at 0x%x\n",
  174.              retval, (unsigned int) p);
  175.     }
  176.     return retval;
  177. #undef OFFSET_SYSCALL
  178. }/*}}}*/



  179. /**
  180. * new_open - replace the original sys_open when initilazing,
  181. *           as well as be got rid of when removed
  182. */
  183. asmlinkage long new_open(char *filename, int flags, int mode)
  184. {
  185.     dbgprint("call open()\n");
  186.     return old_open (filename, flags, mode);
  187. }


  188. /**
  189. * new_execve - you should change this function whenever the kernel's sys_execve()
  190. * changes
  191. */
  192. asmlinkage int new_execve(struct pt_regs regs)
  193. {
  194.     int error;
  195.     char *filename;

  196.     dbgprint("Hello\n");

  197.     filename = getname( (char __user *) regs.ebx );
  198.     error = PTR_ERR(filename);
  199.     if ( IS_ERR(filename) )
  200.         goto out;
  201.     dbgprint("file to execve: %s\n", filename);
  202.     error = new_do_execve(filename,
  203.                   (char __user * __user *) regs.ecx,
  204.                   (char __user * __user *) regs.edx,
  205.                   &regs);
  206.     if (error == 0) {
  207.         task_lock(current);
  208.         current->ptrace &= ~PT_DTRACE;
  209.         task_unlock(current);
  210.         set_thread_flag(TIF_IRET);
  211.     }
  212.     putname (filename);
  213. out:
  214.     return error;
  215. }

  216. static int intercept_init(void)
  217. {
  218.     my_table = (void **)get_sys_call_table();
  219.     if (my_table == NULL)
  220.         return -1;

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

  222. #define REPLACE(x) old_##x = my_table[__NR_##x];\
  223.     my_table[__NR_##x] = new_##x

  224.    
  225.     REPLACE(open);
  226. #if 0
  227.     can_exec_fork = can_intercept_fork_exec();
  228.     if(can_exec_fork == 1)
  229.         REPLACE(execve);
  230. #endif

  231. #undef REPLACE
  232.     return 0;
  233. }





  234. static int __init this_init(void)
  235. {
  236.     int ret;
  237.     printk("syscall intercept: Hi, poor linux!\n");

  238.     orig_cr0 = clear_and_return_cr0();   
  239.     ret = intercept_init();
  240.     setback_cr0(orig_cr0);

  241.     return ret;
  242. }

  243. static void __exit this_fini(void)
  244. {
  245.     printk("syscall intercept: bye, poor linux!\n");

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

  247.     orig_cr0 = clear_and_return_cr0();   
  248.     RESTORE(open);
  249. #if 0
  250.     if(can_exec_fork == 1)
  251.         RESTORE(execve);
  252. #endif
  253.     setback_cr0(orig_cr0);

  254. #undef RESTORE
  255. }

  256. module_init(this_init);
  257. 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系统调用执行次数之频繁。

[ 本帖最后由 Godbach 于 2010-1-19 11:24 编辑 ]
作者: Godbach    时间: 2009-12-02 13:22
二、实现原理分析
        实现的方法就是通过中断向量表,找到系统调用的中断向量,再通过系统调用时执行的指令,最终找到系统调用表的地址, 进行系统调用的替换。。

(一)中断向量表地址的获取
        中断向量表(IDT)的入口地址是通过IDTR寄存器来确定的。IDTR寄存器的内容如下图所示。

这就是上面代码中定义结构体
  1. struct idtr {
  2.     unsigned short limit;
  3.     unsigned int base;
  4. } __attribute__ ((packed))
复制代码
的原因。
idtr寄存器的内容可以通过汇编指令sidt取出,然后就可以IDT的入口地址idtr.base,即高32bit。

(二)系统调用中断向量地址的获取
下一步就通过IDT找到系统调用中断即(0x80)的地址。下图即一个中断向量的组成。由此可以见,一个中断向量占用8个字节。因此,系统调用中断的地址可以表示为:
idt  = idtr.base + 8 * 0x80



(三)系统调用处理例程地址的获取
获取到系统调用中断的地址后,同样需要一个数据结构,将中断向量描述符的相关内容保存。数据结构的定义如下:
  1. struct idt {
  2.     unsigned short off1;
  3.     unsigned short sel;
  4.     unsigned char none, flags;
  5.     unsigned short off2;
  6. } __attribute__ ((packed));
复制代码
通过以上数据结构就可以得到系统调用中断发生时的中断处理例程的地址,即函数system_call()函数的地址:
  1. sys_call_off = idt.off2 << 16 | idt.off1
复制代码
该函数是用汇编实现的,位于arch/i386/kernel/entry.S,下面截取该函数的部分实现:
    # system call handler stub
ENTRY(system_call)
    RING0_INT_FRAME         # can't unwind into user space anyway
    pushl %eax          # save orig_eax
    CFI_ADJUST_CFA_OFFSET 4
    SAVE_ALL
    GET_THREAD_INFO(%ebp)
    testl $TF_MASK,EFLAGS(%esp)
    jz no_singlestep
    orl $_TIF_SINGLESTEP,TI_flags(%ebp)
no_singlestep:
                    # system call tracing in operation / emulation
    /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */
    testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
    jnz syscall_trace_entry
    cmpl $(nr_syscalls), %eax
    jae syscall_badsys
syscall_call:
    call *sys_call_table(,%eax,4)
movl %eax,EAX(%esp)     # store the return value

(四)系统调用表地址的获取

从上面代码中,我们可以看到,系统调用表的入口地址就是上面倒数第二行中sys_call_table。那么如果获取到sys_call_table的地址呢。由于这行代码执行了函数调用的指令call,该指令对应的指令码为\xff\x14\x85。因此,我们只要在system_call函数体内搜索的前三个字节为\xff\x14\x85的内存地址,然后将该地址加3,即可获取到sys_call_table的地址。同时,还有一个问题需要确定,就是call *sys_call_table(,%eax,4)这条指令相对于system_call函数体的偏移是多少,这样我们可以确定内存搜索的范围。下面是entry.o函数反汇编的部分代码:
000000d0 <system_call>:
  d0:   50                      push   %eax
  d1:   fc                      cld   
  d2:   06                      push   %es  
  d3:   1e                      push   %ds  
  d4:   50                      push   %eax
  d5:   55                      push   %ebp
  d6:   57                      push   %edi
  d7:   56                      push   %esi
  d8:   52                      push   %edx
  d9:   51                      push   %ecx
  da:   53                      push   %ebx
  db:   ba 7b 00 00 00          mov    $0x7b,%edx
  e0:   8e da                   movl   %edx,%ds
  e2:   8e c2                   movl   %edx,%es
  e4:   bd 00 f0 ff ff          mov    $0xfffff000,%ebp
  e9:   21 e5                   and    %esp,%ebp
  eb:   f7 44 24 30 00 01 00    testl  $0x100,0x30(%esp)
  f2:   00   
  f3:   74 04                   je     f9 <no_singlestep>
  f5:   83 4d 08 10             orl    $0x10,0x8(%ebp)

000000f9 <no_singlestep>:
  f9:   66 f7 45 08 c1 01       testw  $0x1c1,0x8(%ebp)
  ff:   0f 85 bf 00 00 00       jne    1c4 <syscall_trace_entry>
105:   3d 3e 01 00 00          cmp    $0x13e,%eax
10a:   0f 83 27 01 00 00       jae    237 <syscall_badsys>

00000110 <syscall_call>:
110:   ff 14 85 00 00 00 00    call   *0x0(,%eax,4)
117:   89 44 24 18             mov    %eax,0x18(%esp)
通过以上反汇编代码的倒数第二行可以看到,该行即执行call *sys_call_table(,%eax,4) 的代码。那么该执行相对于函数体的偏移为0x110-0xd0 = 0x40。我们实际的代码中使用偏移值100作为搜索的最大范围。
至于反汇编出来为什么只是call *0x0(,%eax,4),个人理解应该是该模块尚未与其他模块进行链接的原因。当生成内核镜像vmlinux之后,反汇编vmlinux,然后找到system_call函数,就可以看到指令call *0x0(,%eax,4)中0x0被替换为有效的地址。本人也已经在2.6.18.3的vmlinux上验证过了,实际的代码如下:
  1. c1003d04 <syscall_call>:
  2. c1003d04:   ff 14 85 e0 14 1f c1    call   *0xc11f14e0(,%eax,4)
  3. c1003d0b:   89 44 24 18             mov    %eax,0x18(%esp)
复制代码
因此可以看出,我当前系统的sys_call_table的地址为0xc11f14e0。

(五)系统调用的替换
一旦我们获取到了系统调用表的地址,需要需要替换那些系统调用,只需要将系统调用表的某个系统调用指向自己实现的系统调用即可。
  1. #define REPLACE(x) old_##x = my_table[__NR_##x];\
  2.     my_table[__NR_##x] = new_##x
  3.     REPLACE(open);
复制代码
另外,需要注意的是,在替换系统调用的时候,要先清CR0的第20位并记录原始值,不然在替换sys_call_table的时候会报错。在替换完毕之后,再将CR0的原始值恢复,代码如下:
  1. orig_cr0 = clear_and_return_cr0();   
  2. setback_cr0(orig_cr0);
复制代码
以上为Linux劫持系统调用的总结。欢迎多多交流,如果不妥之处,请大家指正。


参考链接:
1. http://linux.chinaunix.net/bbs/viewthread.php?tid=909712
3. http://linux.chinaunix.net/bbs/thread-1135859-1-2.html

[ 本帖最后由 Godbach 于 2009-12-2 13:43 编辑 ]
作者: Godbach    时间: 2009-12-02 13:27
标题: 回复 #2 scutan 的帖子
scutan兄,你回复的真快啊。
作者: scutan    时间: 2009-12-02 13:31
标题: 回复 #3 Godbach 的帖子
呵呵,为了不影响文章的连贯性,所以我将之前那个回贴删了。
学习学习。
作者: Godbach    时间: 2009-12-02 13:34
标题: 回复 #4 scutan 的帖子
呵呵,我应该在1楼注上未完待续:wink:
作者: Godbach    时间: 2009-12-02 16:44
1楼中添加了全文的目录结构。
其实第一部分就是使用albcamus兄的代码。
重点内容在于第二部分,分析了劫持系统调用的整个过程。
作者: GoldenSoldier    时间: 2009-12-02 20:07
标题: 回复 #1 Godbach 的帖子
代码没有改动吧?

学习了,分析的很好啊,赞一下!
很好,很强大
作者: godbach    时间: 2009-12-02 21:54
标题: 回复 #7 GoldenSoldier 的帖子
代码没有改变。

我就是拿了ablcamus兄的代码,只保留的open系统调用的劫持。

如果想看完成的代码,可以打开参考连接1.
作者: mik    时间: 2009-12-02 22:24
好文,棒下场。godbach 继续发表些有益的文章
作者: Godbach    时间: 2009-12-02 22:25
标题: 回复 #9 mik 的帖子
呵呵,惭愧。还请mik兄多多指教。这样的文章在mik面前,有点班门弄斧了。
作者: mik    时间: 2009-12-02 22:36
标题: 回复 #10 Godbach 的帖子
godbach 太谦了,我是进来学习的
作者: platinum    时间: 2009-12-03 10:07
之前这部分知识很零散,通过 Godbach 这么一串,理解了很多
作者: Godbach    时间: 2009-12-03 10:29
标题: 回复 #12 platinum 的帖子
我也是通过总结第二部分,把这个流程梳理清楚了
作者: almeydifer    时间: 2009-12-03 10:32
经典,收藏了。
这样的大作,实在是一种享受。
作者: dreamice    时间: 2009-12-03 15:44
这贴很有技术含量
作者: Godbach    时间: 2009-12-03 15:57
标题: 回复 #15 dreamice 的帖子
dreamice兄过奖了。。。
作者: piaobo79    时间: 2009-12-03 16:47
Godbach的文章都很有技术内涵,顶你。
作者: vupiggy    时间: 2009-12-05 00:15
本帖最后由 Godbach 于 2010-08-30 11:45 编辑

unsigned long
get_syscall_table_x86_64(void)
{
#define OFFSET_SYSCALL 256

    unsigned long     syscall_entry;
    char         sc_asm[OFFSET_SYSCALL];
    char        *p;

#ifndef CONFIG_XEN
    syscall_entry = get_syscall_from_hw(ARCH_X86_64);
#else
    if (!is_running_on_xen())
        syscall_entry = get_syscall_from_hw(ARCH_X86_64);
    else if ((is_initial_xendomain()))
        syscall_entry = get_syscall_from_xen(ARCH_X86_64);
    else
        syscall_entry = get_syscall_from_kallsyms(ARCH_X86_64);
#endif

    if (unlikely(syscall_entry == 0)) {
        printk("[%s] Could not find system_call entry\n",
               __FUNCTION__);
        return -ENOSYS;
    }
   
    memcpy(sc_asm, (void*)syscall_entry, OFFSET_SYSCALL);

    p = (char *)memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\xc5", 3);
    if (unlikely(!p)) {
        printk("[%s] Could not to find system calls table\n",
               __FUNCTION__);
        return -ENOSYS;
    }
   
    return *(unsigned long*)(p + 3);

#undef OFFSET_SYSCALL
}

作者: vupiggy    时间: 2009-12-05 00:16
本帖最后由 Godbach 于 2010-08-30 11:45 编辑

unsigned long
get_syscall_from_hw(arch_t arch)
{
    unsigned long    syscall_entry = 0;
    struct idtr    idtr;
    struct idt    idt;

    switch(arch) {
    case ARCH_X86_64:
        rdmsrl(MSR_LSTAR, syscall_entry);
        break;
    case ARCH_X86:
        asm("sidt %0"
            :"=m"(idtr)
            );
        memcpy(&idt, (char *) idtr.base + 16 * 0x80, sizeof(idt));
        syscall_entry =
            (((unsigned long)idt.offset_high ) << 32) |
            (((idt.offset_middle << 16) |
              idt.offset_low) &
             0x00000000ffffffff );
        break;
    }
    return syscall_entry;
}

作者: vupiggy    时间: 2009-12-05 00:17
本帖最后由 Godbach 于 2010-08-30 11:45 编辑

unsigned long
get_syscall_from_xen(arch_t arch)
{
    unsigned long        syscall_entry = 0;
    xen_domctl_t        dmctl;
    vcpu_guest_context_t    ctxt;

    dmctl.cmd        = XEN_DOMCTL_getvcpucontext;
    dmctl.domain        = 0;
    dmctl.interface_version = XEN_DOMCTL_INTERFACE_VERSION;

    dmctl.u.vcpucontext.vcpu   = 0;
    dmctl.u.vcpucontext.ctxt.p = &ctxt;

    if (unlikely(HYPERVISOR_domctl(&dmctl) != 0)) {
        printk("[%s] hypercall failed\n", __FUNCTION__);
        return 0;
    }

    switch(arch) {
    case ARCH_X86_64:
        syscall_entry = ctxt.syscall_callback_eip;
        break;
    case ARCH_X86:
        syscall_entry = ctxt.trap_ctxt[0x80].address;
        break;
    }
   
    return syscall_entry;
}

作者: vupiggy    时间: 2009-12-05 00:18
本帖最后由 Godbach 于 2010-08-30 11:45 编辑

unsigned long
get_syscall_from_kallsyms(arch_t arch)
{
    unsigned long          syscall_entry;
    struct nameidata     nd;
    const  char        *path_str = "/proc/kallsyms";
    const  char        *symbol_str;
    struct vfsmount        *old_mnt;
    struct dentry        *old_dentry;
    int             err = 0;
    void            *p;
    struct seq_file        *m;
    struct kallsym_iter    *iter;
    struct file        *kallsyms_file;
    loff_t             index = 0;
    void            *old_buf;
            
    switch(arch) {
    case ARCH_X86_64:
        symbol_str = "system_call";
        break;
    case ARCH_X86:
        symbol_str = "ia32_syscall";
        break;
    default:
        return 0;
    }
   
    old_mnt         = current->fs->rootmnt;
    old_dentry   = current->fs->root;
   
    nd.mnt         = mntget(current->fs->rootmnt);
    nd.dentry    = dget(current->fs->root);
    nd.last_type = LAST_ROOT;
    nd.flags     = LOOKUP_FOLLOW | LOOKUP_NOALT;
    nd.depth     = 0;

    if ((err = path_walk(path_str, &nd)) == 0) {
        kallsyms_file = dentry_open(nd.dentry, nd.mnt, O_RDWR);
        if (kallsyms_file == NULL) {
            printk("[%s] can not open kallsyms file\n",
                   __FUNCTION__);
            syscall_entry = -EIO;
            goto out;
        }
        
        m = kallsyms_file->private_data;
        m->version = kallsyms_file->f_version;
            
        mutex_lock(&m->lock);
            
        old_buf = m->buf;

        m->buf = kmalloc(m->size = KSYM_SYMBOL_LEN, GFP_KERNEL);
        iter = m->private;
            
        while (1) {
            p = m->op->start(m, &index);
            err = PTR_ERR(p);
            if (!p || IS_ERR(p)) {
                syscall_entry = err;
                break;
            }
            m->count = 0;
            m->op->show(m, p);

            if (!strncmp(m->buf + sizeof("%s+%#lx/%#lx [%s]") + 1,
                     symbol_str,
                     strlen(symbol_str))) {
                m->buf[2 * sizeof(void *)] = 0;
                sscanf(m->buf, "%lx", &syscall_entry);
                break;
            }
            m->op->stop(m, p);
            index++;
        }
        
        m->op->stop(m, p);
        m->count = 0;
        m->size = 0;
        kfree(m->buf);
        m->buf = old_buf;
            
        mutex_unlock(&m->lock);
        filp_close(kallsyms_file, NULL);
    } else {
        printk("[%s] path_walk failed %d\n", __FUNCTION__, err);
    }

out:
    dput(old_dentry);
    mntput(old_mnt);

    return syscall_entry;
}

作者: vupiggy    时间: 2009-12-05 00:19
本帖最后由 Godbach 于 2010-08-30 11:46 编辑

typedef enum {
    ARCH_X86 = 0,
    ARCH_X86_64,
} arch_t;

作者: vupiggy    时间: 2009-12-05 00:19
本帖最后由 Godbach 于 2010-08-30 11:46 编辑

.macro LOAD_SYSCALL_ARGS
    movq 8(%rsp),  %r11
    movq 16(%rsp), %r10
    movq 24(%rsp), %r9
    movq 32(%rsp), %r8
    movq 56(%rsp), %rdx
    movq 64(%rsp), %rsi
    movq 72(%rsp), %rdi
    movq %r10,     %rcx
.endm

作者: vupiggy    时间: 2009-12-05 00:24
以上是64位系统调用劫持代码,支持 Xen dom0,如果是 domU,那就看运气了,目前流行的发行版缺省都采用了 kallsyms。kvm/vmware和物理机没区别。

如果在调用原始的系统调用例程之前做了很多事情会改变寄存器值,需要执行一下那一小段汇编代码。

P.S 这坨东西应该是 CU 首发,要转载请注明转载自 CU。

[ 本帖最后由 vupiggy 于 2009-12-4 17:27 编辑 ]
作者: accessory    时间: 2009-12-05 02:21
GOD 兄, 我来提下意见:
你的第一贴和第二贴似乎没啥关系啊. 第一贴用的是KPROBE. 它是内核已经做好的监测内核运行行为的工具. 可以监视任何内核函数.  第二贴讲的是传统的改SYSTEM CALL TABLE的方法. 而KPROBE用的不是这种方法. 关于KPROBE的解释可以看下面2个帖子:
http://www.ibm.com/developerworks/cn/linux/l-kprobes.html
http://www.maycode.com/index.php ... /1072-memcache.html
看你的帖子,似乎想说KPROBE就是用的改SYSTEM CALL TABLE的方法. 这点是不对的.

PS: 我只是大概看了下, 不对的地方多包涵.
作者: 大鬼不动    时间: 2009-12-05 09:45
学习了,希望好贴连篇!
作者: Godbach    时间: 2009-12-05 10:08
GOD 兄, 我来提下意见:
你的第一贴和第二贴似乎没啥关系啊. 第一贴用的是KPROBE. 它是内核已经做好的监测内核运行行为的工具. 可以监视任何内核函数.  第二贴讲的是传统的改SYSTEM CALL TABLE的方法. 而KPROBE用的不是这种方法. 关于KPROBE的解释可以看下面2个帖子:


多谢accessory兄提意见。

我第一帖最开始就提到时使用了albcamus兄提供的代码,他本身就是使用KPROBE的。但是我这里展示的代码,应经屏蔽了相关的代码。
你可以看到#if 0的条件宏的。我保留了传统的那种方式,也就是我2楼中分析的那个流程:从获取idtr到最后获取系统调用表,然后替换系统调用,实例中替换的就是open系统调用。
作者: marsaber    时间: 2009-12-05 10:15
强人!
作者: ubuntuer    时间: 2009-12-05 10:55
不错不错,Godbach出品必属精品!不过现在一般都不用劫持系统调用这一招了,因为rkhunter这些软件很容易发现是否被劫持了!
现在如adore-ng这些当红软件都是用的VFS那一套。
可以看看
rootkit学习笔记
Adore-ng-0.56源码分析
作者: saphires    时间: 2009-12-05 14:17
标题: 回复 #1 Godbach 的帖子
很好很强大。
不过,如果单是想跟踪一下系统调用的话,用systemtap是不是更简单便捷一些?
作者: hacktao    时间: 2009-12-05 15:21
lz 厉害!!
作者: Godbach    时间: 2009-12-05 15:49
标题: 回复 #29 ubuntuer 的帖子
多谢,有时间一定学习一下
作者: W.Z.T    时间: 2009-12-06 16:58
vupiggy 兄发的代码才是精华, 赞一个。
作者: W.Z.T    时间: 2009-12-06 16:59
截获系统调用的东西都是n久之前的技术了, 楼主加油,继续搞点inline hook的总结出来
作者: vupiggy    时间: 2009-12-08 09:40
原帖由 W.Z.T 于 2009-12-6 09:59 发表
截获系统调用的东西都是n久之前的技术了, 楼主加油,继续搞点inline hook的总结出来

看到你写的关于inline hook的文章, 忘了是米国哪个大学若干年前(32位时代)某门课的一份作业是劫持 page fault 处理就用的是同一思路. 那个老师给的参考代码和你文章里出现的一段代码一样, 我认为有问题, 至少不美.

p[1] = (offset & 0x000000ff);
p[2] = (offset & 0x0000ff00) >> 8;
p[3] = (offset & 0x00ff0000) >> 16;
p[4] = (offset & 0xff000000) >> 24;

建议改成 xchg, 好处嘛...

[ 本帖最后由 vupiggy 于 2009-12-8 02:42 编辑 ]
作者: sdzzzxj    时间: 2009-12-08 09:50
都是牛人,
作者: Godbach    时间: 2009-12-08 10:17
看到你写的关于inline hook的文章,

原来W.Z.T兄已经有inline hook的文章了,可否给出链接?
作者: W.Z.T    时间: 2009-12-08 13:02
原帖由 vupiggy 于 2009-12-8 09:40 发表

看到你写的关于inline hook的文章, 忘了是米国哪个大学若干年前(32位时代)某门课的一份作业是劫持 page fault 处理就用的是同一思路. 那个老师给的参考代码和你文章里出现的一段代码一样, 我认为有问题, 至少 ...


inline hook确实也是几年前的东西了, 最早出现在*inx上, 然后windows将其发扬光大。 大多数inline hook技术都用在win rk上, linux搞也没啥用。 至于怎么inline hook, 方法也很多 , 加jmp或push, ret指令就ok, linux有源码, 不必在跳转回来了, 重写一个函数就 ok。或者动态修改offset也可以。


BTW: linux rk不在于怎么hook, 只要稳定就好。 系统管理员不会去那么认真查的。
作者: W.Z.T    时间: 2009-12-08 13:03
原帖由 Godbach 于 2009-12-8 10:17 发表

原来W.Z.T兄已经有inline hook的文章了,可否给出链接?


www.debugman.com上有很多inline hook的例子, windows于linux都差不多的。
作者: W.Z.T    时间: 2009-12-08 13:06
原帖由 vupiggy 于 2009-12-8 09:40 发表

看到你写的关于inline hook的文章, 忘了是米国哪个大学若干年前(32位时代)某门课的一份作业是劫持 page fault 处理就用的是同一思路. 那个老师给的参考代码和你文章里出现的一段代码一样, 我认为有问题, 至少 ...


>>米国哪个大学若干年前(32位时代)某门课的一份作业

这个确实不知, 我只知道是从phrack杂志上出来的。兄弟不妨继续爆点料?
作者: Godbach    时间: 2009-12-08 13:15
至于怎么inline hook, 方法也很多 , 加jmp或push, ret指令就ok,


那就是WIN下同样需要找到函数的入口地址吧
作者: W.Z.T    时间: 2009-12-08 13:17
有些内核函数是导出的, 没导出的从/proc/kallsyms查就ok了。 怎么查vupiggy贴过代码了。
作者: W.Z.T    时间: 2009-12-08 13:24
原帖由 Godbach 于 2009-12-8 13:15 发表


那就是WIN下同样需要找到函数的入口地址吧


win下不是很了解, 可以通过解析pe结构也是可以的
作者: wohenry84    时间: 2009-12-08 20:18
这个下次看
作者: zhj1011    时间: 2009-12-24 15:41
标题: 回复 #1 Godbach 的帖子
哈哈 好文章啊
作者: Godbach    时间: 2010-01-19 11:26
修改了1楼中的一个小错误,CR0的WP bit是bit16,而不是bit20. 该位置位的时候是只读模式,清0之后有写权限。
参见intel的手册:
WP Write Protect (bit 16 of CR0) — When set, inhibits supervisor-level procedures
from writing into read-only pages; when clear, allows supervisor-level
procedures to write into read-only pages (regardless of the U/S bit setting;
see Section 4.1.3 and Section 4.6). This flag facilitates implementation of the
copy-on-write method of creating a new process (forking) used by operating
systems such as UNIX.

作者: W.Z.T    时间: 2010-01-19 12:51
原帖由 Godbach 于 2010-1-19 11:26 发表
修改了1楼中的一个小错误,CR0的WP bit是bit16,而不是bit20. 该位置位的时候是只读模式,清0之后有写权限。
参见intel的手册:


楼主的那个代码是从abl*版主那借鉴过来的, 原始的代码就有问题。 我也贴一个自己写的宏:


  1. #define CLEAR_CR0       asm ("pushl %eax\n\t"                   \
  2.                                 "movl %cr0, %eax\n\t"           \
  3.                                 "andl $0xfffeffff, %eax\n\t"    \
  4.                                 "movl %eax, %cr0\n\t"           \
  5.                                 "popl %eax");

  6. #define SET_CR0         asm ("pushl %eax\n\t"                   \
  7.                                 "movl %cr0, %eax\n\t"           \
  8.                                 "orl $0x00010000, %eax\n\t"     \
  9.                                 "movl %eax, %cr0\n\t"           \
  10.                                 "popl %eax");
复制代码

作者: Godbach    时间: 2010-01-19 12:54
标题: 回复 #47 W.Z.T 的帖子
呵呵,是啊。应该是albcamus兄的笔误吧。本来0xfffeffff就是bit16清0的。
作者: Godbach    时间: 2010-01-19 12:55
刚刚去WZT兄的博客上学习了。很佩服WZT兄做了那么多安全方面的研究。
作者: ubuntuer    时间: 2010-01-19 13:06
原帖由 W.Z.T 于 2010-1-19 12:51 发表


楼主的那个代码是从abl*版主那借鉴过来的, 原始的代码就有问题。 我也贴一个自己写的宏:


#define CLEAR_CR0       asm ("pushl %eax\n\t"                   \
                                "m ...

刚看了w.z.t  hi.baidu上的最新的一篇文章...深有感触!!!看的心里酸酸的
作者: Godbach    时间: 2010-01-19 13:13
原帖由 ubuntuer 于 2010-1-19 13:06 发表

刚看了w.z.t  hi.baidu上的最新的一篇文章...深有感触!!!看的心里酸酸的


是啊,你们都很强啊。
作者: ubuntuer    时间: 2010-01-19 13:20
原帖由 Godbach 于 2010-1-19 13:13 发表


是啊,你们都很强啊。

唉  他懂那么多东西 写过那么多东西  老大居然说他不懂网络
还好我没选择阿里巴巴
作者: Godbach    时间: 2010-01-19 13:26
原帖由 ubuntuer 于 2010-1-19 13:20 发表

唉  他懂那么多东西 写过那么多东西  老大居然说他不懂网络
还好我没选择阿里巴巴


呵呵,那么说估计是有前后语境的。

工作的时候,不时的会遇上一些不顺心的事,不管你在MS, Intel或者是小公司。看你怎么看待问题了。。
作者: T-Bagwell    时间: 2010-01-19 14:49
原帖由 ubuntuer 于 2010-1-19 13:20 发表

唉  他懂那么多东西 写过那么多东西  老大居然说他不懂网络
还好我没选择阿里巴巴


很正常,毕竟术业有专攻,更何况W.Z.T兄刚毕业不久
作者: vupiggy    时间: 2010-01-21 01:42
个人认为武断地清除和设置 cr0 的权限位也不合理,更合理的是,保存原来的 cr0,干完坏事之后再恢复回去。

多嘴说点题内话,用 kprobe 这类``官方''提供的方法来插入自己的逻辑很不美,受制于人。要做到真正意义的劫持, fork/vfork/clone/execve 这一系列的系统调用,唯一的方法是:RTFC,搞懂了 entry.S,解决方案就呼之欲出。

首先要明确,fork/vfork/clone/execve 这一系列系统调用比较特殊,系统调用表里的对应项不像其它系统调用那样是 C 函数地址,而是汇编代码的地址 (stub_XXXX),在这些 stub_XXXX 前头一段注释这么说:

/*
 * Certain special system calls that need to save a complete full stack frame.
 */


对 execve,看内核怎么玩的:

ENTRY(stub_execve)
    CFI_STARTPROC
    popq %r11
    CFI_ADJUST_CFA_OFFSET -8
    CFI_REGISTER rip, r11
    SAVE_REST
    FIXUP_TOP_OF_STACK %r11
    movq %rsp, %rcx
    call sys_execve
    RESTORE_TOP_OF_STACK %r11
    movq %rax,RAX(%rsp)
    RESTORE_REST
    jmp int_ret_from_sys_call
    CFI_ENDPROC
END(stub_execve)


如果像劫持其它系统调用那样把系统调用表对应 __NR_execve 的那项改成了自己实现的 C 函数地址,并且在该 C 函数内妄图调用原始的系统调用例程 (oops, 是 stub_execve),必死无疑,因为寄存器全乱了 (所以所谓的栈要调平衡一说并不准确,而是内核汇编代码将要放入栈的寄存器值被破坏了)。要么:
1. 整明白内核那段汇编是干什么的,要保存/恢复那些寄存器,自己照样做,多挂几次机,就想明白了 有些宏在 calling.h 中定义。要么:
2. 要是实在不想和底层机制玩命,有一个投机取巧的方法,看到那句可爱的:``call sys_execve'' 了没有,找到它 (它的 opcode 是 0xe9 .. .. .. .. ),把后面的操作数,也就是一个 C 函数, sys_execve 地址的偏移,换成自己实现的一个 asmlinkage 的 C 函数的偏移值即可,这样在自己的 C 函数里就可以在调用 sys_execve 之前之后为所欲为,因为 sys_execve 是 asmlinkage 的,编译器替我们保证参数正确。

要劫持 fork/vfork/clone 也只需要那么一点点技巧,看内核代码:
.macro PTREGSCALL label,func,arg
        .globl \label
\label:
        leaq        \func(%rip),%rax
        leaq    -ARGOFFSET+8(%rsp),\arg /* 8 for return address */
        jmp        ptregscall_common
END(\label)
        .endm
...
PTREGSCALL stub_clone, sys_clone, %r8
...
ENTRY(ptregscall_common)
        popq %r11
        CFI_ADJUST_CFA_OFFSET -8
        CFI_REGISTER rip, r11
        SAVE_REST
        movq %r11, %r15
        CFI_REGISTER rip, r15
        FIXUP_TOP_OF_STACK %r11
        call *%rax
        RESTORE_TOP_OF_STACK %r11
        movq %r15, %r11
        CFI_REGISTER rip, r11
        RESTORE_REST
        pushq %r11
        CFI_ADJUST_CFA_OFFSET 8
        CFI_REL_OFFSET rip, 0
        ret
        CFI_ENDPROC
END(ptregscall_common)

看看,又是 jmp 又是 call 的,应该清楚要做什么了。

注意:2.6.29开始,PTREGSCALL 宏的定义变了,于是 hack 的方式要跟着变。

[ 本帖最后由 vupiggy 于 2010-1-20 18:50 编辑 ]
作者: W.Z.T    时间: 2010-01-21 09:09
原帖由 vupiggy 于 2010-1-21 01:42 发表
个人认为武断地清除和设置 cr0 的权限位也不合理,更合理的是,保存原来的 cr0,干完坏事之后再恢复回去。

多嘴说点题内话,用 kprobe 这类``官方''提供的方法来插入自己的逻辑很不美,受制于人。要做到真正 ...


直接改写CR0确实不保险, 我们的做法是直接改sys_call_table地址的属性。


  1. int set_page_rwe(long unsigned int _addr)
  2. {
  3.         struct page *pg;
  4.         pgprot_t prot;

  5.         pg = virt_to_page(_addr);
  6.         prot.pgprot = VM_READ | VM_WRITE | VM_EXEC;

  7.         return change_page_attr(pg, 1, prot);
  8. }
复制代码


另外兄弟提到的直接改offset的方法, 我在《高级linux kernel inline hook技术》中提出过了。这个才是比较保险的hook, 呵呵
作者: Godbach    时间: 2010-01-21 10:13
个人认为武断地清除和设置 cr0 的权限位也不合理,更合理的是,保存原来的 cr0,干完坏事之后再恢复回去。

如果你保存了CR0的之后,在某些情况下又修改了CR0,再恢复的时候不就出现问题了
作者: liuhengloveyou    时间: 2010-07-08 11:07
好东东。。。 顶一个。
作者: plaieu    时间: 2010-07-08 17:00
提示: 作者被禁止或删除 内容自动屏蔽
作者: linux初学三月    时间: 2010-07-09 13:03
顶一下,谢谢了
作者: zonelight    时间: 2010-07-09 16:05
提示: 作者被禁止或删除 内容自动屏蔽
作者: zonelight    时间: 2010-07-09 17:14
提示: 作者被禁止或删除 内容自动屏蔽
作者: ubuntuer    时间: 2010-07-12 09:49
不错
作者: zonelight    时间: 2010-07-16 20:38
提示: 作者被禁止或删除 内容自动屏蔽
作者: linux初学三月    时间: 2010-07-16 20:58
谢谢了,要好好学习了
作者: zonelight    时间: 2010-07-16 23:43
提示: 作者被禁止或删除 内容自动屏蔽
作者: zonelight    时间: 2010-07-17 12:20
提示: 作者被禁止或删除 内容自动屏蔽
作者: _LoveLinux    时间: 2010-08-29 09:34
都是牛人啊 ~
自己还得加快学习的脚步!
作者: 卐伪装卐    时间: 2010-08-29 22:11
sysctl:
    使对任何ping请求不响应,重启后有效?怎么设的呀!请还记得的指教下,谢谢!!!

作者: qiangtoucao121    时间: 2011-03-11 10:03
这个确实要顶一下 好文啊 奇文
作者: safeqq2    时间: 2011-03-31 23:22
本帖最后由 safeqq2 于 2011-03-31 23:24 编辑

如果要劫持   netif_rx   和  dev_queue_xmit   函数呢     麻烦高手们给点建议
作者: Godbach    时间: 2011-04-01 13:26
基于什么需求要劫持这两个函数
作者: tanyayun    时间: 2011-05-25 10:04
怎么我运行会出现下面的错误呀?
[root@localhost xitongjiechi]# make -C /usr/src/linux M=`pwd` modules
make: Entering directory `/usr/src/linux-2.6.33.4'
  CC [M]  /usr/src/linux/xitongjiechi/hack_open.o
/usr/src/linux/xitongjiechi/hack_open.c:1: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:1: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:2: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:3: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:4: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:5: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:6: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:7: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:8: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:9: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:10: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:11: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:12: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:13: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:14: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:15: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:19: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:20: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:21: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:27: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:30: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:31: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:31: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:33: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:33: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:34: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:34: 错误:expected identifier or ‘(’ before ‘while’
/usr/src/linux/xitongjiechi/hack_open.c:35: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:35: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:45: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:48: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:54: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:55: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:66: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:70: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:78: 警告:空声明
/usr/src/linux/xitongjiechi/hack_open.c:79: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:89: 警告:空声明
/usr/src/linux/xitongjiechi/hack_open.c:90: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:95: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:102: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:107: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:109: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:138: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:140: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:168: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:181: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:220: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:231: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:280: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:282: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:300: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:337: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:349: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:349: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:349: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:349: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:349: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:355: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:359: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:362: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:365: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:398: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:405: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:405: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:405: 错误:程序中有游离的 ‘##’
/usr/src/linux/xitongjiechi/hack_open.c:410: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:413: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:417: 错误:程序中有游离的 ‘#’
/usr/src/linux/xitongjiechi/hack_open.c:419: 错误:expected identifier or ‘(’ before numeric constant
/usr/src/linux/xitongjiechi/hack_open.c:422: 错误:expected identifier or ‘(’ before numeric constant
make[1]: *** [/usr/src/linux/xitongjiechi/hack_open.o] 错误 1
make: *** [_module_/usr/src/linux/xitongjiechi] 错误 2
make: Leaving directory `/usr/src/linux-2.6.33.4'
[root@localhost xitongjiechi]#
作者: tanyayun    时间: 2011-05-25 10:10
明白了
作者: Godbach    时间: 2011-05-25 10:18
LS 明白神马了
作者: tanyayun    时间: 2011-05-25 10:36
出现了这个问题,怎么改呀?
[root@localhost xitongjiechi]# vim hack_open.c
[root@localhost xitongjiechi]# make -C /usr/src/linux M=`pwd` modules
make: Entering directory `/usr/src/linux-2.6.33.4'
  CC [M]  /usr/src/linux/xitongjiechi/hack_open.o
/usr/src/linux/xitongjiechi/hack_open.c: In function ‘new_execve’:
/usr/src/linux/xitongjiechi/hack_open.c:242: 错误:隐式声明函数 ‘getname’
/usr/src/linux/xitongjiechi/hack_open.c:242: 错误:‘struct pt_regs’ 没有名为 ‘ebx’ 的成员
/usr/src/linux/xitongjiechi/hack_open.c:242: 警告:赋值时将整数赋给指针,未作类型转换
/usr/src/linux/xitongjiechi/hack_open.c:248: 错误:‘struct pt_regs’ 没有名为 ‘ecx’ 的成员
/usr/src/linux/xitongjiechi/hack_open.c:249: 错误:‘struct pt_regs’ 没有名为 ‘edx’ 的成员
/usr/src/linux/xitongjiechi/hack_open.c:257: 错误:隐式声明函数 ‘putname’
make[1]: *** [/usr/src/linux/xitongjiechi/hack_open.o] 错误 1
make: *** [_module_/usr/src/linux/xitongjiechi] 错误 2
make: Leaving directory `/usr/src/linux-2.6.33.4'
作者: Godbach    时间: 2011-05-25 11:01
提示很明显啊,那个数据结构是内核的吗
作者: tanyayun    时间: 2011-05-25 11:02
回复 77# Godbach

是内核的吧
   61楼说, 自己写个struct加进去把原来的换调
可是,我不知道怎么写呀
作者: Godbach    时间: 2011-05-25 11:13
不同的内核版本,可能数据结构会发生变化,你要比较一下
作者: tanyayun    时间: 2011-05-25 15:55
回复 79# Godbach


    我怎么找pt_regs定义的位置呢?我在网上找的,说是在include/asm-i386/Ptrace.h 中.可是,我这没有asm-i386,只有asm-generic,有什么命令可以查找我电脑上pt_regs定义的位置吗?谢谢
作者: tanyayun    时间: 2011-05-25 16:55
回复 61# zonelight


    你好,我是在linux/arch/x86/include/asm/ptrace.h和linux/include/linux/ptrace.h中找到pt_regs结构体的定义,如下:

   struct pt_regs {  
       long ebx;     
       long ecx;     
       long edx;     
       long esi;     
       long edi;     
       long ebp;     
       long eax;     
       int  xds;     
       int  xes;     
       long orig_eax;
       long eip;     
       int  xcs;     
       long eflags;  
       long esp;     
       int  xss;     
   };
为什么编译hack_open.c时还会说没有名为“ebx”等成员呢?
作者: Godbach    时间: 2011-05-25 17:43
看一下完整的定义吧

#ifdef __i386__
/* this struct defines the way the registers are stored on the
   stack during a system call. */

#ifndef __KERNEL__

struct pt_regs {
        long ebx;
        long ecx;
        long edx;
        long esi;
        long edi;
        long ebp;
        long eax;
        int  xds;
        int  xes;
        int  xfs;
        int  xgs;
        long orig_eax;
        long eip;
        int  xcs;
        long eflags;
        long esp;
        int  xss;
};

#else /* __KERNEL__ */

struct pt_regs {
        unsigned long bx;
        unsigned long cx;
        unsigned long dx;
        unsigned long si;
        unsigned long di;
        unsigned long bp;
        unsigned long ax;
        unsigned long ds;
        unsigned long es;
        unsigned long fs;
        unsigned long gs;
        unsigned long orig_ax;
        unsigned long ip;
        unsigned long cs;
        unsigned long flags;
        unsigned long sp;
        unsigned long ss;
};

#endif /* __KERNEL__ */

作者: Godbach    时间: 2011-05-25 17:47
对比一下我实践通过的 2.6.18.3 的代码中和你当前源码的数据结构有什么不同吧
作者: tanyayun    时间: 2011-05-25 17:52
回复 83# Godbach


    对呀,我的源码的数据结构和你的一摸一样呀,好怪,为什么就说我的没有那几个成员呢
作者: tanyayun    时间: 2011-05-25 19:21
回复 83# Godbach


    我重新定义了三个变量取代了regs.ebx,regs.ecx,regs.edx,现在所有编译都成功了,呵呵,就是不知道我这种该法有没有其他隐患
作者: ww2000e    时间: 2011-05-25 23:26

作者: Godbach    时间: 2011-05-26 11:00
回复 85# tanyayun


   跑跑先。遇上 oops 了,正好也了解一下如何解决 oops 。
作者: cuibixiong    时间: 2011-05-27 15:53
强大,很好的学习了
作者: menubada    时间: 2011-06-10 16:06
【通过以上数据结构就可以得到系统调用中断发生时的中断处理例程的地址,即函数system_call()函数的地址: sys_call_off = idt.off2 << 16 | idt.off1】

请教,为什么计算system_call( )的地址时,没有使用到 16位的 sgement selector, 而是直接将off2作为存放system_call( )地址的代码段的基地址?
作者: wangzhen11aaa    时间: 2011-06-15 22:49
看过内核代码,看这些,心里有点底......
作者: 冷秋风扫落叶    时间: 2012-11-13 19:52
谢谢Godbach兄
作者: 孤行者_cuit    时间: 2012-11-19 21:43
赞,赞
作者: iyunny    时间: 2013-04-05 12:45
您好,我想获得系统运行时所有进程的所有系统调用,怎么实现?麻烦您给相关提示,或者相关资料,我在entry.S中找到系统调用那块,能劫持到系统调用号,但是不知道如何用汇编代码将内核中的信息打印到文件,希望您能给些帮助,不生感激~
回复 1# Godbach


   
作者: mousexqshe    时间: 2014-05-23 11:52
                楼主你好,学习完后,我自己实践了一下,想劫持read操作,但是出现了死循环,请问如何解决啊?修改后的代码如下
#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/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_AUTHOR("alert7 (alert7@xfocus.org) \n\t\talbcamus <albcamus@gmail.com>");
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 long (*old_write) (unsigned int fd, const char __user * buf, size_t count);
asmlinkage long (*old_read) (unsigned int fd, const char __user * buf, size_t count);
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 16 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);
}

asmlinkage long new_write(unsigned int fd, const char __user * buf, size_t count)
{
    dbgprint("call write()\n");
    return old_write (fd, buf, count);;
}

asmlinkage long new_read(unsigned int fd, const char __user * buf, size_t count)
{
    dbgprint("call read()\n");
    return old_read (fd, buf, count);;
}

/**
* 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,
                  &regs);
    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);
                        REPLACE(read);
#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);


错误截图:

Fedora Core 4-2014-05-23-11-51-40.png (14.11 KB, 下载次数: 43)

Fedora Core 4-2014-05-23-11-51-40.png

作者: wugj03    时间: 2015-01-13 10:42
我是在kernel 2.6.32上跑的,劫持了read系统调用出现了panic,其他系统调用都没有问题,求大神指点

1.bug重现:
#insmod prov.ko (涉及的模块)
...read some files...
#rmmod prov
#insmod prov.ko
切换到另一个tty终端上,系统就崩溃了。
PS:a.在第一次insmod时,点击另外一个tty终端,是不会出错的。
b. 去掉if(ret ==0){...}, 也是不会出现panic的
c. 不是每次都会出现,但基本都出现,否则就要多一次rmmod,insmod,才出现panic

附全部代码:
  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/kprobes.h>
  5. #include <linux/kallsyms.h>
  6. #include <linux/sched.h>
  7. #include <linux/ptrace.h>
  8. #include <linux/mm.h>
  9. #include <linux/fs.h>
  10. #include <linux/file.h>
  11. #include <linux/smp.h>
  12. #include <linux/user.h>
  13. #include <linux/errno.h>
  14. #include <linux/cpu.h>
  15. #include <asm/uaccess.h>
  16. #include <asm/fcntl.h>
  17. #include <asm/unistd.h>

  18. MODULE_DESCRIPTION("Intercept the system call table in Linux");
  19. MODULE_LICENSE("GPL");


  20. /* comment the following line to shut me up */
  21. #define INTERCEPT_DEBUG

  22. #ifdef INTERCEPT_DEBUG
  23.     #define dbgprint(format,args...) \
  24.         printk("intercept: function:%s-L%d: "format, __FUNCTION__, __LINE__, ##args);
  25. #else
  26.     #define dbgprint(format,args...)  do {} while(0);
  27. #endif


  28. /**
  29. * * the system call table
  30. * */
  31. void **my_table;

  32. unsigned int orig_cr0;

  33. /**
  34. * * the original syscall functions
  35. * */
  36. asmlinkage long (*old_read) (unsigned int fd, char __user *buf, size_t count);


  37. struct idtr {
  38.     unsigned short limit;
  39.     unsigned int base;
  40. } __attribute__ ((packed));


  41. struct idt {
  42.     unsigned short off1;
  43.     unsigned short sel;
  44.     unsigned char none, flags;
  45.     unsigned short off2;
  46. } __attribute__ ((packed));



  47. /**
  48. * * clear WP bit of CR0, and return the original value
  49. * */
  50. unsigned int clear_and_return_cr0(void)
  51. {
  52.     unsigned int cr0 = 0;
  53.     unsigned int ret;

  54.     asm volatile ("movl %%cr0, %%eax"
  55.               : "=a"(cr0)
  56.               );
  57.     ret = cr0;

  58.     /* clear the 20 bit of CR0, a.k.a WP bit */
  59.     cr0 &= 0xfffeffff;

  60.     asm volatile ("movl %%eax, %%cr0"
  61.               :
  62.               : "a"(cr0)
  63.               );
  64.     return ret;
  65. }

  66. /** set CR0 with new value
  67. * *
  68. * * @val : new value to set in cr0
  69. * */
  70. void setback_cr0(unsigned int val)
  71. {
  72.     asm volatile ("movl %%eax, %%cr0"
  73.               :
  74.               : "a"(val)
  75.               );
  76. }


  77. /**
  78. * * Return the first appearence of NEEDLE in HAYSTACK.  
  79. * * */
  80. static void *memmem(const void *haystack, size_t haystack_len,
  81.             const void *needle, size_t needle_len)
  82. {/*{{{*/
  83.     const char *begin;
  84.     const char *const last_possible
  85.         = (const char *) haystack + haystack_len - needle_len;

  86.     if (needle_len == 0)
  87.         /* The first occurrence of the empty string is deemed to occur at
  88. *            the beginning of the string.  */
  89.         return (void *) haystack;

  90.     /* Sanity check, otherwise the loop might search through the whole
  91. *        memory.  */
  92.     if (__builtin_expect(haystack_len < needle_len, 0))
  93.         return NULL;

  94.     for (begin = (const char *) haystack; begin <= last_possible;
  95.          ++begin)
  96.         if (begin[0] == ((const char *) needle)[0]
  97.             && !memcmp((const void *) &begin[1],
  98.                    (const void *) ((const char *) needle + 1),
  99.                    needle_len - 1))
  100.             return (void *) begin;

  101.     return NULL;
  102. }/*}}}*/


  103. /**
  104. * * Find the location of sys_call_table
  105. * */
  106. static unsigned long get_sys_call_table(void)
  107. {/*{{{*/
  108. /* we'll read first 100 bytes of int $0x80 */
  109. #define OFFSET_SYSCALL 100        

  110.     struct idtr idtr;
  111.     struct idt idt;
  112.     unsigned sys_call_off;
  113.     unsigned retval;
  114.     char sc_asm[OFFSET_SYSCALL], *p;

  115.     /* well, let's read IDTR */
  116.     asm("sidt %0":"=m"(idtr)
  117.              :
  118.              :"memory" );

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

  120.     /* Read in IDT for vector 0x80 (syscall) */
  121.     memcpy(&idt, (char *) idtr.base + 8 * 0x80, sizeof(idt));

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

  123.     dbgprint("idt80: flags=%X sel=%X off=%X\n",
  124.                  (unsigned) idt.flags, (unsigned) idt.sel, sys_call_off);

  125.     /* we have syscall routine address now, look for syscall table
  126. *        dispatch (indirect call) */
  127.     memcpy(sc_asm, (void *)sys_call_off, OFFSET_SYSCALL);

  128.     /**
  129. *      * Search opcode of `call sys_call_table(,eax,4)'
  130. *           */
  131.     p = (char *) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\x85", 3);
  132.     if (p == NULL)
  133.         return 0;

  134.     retval = *(unsigned *) (p + 3);
  135.     if (p) {
  136.         dbgprint("sys_call_table at 0x%x, call dispatch at 0x%x\n",
  137.              retval, (unsigned int) p);
  138.     }
  139.     return retval;
  140. #undef OFFSET_SYSCALL
  141. }/*}}}*/

  142. asmlinkage long new_read(unsigned int fd, char __user *buf, size_t count)
  143. {
  144.         long ret;
  145.         struct file *file;
  146.         ret = old_read(fd, buf, count);
  147.         /*0 represent EOF
  148.         * We just collect a 'read' when finished
  149.         */
  150.         if(ret == 0){
  151.                 file = fget(fd);
  152.                 if(file){
  153.                         fput(file);
  154.                 }
  155.         }
  156.         return ret;
  157. }

  158. static int intercept_init(void)
  159. {
  160.     my_table = (void **)get_sys_call_table();
  161.     if (my_table == NULL)
  162.         return -1;

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

  164. #define REPLACE(x) old_##x = my_table[__NR_##x];\
  165.     my_table[__NR_##x] = new_##x

  166.    
  167.         REPLACE(read);

  168. #undef REPLACE
  169.     return 0;
  170. }


  171. static int __init this_init(void)
  172. {
  173.     int ret;
  174.     printk("syscall intercept: Hi, poor linux!\n");

  175.     orig_cr0 = clear_and_return_cr0();   
  176.     ret = intercept_init();
  177.     setback_cr0(orig_cr0);

  178.     return ret;
  179. }

  180. static void __exit this_fini(void)
  181. {
  182.     printk("syscall intercept: bye, poor linux!\n");

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

  184.     orig_cr0 = clear_and_return_cr0();   
  185.         RESTORE(read);
  186.     setback_cr0(orig_cr0);

  187. #undef RESTORE
  188. }

  189. module_init(this_init);
  190. module_exit(this_fini);
复制代码

作者: Godbach    时间: 2015-01-13 10:47
回复 95# wugj03

你是要把所有系统调用统统尝试一遍么

   
作者: wugj03    时间: 2015-01-13 10:52
回复 96# Godbach

倒没有==、,其他几个系统调用就write,open, close,就是想观察这些系统调用,收集一些信息,就在read上卡住了

发了帖子,沉了    http://bbs.chinaunix.net/thread-4166052-1-1.html

有空帮我看看


   
作者: Godbach    时间: 2015-01-13 10:55
回复 97# wugj03

因为 read 无处不在,调用的最为频繁,所以坑也比较多。


   
作者: wugj03    时间: 2015-01-13 11:01
回复 98# Godbach


    是呀,anyway, thanks
作者: yzh07137    时间: 2015-04-06 13:33
我在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,还有一些警告,请问是什么原因啊,是因为内核版本的问题吗?




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2