[fw]拦截系统调用

今天在ubuntu中玩了下“拦截系统调用”,记录下自己对整个实现的理解。

原理

在linux kernel中,系统调用都放在一个叫做“sys_call_table”的分配表里面,在进入一个系统调用的最后一步,会调用与eax中包含的系统调用号对应的特定服务例程:

[cpp] view plaincopy

  1. call *sys_call_table(,%eax,4)

因为分派表中的每个表项占4个字节,因此首先把系统调用号乘以4,再加上sys_call_table分配表的起始地址,然后从从这个地址单元获取指向服务例程的指针,内核就找到了要调用的服务例程。我们只要修改对应的分配表项,即可实现系统调用的拦截。

获取sys_call_table的地址

网上介绍了很多种方法得到sys_call_table的地址,我使用了相对简单的一种方法——从内核导出的符号表中获取。

图中,十六进制数c15b3000即为sys_call_table的地址。同时,我们也得到了一个重要的信息,该符号对应的内存区域是只读的!

清除写保护

因为sys_call_table分配表的内存属性为只读,因此,我们要先清除对应地址的写保护。暂时使用了两种方法实现该目的:

第一种方法,修改cr0读写保护位:

[cpp] view plaincopy

  1. /* 清除写保护 */
  2. unsigned int clear_and_return_cr0(void)
  3. {
  4. unsigned int cr0 = 0;
  5. unsigned int ret;
  6. asm volatile ("movl %%cr0, %%eax"
  7. : "=a"(cr0)
  8. );
  9. ret = cr0;
  10. /* clear the 16 bit of CR0, a.k.a WP bit */
  11. cr0 &= 0xfffeffff;
  12. asm volatile ("movl %%eax, %%cr0"
  13. :
  14. : "a"(cr0)
  15. );
  16. return ret;
  17. }
  18. /* 设置cr0,--本程序用来恢复写保护 */
  19. void setback_cr0(unsigned int val)
  20. {
  21. asm volatile ("movl %%eax, %%cr0"
  22. :
  23. : "a"(val)
  24. );
  25. }

第二种方法,设置虚拟地址对应页表项的读写属性:

[cpp] view plaincopy

  1. /* make the page writable */
  2. int make_rw(unsigned long address)
  3. {
  4. unsigned int level;
  5. pte_t *pte = lookup_address(address, &level);
  6. if (pte->pte & ~_PAGE_RW)
  7. pte->pte |=  _PAGE_RW;
  8. return 0;
  9. }
  10. /* make the page write protected */
  11. int make_ro(unsigned long address)
  12. {
  13. unsigned int level;
  14. pte_t *pte = lookup_address(address, &level);
  15. pte->pte &= ~_PAGE_RW;
  16. return 0;
  17. }

附:完整代码

[cpp] view plaincopy

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>
  4. #include <linux/sched.h>
  5. #include <linux/init.h>
  6. #include <linux/fs.h>
  7. #include <linux/file.h>
  8. #include <linux/fs_struct.h>
  9. #include <linux/fdtable.h>
  10. #include <linux/string.h>
  11. #include <linux/mm.h>
  12. #include <linux/syscalls.h>
  13. #include <linux/list.h>
  14. #include <linux/jiffies.h>
  15. #include <linux/cdev.h>
  16. #include <asm/unistd.h>
  17. #include <asm/uaccess.h>
  18. #include <linux/path.h>
  19. #include <linux/time.h>
  20. #include <linux/stat.h>
  21. #include <net/sock.h>
  22. #include <net/inet_sock.h>
  23. #include <asm/cpufeature.h>
  24. /* grep sys_call_table  /boot/System.map-`uname -r` */
  25. unsigned long **sys_call_table = (unsigned long **)0xc15b3000;
  26. unsigned long *orig_mkdir = NULL;
  27. /* make the page writable */
  28. int make_rw(unsigned long address)
  29. {
  30. unsigned int level;
  31. pte_t *pte = lookup_address(address, &level);
  32. if (pte->pte & ~_PAGE_RW)
  33. pte->pte |=  _PAGE_RW;
  34. return 0;
  35. }
  36. /* make the page write protected */
  37. int make_ro(unsigned long address)
  38. {
  39. unsigned int level;
  40. pte_t *pte = lookup_address(address, &level);
  41. pte->pte &= ~_PAGE_RW;
  42. return 0;
  43. }
  44. asmlinkage long hacked_mkdir(const char __user *pathname, int mode)
  45. {
  46. printk("mkdir pathname: %s\n", pathname);
  47. printk(KERN_ALERT "mkdir do nothing!\n");
  48. return 0; /*everything is ok, but he new systemcall does nothing*/
  49. }
  50. static int syscall_init_module(void)
  51. {
  52. printk(KERN_ALERT "sys_call_table: 0x%lx\n", sys_call_table);
  53. orig_mkdir = (unsigned long *)(sys_call_table[__NR_mkdir]);
  54. printk(KERN_ALERT "orig_mkdir: 0x%lx\n", orig_mkdir);
  55. make_rw((unsigned long)sys_call_table);
  56. sys_call_table[__NR_mkdir] = (unsigned long *)hacked_mkdir;
  57. make_ro((unsigned long)sys_call_table);
  58. return 0;
  59. }
  60. static void syscall_cleanup_module(void)
  61. {
  62. printk(KERN_ALERT "Module syscall unloaded.\n");
  63. make_rw((unsigned long)sys_call_table);
  64. sys_call_table[__NR_mkdir] = (unsigned long *)orig_mkdir; /*set mkdir syscall to the origal one*/
  65. make_ro((unsigned long)sys_call_table);
  66. }
  67. module_init(syscall_init_module);
  68. module_exit(syscall_cleanup_module);
  69. MODULE_LICENSE("GPL");
  70. MODULE_DESCRIPTION("hack syscall");

