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 = 0; //save old address of sys_open
long g_oldcr0 = 0; //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 = 0; 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=0; 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+8*0x80); sys_call_off=((unsigned int)(idt->offset_high<<16)|(unsigned int)idt->offset_low); kprintk(" address of idt 0x80: 0x%x\n",sys_call_off); p=(unsigned char *)sys_call_off; for(i=0;i<100;i++){ if(p[i]==0xff&&p[i+1]==0x14&&p[i+2]==0x85){ sys_call_table=*(int*)((int)p+i+3); kprintk(" address of sys_call_table: 0x%x\n",sys_call_table); return (long*)sys_call_table; } } return 0; } //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 0; } 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);
View Code

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了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM