直接上代码吧:
hello.c:
#include <linux/kernel.h> /*Needed by all modules*/
#include <linux/module.h> /*Needed for KERN_* */
#include <linux/init.h> /* Needed for the macros */
#include <linux/syscalls.h> //包含系统调用的相关定义和声明
//若要打印相关目录,则需要包含以下的头文件
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/dcache.h>
#include <linux/path.h>
//内核开发者容不得闭源,所以要想利用内核编程,必须包含这个宏定义
MODULE_LICENSE("GPL");
//内核为了安全,虽然内核符号表导出了sys_call_table,但是不能直接使用
//否则,加载模块会报“未定义的符号”错误信息,所以,我们直接使用
//sys_call_table的内核地址,用cat /proc/kallsyms | grep sys_call_table
//命令可以找到他的内核地址:c165e140 R sys_call_table
//这里还有一个注意点,就是R表明其是只读的,所以我们要想替换系统调用(就必须
//写入我们自己的函数以替换内核系统调用,这里需要写的权限),后面我们将看到,
//我们在内核态改变了cr0寄存器的WP标志位,令其为0从而允许写操作。
#define SYS_TAB 0xc165e140
//指向系统调用数组
unsigned long* sys_call_table = (unsigned long*)SYS_TAB;
int (*orig_mkdir)(void);
unsigned int clear_and_return_cr0(void);
void setback_cr0(unsigned int val);
int orig_cr0;
asmlinkage int my_mkdir(const char *path)
{
//获取当前进程的id
int pid = current->pid;
//获取当前进程的父进程id
int ppid = current->real_parent->real_parent->pid;
//获取当前进程的根目录
const char *ppwd = (current->fs->root).dentry->d_name.name;
struct path pwd;
//获取当前目录
get_fs_pwd(current->fs,&pwd);
printk(KERN_WARNING "Warnning Someone(PID=%d,parent=%d) attempts to mkdir!\n",pid,ppid);
printk(KERN_WARNING "ROOT:%s!\n",ppwd);
printk(KERN_WARNING "PWD:%s!\n",pwd.dentry->d_name.name);
return 0;
}
int hello_init(void)
{
printk(KERN_WARNING "Hello kernel, it's %d!\n",2014);
//清cr0的WP位
orig_cr0 = clear_and_return_cr0();
//保存原系统调用
orig_mkdir=(int (*)(void))sys_call_table[__NR_mkdir];
//等价sys_call_table[__NR_mkdir]=(unsigned long)&my_mkdir;
//替换成我们自己的函数
sys_call_table[__NR_mkdir]=(unsigned long)my_mkdir;
//为了内核安全,恢复cr0的WP位
setback_cr0(orig_cr0);
return 0;
}
void hello_exit(void)
{
printk("Bye, kernel!");
orig_cr0 = clear_and_return_cr0();
//恢复原系统调用
sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;
setback_cr0(orig_cr0);
}
unsigned int clear_and_return_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret;
asm volatile ("movl %%cr0, %%eax": "=a"(cr0));
ret = cr0;
/*clear the 20th bit of 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));
}
/* main module function*/
module_init(hello_init);
module_exit(hello_exit);
Makefile文件:
obj-m := hello.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
把hello.c文件和Makefile文件放在同一个目录之下,执行make命令,即可得到hello.ko,以root权限执行insmod ./hello.ko。然后用lsmod | grep hello命令可看到此模块已经加载了。tail /var/log/kern.log命令可以查看代码中的printk输出。好了,我们执行mkdir adf命令想新建目录,但是相应的目录不会被创建,而且kern.log中会有相应的输出。