高级Linux Kernel Inline Hook技术分析与实现


  一、简述

  目前流行和成熟的Linux kernel inlinehook技术就是修改内核函数的opcode,通过写入jmp或pushret等指令跳转到新的内核函数中,从而达到修改或过滤的功能。这些技术的共同点就是都会覆盖原有的指令,这样很容易在函数中通过查找jmp,pushret等指令来查出来,因此这种inlinehook方式不够隐蔽。本文将使用一种高级inlinehook技术来实现更隐蔽的inlinehook技术。

  二、更改offset实现跳转

  如何不给函数添加或覆盖新指令,就能跳转到我们新的内核函数中去呢?我们知道实现一个系统调用的函数中不可能把所有功能都在这个函数中全部实现,它必定要调用它的下层函数。如果这个下层函数也可以得到我们想要的过滤信息等内容的话,就可以把下层函数在上层函数中的offset替换成我们新的函数的offset,这样上层函数调用下层函数时,就会跳到我们新的函数中,在新的函数中做过滤和劫持内容的工作。原理是这样的,具体来分析它该怎么实现,我们去看看sys_read的具体实现:

  linux-2.6.18/fs/read_write.c
  asmlinkage ssize_t sys_read(unsigned int fd, char __user *buf,size_t count)
  struct file *file;ssize_t ret = -EBADF;int fput_needed;
  file = fget_light(fd, &fput_needed);if (file) loff_t pos=file_pos_read(file);ret = vfs_read(file, buf,count,&pos);file_pos_write(file,pos);fput_light(file,fput_needed);
  return ret;
  EXPORT_SYMBOL_GPL(sys_read);

  我们看到sys_read最终是要调用下层函数vfs_read来完成读取数据的操作,所以我们不需要给sys_read添加或覆盖指令,而是要更改vfs_read在sys_read代码中的offset就可以跳转到我们新的new_vfs_read中去。如何修改vfs_read的offset呢?先反汇编下sys_read看看:

  [root@xsec linux-2.6.18]# gdb -q vmlinux
  Using host libthread_db library "/lib/libthread_db.so.1".
  (gdb) disass sys_read
  Dump of assembler code for n sys_read:
  0xc106dc5a : push %ebp
  0xc106dc5b : mov %esp,%ebp
  0xc106dc5d : push %esi
  0xc106dc5e : mov $0xfffffff7,%esi
  0xc106dc63 : push %ebx
  0xc106dc64 : sub $0xc,%esp
  0xc106dc67 : mov 0x8(%ebp),%eax
  0xc106dc6a : lea 0xfffffff4(%ebp),%edx
  0xc106dc6d : call 0xc106e16c
  0xc106dc72 : test %eax,%eax
  0xc106dc74 : mov %eax,%ebx
  0xc106dc76 : je 0xc106dcb1
  0xc106dc78 : mov 0x24(%ebx),%edx
  0xc106dc7b : mov 0x20(%eax),%eax
  0xc106dc7e : mov 0x10(%ebp),%ecx
  0xc106dc81 : mov %edx,0xfffffff0(%ebp)
  0xc106dc84 : mov 0xc(%ebp),%edx
  0xc106dc87 : mov %eax,0xffffffec(%ebp)
  0xc106dc8a : lea 0xffffffec(%ebp),%eax
  0xc106dc8d : push %eax
  0xc106dc8e : mov %ebx,%eax
  0xc106dc90 : call 0xc106d75c
  0xc106dc95 : mov 0xfffffff0(%ebp),%edx
  0xc106dc98 : mov %eax,%esi
  0xc106dc9a : mov 0xffffffec(%ebp),%eax
  0xc106dc9d : mov %edx,0x24(%ebx)
  0xc106dca0 : mov %eax,0x20(%ebx)
  0xc106dca3 : cmpl $0x0,0xfffffff4(%ebp)
  0xc106dca7 : pop %eax
  0xc106dca8 : je 0xc106dcb1
  0xc106dcaa : mov %ebx,%eax
  0xc106dcac : call 0xc106e107
  0xc106dcb1 : lea 0xfffffff8(%ebp),%esp
  0xc106dcb4 : mov %esi,%eax
  0xc106dcb6 : pop %ebx
  0xc106dcb7 : pop %esi
  0xc106dcb8 : pop %ebp
  0xc106dcb9 : ret
  End of assembler dump.
  (gdb)
  0xc106dc90 : call 0xc106d75c

  通过call指令来跳转到vfs_read中去。0xc106d75c是vfs_read的内存地址。所以只要把这个地址替换成我们的新函数地址,当sys_read执行这块的时候,就会跳转到我们的函数来了。

  • 1
  • 2
  • 3
  • 下一页

相关内容