在网上搜了很多关于2.6内核系统调用劫持的文章,流传的最多的可能就是那个以mkdir系统调用为例子的文章,自己按照源码敲进去,写好Makefile后也编译通过了,但是加载时却出现了Segment fault的错误,后来调试了一下,发现sys_call_table找得不对,后面却用了返回的一个非法地址,结果出现访问错误。看来这个segment fault是由于非法访问内存引起的,添加了返回值判断后,再次加载模块,没有出现段错误,但是后来发现查找sys_call_table的结果为NULL。后来在网上找了一下,发现系统调用劫持时需要更改一下cr0的标志位(以前Windows系统中进行系统调用劫持时也是要操作这个寄存器)。修改了以后的模块代码如下:
/*
*
*
* Filename: syscallhook.c
*
* Description:
*
* Version: 1.0
* Created: 2009年10月02日 14时32分39秒
* Revision: none
* Compiler: gcc
*
* Author: YOUR NAME (),
* Company:
*
*
=======================================================================
==============
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/unistd.h>
MODULE_LICENSE("GPL");
struct {
unsigned short limit;
unsigned int base;
}__attribute__((packed)) idtr;
unsigned long *sys_call_table=NULL;
asmlinkage int (*orig_mkdir)(const char *,int);
unsigned int orig_cr0;
struct _idt
{
unsigned short offset_low;
unsigned short segment_sel;
unsigned char reserved, flags;
unsigned short offset_high;
}__attribute__((packed));
unsigned int clear_and_ret_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret = 0;
asm volatile ("movl %%cr0, %%eax"
:"=a"(cr0));
ret = cr0;
cr0 &= 0xfffeffff;
asm volatile ("movl %%eax, %%cr0"
:
:"a"(cr0));
return ret;
}
void setback_cr0(unsigned int val)
{
asm volatile("movl %%eax, %%cr0"
:
:"a"(val));
}
char* findoffset(char *start)
{
char *ptr = start;
int i = 0;
for(; i < 100; i++)
{
if(*(ptr+i) == '/xff'
&& *(ptr+ i + 1) == '/x14'
&& *(ptr+ i + 2) == '/x85')
{
return ptr + i;
}
}
return NULL;
}
unsigned long getscTable(void)
{
struct _idt *idt;
unsigned long system_call = 0, sct = 0;
unsigned short offset_low,offset_high;
char *p = NULL;
orig_cr0 = clear_and_ret_cr0(); //注意在这里设置一下cr0
/* get the interrupt descriptor table */
__asm__("sidt %0" : "=m" (idtr));
setback_cr0(orig_cr0);
/* get the address of system_call */
idt = (void *)(idtr.base + 8 * 0x80);
offset_low = idt->offset_low;
offset_high = idt->offset_high;
system_call = (offset_high<<16)|offset_low;
printk("idt:%x system_call:%x/n", idt, system_call);
p = findoffset((char*)system_call);
if(NULL != p)
{
sct = *(unsigned long*)(p+3);
}
return (unsigned long*)sct;
}
asmlinkage int hacked_mkdir(const char * pathname, int mode)
{
printk("PID %d called sys_mkdir, path: %s!/n", current->pid,
pathname);
return orig_mkdir(pathname,mode);
}
static int find_init(void)
{
sys_call_table = getscTable();
printk("sys_call_table find:0x%x/n", sys_call_table);
if(NULL != sys_call_table)
{
orig_mkdir=(int(*)(const char*,int))sys_call_table
[__NR_mkdir];
orig_cr0 = clear_and_ret_cr0();
sys_call_table[__NR_mkdir]=(unsigned long)hacked_mkdir;
setback_cr0(orig_cr0);
}
return 0;
}
void find_cleanup(void)
{
orig_cr0 = clear_and_ret_cr0();
if(sys_call_table)
{
sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;
}
setback_cr0(orig_cr0);
return;
}
module_init(find_init);
module_exit(find_cleanup);
Makefile如下:
obj-m := syscallhook.o
KERNELDIR := /home/done/linux-2.6.27.18/
PWD := $(shell pwd)
EXTRA_CFLAGS += -ggdb
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
ls | egrep -v "Makefile|syscallhook.c" | xargs $(RM) -rf
下面是在2.6.27.18内核下的测试图: