免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 59581 | 回复: 102

Linux下实现劫持系统调用的总结 [复制链接]

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 13:20 |显示全部楼层
原文链接: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 编辑 ]

评分

参与人数 3可用积分 +75 收起 理由
amarant + 15 精品文章
fender0107401 + 30 精品文章
scutan + 30 精品文章

查看全部评分

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 13:22 |显示全部楼层
二、实现原理分析
        实现的方法就是通过中断向量表,找到系统调用的中断向量,再通过系统调用时执行的指令,最终找到系统调用表的地址, 进行系统调用的替换。。

(一)中断向量表地址的获取
        中断向量表(IDT)的入口地址是通过IDTR寄存器来确定的。IDTR寄存器的内容如下图所示。
idtr.JPG
这就是上面代码中定义结构体
  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
idt.JPG


(三)系统调用处理例程地址的获取
获取到系统调用中断的地址后,同样需要一个数据结构,将中断向量描述符的相关内容保存。数据结构的定义如下:
  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 编辑 ]

评分

参与人数 2可用积分 +45 收起 理由
amarant + 15 凑齐十分
dreamice + 30 原创内容

查看全部评分

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 13:27 |显示全部楼层

回复 #2 scutan 的帖子

scutan兄,你回复的真快啊。

论坛徽章:
0
发表于 2009-12-02 13:31 |显示全部楼层

回复 #3 Godbach 的帖子

呵呵,为了不影响文章的连贯性,所以我将之前那个回贴删了。
学习学习。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 13:34 |显示全部楼层

回复 #4 scutan 的帖子

呵呵,我应该在1楼注上未完待续:wink:

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 16:44 |显示全部楼层
1楼中添加了全文的目录结构。
其实第一部分就是使用albcamus兄的代码。
重点内容在于第二部分,分析了劫持系统调用的整个过程。

论坛徽章:
0
发表于 2009-12-02 20:07 |显示全部楼层

回复 #1 Godbach 的帖子

代码没有改动吧?

学习了,分析的很好啊,赞一下!
很好,很强大

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 21:54 |显示全部楼层

回复 #7 GoldenSoldier 的帖子

代码没有改变。

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

如果想看完成的代码,可以打开参考连接1.

论坛徽章:
0
发表于 2009-12-02 22:24 |显示全部楼层
好文,棒下场。godbach 继续发表些有益的文章

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-12-02 22:25 |显示全部楼层

回复 #9 mik 的帖子

呵呵,惭愧。还请mik兄多多指教。这样的文章在mik面前,有点班门弄斧了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2021中国数据库技术大会

【数造未来】2021年10月18日-20日第十二届中国数据库技术大会
ITPUB、大会的会员您们好: 因目前国内疫情严峻,为响应北京市最新疫情防控要求,保障参会人员的健康和安全,组委会经协商决定:DTCC2021第十二届中国数据库技术大会延期至10月18日-20日(周一~周三)在北京国际会议中心举行,由此给各位带来的不便,敬请谅解!

大会官网
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP