Linux内核监控模块-3-系统调用的截获
上一章,我们获取了系统调用表的地址,这里我们来搞点所谓“截获”的事情。所谓“截获”即是将系统调用表里的地址指向我们自己写的一个函数,系统调用先执行我们自己写的函数,处理完后,再返回原来系统调用的执行函数。
还是先贴代码吧。
modu.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/unistd.h>
#include<linux/sched.h>
#include<linux/syscalls.h>
#include<linux/string.h>
#include<linux/fs.h>
#include<linux/fdtable.h>
#include<linux/uaccess.h> #include<linux/rtc.h> MODULE_LICENSE("Dual BSD/GPL"); #define _DEBUG
#ifdef _DEBUG
#define kprintk(fmt,args...) printk(KERN_ALERT fmt,##args)
#define kprintf(fmt,args...) printf(fmt,##args)
#define kperror(str) perror(str)
#else
#define kprintk
#define kprintf
#define kperror
#endif /*Function declaration*/
long * get_sys_call_table(void);
unsigned int close_cr(void);
void open_cr(unsigned int oldval);
void start_hook(void);
asmlinkage long (*orig_open)(char __user *filename, int flags, int mode); long * g_sys_call_table = NULL; //save address of sys_call_table
long g_old_sys_open = ; //save old address of sys_open
long g_oldcr0 = ; //save address of cr0 struct _idtr{
unsigned short limit;
unsigned int base;
}__attribute__((packed)); struct _idt_descriptor{
unsigned short offset_low;
unsigned short sel;
unsigned char none,flags;
unsigned short offset_high;
}__attribute__((packed)); unsigned int close_cr(void){
unsigned int cr0 = ;
unsigned int ret;
asm volatile("movl %%cr0,%%eax":"=a"(cr0));
ret = cr0;
cr0 &= 0xfffeffff;
asm volatile("movl %%eax,%%cr0"::"a"(cr0));
return ret;
} void open_cr(unsigned int oldval){
asm volatile("movl %%eax,%%cr0"::"a"(oldval));
} /*Get the address of sys_call_table*/
long * get_sys_call_table(void){ struct _idt_descriptor * idt;
struct _idtr idtr;
unsigned int sys_call_off;
int sys_call_table=;
unsigned char* p;
int i;
asm("sidt %0":"=m"(idtr));
kprintk(" address of idtr: 0x%x\n",(unsigned int)&idtr);
idt=(struct _idt_descriptor *)(idtr.base+*0x80);
sys_call_off=((unsigned int)(idt->offset_high<<)|(unsigned int)idt->offset_low);
kprintk(" address of idt 0x80: 0x%x\n",sys_call_off);
p=(unsigned char *)sys_call_off;
for(i=;i<;i++){
if(p[i]==0xff&&p[i+]==0x14&&p[i+]==0x85){
sys_call_table=*(int*)((int)p+i+);
kprintk(" address of sys_call_table: 0x%x\n",sys_call_table); return (long*)sys_call_table;
}
} return ;
} //My own sys_open
asmlinkage long my_sys_open(char * filename, int flags, int mode){
kprintk("The process is \"%s\"(pid is %i)\n",current->comm,current->pid);
kprintk("The file is being accessed is \"%s\"\n",filename);
return orig_open(filename,flags,mode);
} void start_hook(void){
g_sys_call_table = get_sys_call_table();
if(!g_sys_call_table){
kprintk("Get sys_call_table error!\n");
return;
}
if(g_sys_call_table[__NR_close] != (unsigned long)sys_close){
kprintk("Incorrect sys_call_table address!\n");
return;
} g_old_sys_open = g_sys_call_table[__NR_open];
orig_open = (long(*)(char *, int, int))g_sys_call_table[__NR_open]; g_oldcr0=close_cr();
g_sys_call_table[__NR_open] = my_sys_open;
open_cr(g_oldcr0);
} int monitor_init(void){
kprintk("Monitor init\n");
start_hook();
return ;
} void monitor_exit(void){
if(g_sys_call_table && g_old_sys_open){
g_oldcr0 = close_cr();
g_sys_call_table[__NR_open] = g_old_sys_open;
open_cr(g_oldcr0);
}
kprintk("Monitor exit\n");
} module_init(monitor_init);
module_exit(monitor_exit);
Makefile文件和上一节是一样的,这里就不贴了。同样按照上一节的方法,将modu.c编译,然后加载到内核中。
加载成功后,执行“dmesg”,看到的系统日志如图。
如果稍微晚一点执行“dmesg”,系统日志就可能被截获open调用打印的信息刷屏了,因为open系统调用实在是被调用的太多了。
可以执行“lsmod”,查看当前系统中含有的模块,可以找到我们刚刚加载的modu。
ok,接下来解释一下原理。
之前也有解释过,“截获”的过程即是:修改系统调用表中调用函数的地址,将其执行我们自己实现的函数,再在我们自己的函数中完成我们想做的事情后,在返回到原来的系统调用执行流程中。
代码里面的这个函数asmlinkage long my_sys_open(char * filename, int flags, int mode),就是我们自己实现的调用函数,注意这里的形参是参考系统原有的open调用函数的原型来了,必须是这个样子。每种系统调用的原型是什么样子,可以自行百度。
在my_sys_open()中,我们打印了当前是哪个进程在访问(进程名和进程号的信息),访问的是哪个文件(文件的绝对路径),打印完后跳转到原来的系统调用函数。
在模块初始化的过程中,执行start_hook()函数。在start_hook()函数中,先获得系统调用表的地址,将系统调用表中的原有地址保存下来,再将my_sys_open()函数的地址赋到系统调用表中。
注意修改系统调用表时,由于内核中的很多东西,比如这里的系统调用表sys_call_table是只读的,我们需要修改一下权限才能修改。由于控制寄存器CR0的第16位若置位,则表示禁止系统进程写那些只有只读权限的文件,所以我们在修改系统调用表sys_call_table之前先将CR0的第16位清零,在修改完后再恢复置位就好了。如代码里的close_cr()函数,即是将CR0第16位清零,open_cr()函数是将CR0第16位恢复。
最后在卸载modu模块的时候,将系统调用表的内容还原就OK了。
Linux内核监控模块-3-系统调用的截获的更多相关文章
- linux内核编程入门--系统调用监控文件访问
参考的资料: hello world https://www.cnblogs.com/bitor/p/9608725.html linux内核监控模块--系统调用的截获 https://www. ...
- 给Linux内核增加一个系统调用的方法(转)
作者:chenjieb520 给Linux内核增加一个系统调用的方法 为了更加好地调试linux内核,笔者的实验均在mini6410的arm板上运行的.这样做的原因,第一是因为本人是学嵌入式的, ...
- Linux内核监控模块-0-开篇简介
最近做了一个关于Linux内核编程的项目,主要实现的功能是对文件系统的监控. 我采用的方式是将监控的功能做成一个可加载模块(LKM)的形式,动态加载到Linux内核中.在这个内核层的“监控模块”中,我 ...
- Linux内核监控模块-2-系统调用表地址的获取(Linux内核版本3.13)
那么在Linux内核2.6之后,不能直接导出sys_call_table的地址后,我们要如何获得系统调用表的地址,从而实现系统调用的截获呢. 先贴上我实现好的代码,然后再来讲解吧. modu.c #i ...
- linux内核学习之四 系统调用
一 概念区分 提到linux系统调用,不得不区分几个比较容易混淆的概念: 系统调用:系统调用就是一种特殊的接口.通过这个接口,用户可以访问内核空间.系统调用规定了用户进程进入内核的具体位置. 应用程 ...
- linux内核分析——扒开系统调用的三层皮(下)
20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 实验 ...
- linux内核分析——扒开系统调用的三层皮(上)
20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 系统调用 ...
- linux内核分析——扒开系统调用的三层皮
万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验部分 选择2 ...
- Linux内核监控模块-1-驱动模块(LKM)开发(以一个简单的hello world程序为例)
在上面一篇中介绍到,监控模块要做成一个驱动模块(或者说是可加载模块,LKM),动态的加载到Linux内核中.那么这篇就简单的介绍一下怎样做一个这样的驱动模块. 以简单的hello world程序为 ...
- linux内核基础(系统调用,简明)
内核基础(系统调用) 在说系统调用之前.先来说说内核是怎么和我们交互的.或者说是怎么和我们产生交集的. 首先,内核是用来控制硬件的仅仅有内核才干直接控制硬件,所以说内核非常重要,假设内核被控制那么电脑 ...
随机推荐
- 使用JAXB来实现Java合xml之间的转换
使用jaxb操作Java与xml之间的转换非常简单,看个例子就明白了. //javaBean-->xml @Test public void test1() { try { JAXBContex ...
- 购买SSD固态硬盘须当心,你知道什么是SLC、 MLC、TLC闪存芯片颗粒吗?
固态硬盘凭借其存取速率超快等自身优势,被越来越多的电脑爱好者所青睐,并迅速普及到了广大用户的电脑中,因为固态硬盘与传统机械硬盘相比,确实在运行效率等方面有了质的提升,但是亦是美网络小编要提醒大家的是, ...
- 11.12 noip模拟试题
题目名称 加密 冒泡排序图 重建可执行文件名 encrypt bubble rebuild输入文件名 encrypt.in bubble.in rebuild.in输出文件名 encrypt.in b ...
- 苹果手机 iframe 无法滚动bug
原来在html5下,iframe 只有 src 属性scroling='no' 解决办法:在iframe外加一层第div,设置样式-webkit-overflow-scrolling:touch;ov ...
- 会话技术之Cookie 和 Session
为什么要使用会话技术 会话从字面上来说就是,就是两个人说话,也就是两个人交流,那么这里说的是计算机web端的交流,因为互联网是基于HTTP传输信息的,而http传输是无状态协议,缺少状态,意味着如果后 ...
- 【STL】string 常用函数
string类的构造函数: string(const char *s); //用c字符串s初始化 string(int n,char c); //用n个字符c初始化 此外,string类还支持默认构造 ...
- Linux同步时间命令ntpdate
转自:http://orgcent.com/linux-ntpdate/ 由于要同步Linux服务器的时间,为了保证时间的高度准确性,放弃date命令而转向ntpdate(同步时间命令). 方案如下: ...
- iOS消息推送机制
iOS消息推送的工作机制可以简单的用下图来概括: Provider是指某个iPhone软件的Push服务器,APNS是Apple Push Notification Service的缩写,是苹果的服务 ...
- wamp使用方法【总】
Apache与php配置:我们把php-5.2.9-Win32.zip解压到C盘根目录下,把文件夹名字改成PHP,这样方便一下. 1. 找到PHP目录下的“php.ini-dist”或者“php.in ...
- libctemplate——C语言模块引擎简介及使用
前言 由先声明此libctemplate不是Google那个ctemplate.这个库是用C语言实现的,只有一个实现文件和一个头文件.Gooogl的ctemplate是C++实现的,和线程还扯上了关系 ...