参考:

1、《深入理解linux内核(第三版)》

2、Hijack Linux System Calls: Part III. System Call Table

[fw]拦截系统调用,布布扣,bubuko.com

时间: 2024-12-30 05:08:42

[fw]拦截系统调用的相关文章

通过系统调用,内核断点方法定位用户进程被内核踩内存的问题

请看我的上一篇博客,https://www.cnblogs.com/xingmuxin/p/11287935.html 介绍了具体的踩内存的问题.下面我来介绍下如何通过一些手段和方法,定位内核踩内存的问题. 1.系统调用拦截 系统调用拦截的目的其实就是把系统真正要执行的系统调用替换为我们自己写的内核函数,这里有一篇博客,对此作了介绍,https://blog.csdn.net/zhangyifei216/article/details/49872861 系统调用拦截的两个问题,一个是找到sys_

玩转ptrace (一)

转自http://www.cnblogs.com/catch/p/3476280.html [本文翻译自这里: http://www.linuxjournal.com/article/6100?page=0,0,作者:Pradeep Padaia] 你是否曾经想过怎样才能拦截系统调用?你是否曾经想过通过修改一下系统调用的参数来耍一把内核?你是否想过调试器是怎样把一个进程停下来,然后把控制权转移给你的?如果你以为这些都是通过复杂的内核编程来实现的,那你就错了,事实上,Linux 提供了一种很优雅的

线程(代码实现)详解

在计算机科学中,一个线程执行的是,可以独立地被一个管理编程指令的最小序列调度,这是通常的的一部分的操作系统.线程和所述的实施过程的操作系统之间的不同,但在大多数情况下,一个线程的过程的一个组成部分.多个线程可以在一个过程中存在,执行同时和共享的资源,例如存储器,而不同的过程不共享这些资源.特别是,一个进程的线程共享其可执行代码和它的变量在任何给定时间的值. 具有单处理器系统通常实现由多线程时间分片:所述中央处理单元(CPU)的不同之间切换的软件线程.这种上下文切换通常发生非常频繁,并迅速不够,用

Linux Rootkit Sample &amp;&amp; Rootkit Defenser Analysis

目录 1. 引言 2. LRK5 Rootkit 3. knark Rootkit 3. Suckit(super user control kit) 4. adore-ng 5. WNPS 6. Sample Rootkit for Linux 7. suterusu 8. Rootkit Defense Tools 9. Linux Rootkit Scanner: kjackal 1. 引言 This paper attempts to analyze the characteristic

注入式类的写法,解耦,组件式开发,沙箱

沙箱(网络编程虚拟执行环境) Sandboxie(又叫沙箱,沙盘)即是一个虚拟系统程序,允许你在沙盘环境中运行浏览器或者其他应用. 因此运行会产生的变化可以随后删掉. 它创造了一个类似沙盒的独立作业环境,在其中内部运行并不会对硬盘产生永久的影响. 其为一个独立的 虚拟环境,可用以测试不受信任的应用程序或上网行为. 沙箱是一种按照安全策略限制程序行为的执行环境. 早期主要用于测试可疑软件等,比如黑客们为了试用某种病毒或者不安全产品,往往可以将它们在沙箱环境中运行. 经典的沙箱系统的实现途经一般是通

[转载]playing with ptrace(Part I) 之一 --- 初识ptrace

本文转载自: http://blog.csdn.net/ixidof/article/details/7673568 ptrace --- 允许在用户层进行系统调用的拦截与修改: 你是否想知道系统调用是如何被拦截的? 你是否尝试过通过修改系统调用的参数来愚弄内核? 你是否考虑过调试器是如何暂停运行中的进程并将控制权交给你的? 无需复杂的内核编程,ptrace( Process trace )系统调用就可以帮你实现上面的功能. ptrace提供了一种机制,使得父进程能够观察并控制其他的进程,它可以

系统调用与模块编程

前言 转载请注明出处http://www.cnblogs.com/dvd0423/p/4183443.html 内核让人最爽的地方就是它给你站在山上看风景的感觉,一切尽收眼底.就像0号博文说的,不管它有没有用,知其所以然总是好的. 这个系列的内容围绕Linux内核展开,涉及的主要是我做KVM的过程中遇到的部分,网络.调度.KVM等等.虽然是底层的东西但是搞应用的人看一看也没有坏处.我们都知道内核太庞大,要想全了解几乎不可能,所以我们只能根据自己的需要去针对性的学习.而如此庞大的项目却能被组织的有

struts2综合例子--------拦截器(登陆检查,日志记录),校验validate,

列表Action package he.action; import he.dao.UserDAO; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; public class ListAction { private List<User> users = new LinkedList<User>(); public List<User> getUsers(

[fw]Linux系统使用time计算命令执行的时间

Linux系统使用time计算命令执行的时间 当测试一个程序或比较不同算法时,执行时间是非常重要的,一个好的算法应该是用时最短的.所有类UNIX系统都包含time命令,使用这个命令可以统计时间消耗.例如: [[email protected] ~]# time ls anaconda-ks.cfg install.log install.log.syslog satools text real 0m0.009s user 0m0.002s sys 0m0.007s 输出的信息分别显示了该命令所花