/*以苏师姐给的程序说明*/
一、步骤概览:
编写系统调用服务例程——>添加系统调用号——>修改系统调用表——>重新编译内核并测试新添加的系统调用。
如果重新编译内核的话需要很长的时间,我们可以将系统调用服务例程作为模块加载进内核。当然,这样做的代价是复杂化了第二和第三步。
二、实现过程:
1、编写模块程序(包含前三步):
#include
#include
#include
#include
#define SYS_CALL_TABLE_ADDRESS 0xc0594150 //这个是系统调用表的地址,可通过
grep sys_call_table /proc/kallsyms
命令查看。
#define NUM 223 //宏定义系统调用号
int orig_cr0; //用于保存初始的CR0寄存器的值
unsigned long *sys_call_table_my = 0; //存放系统调用表起始地址
static int (*anything_saved)(void); //定义一个函数指针,用于保存223号系统调用在系统调用表中的内容
/*由于控制寄存器CR0的第16若置位,则表示禁止系统进程写只有只读权限的页面(sys_call_table),所以我们给sys_call_table添加内容的话就必须将CR0的第16位清零,在模块卸载的时候给还原*/
static int clear_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret;
asm volatile ("movl %%cr0, %%eax":"=a"(cr0)); //汇编代码,用于取出 CR0寄存器的值
ret = cr0;
cr0 &= 0xfffeffff;
asm volatile ("movl %%eax, %%cr0": :"a"(cr0));//汇编代码,将修改后的 CR0值写入CR0寄存器
return ret; //返回初始的CR0值
}
/*改回原CR0寄存器的值*/
static void setback_cr0(int val)
{
asm volatile ("movl %%eax, %%cr0": :"a"(val));
}
/*系统调用服务例程,打印当前进程的PID和进程名,返回当前进程的PID*/
asmlinkage long sys_mycall(void)
{
printk("I am mycall, current->pid: %d,and current->comm:%s\n", current->pid, current->comm);
return current->pid;
}
static int __init call_init(void)
{
sys_call_table_my = (unsigned long*)(SYS_CALL_TABLE_ADDRESS);
printk("call_init...\n");
anything_saved = (int (*)(void))(sys_call_table_my[NUM]);
orig_cr0 = clear_cr0();
sys_call_table_my[NUM] = (unsigned long) &sys_mycall;
setback_cr0(orig_cr0);
return 0;
}
static void __exit call_exit(void)
{
printk("call exit...\n");
orig_cr0 = clear_cr0();
sys_call_table_my[NUM] = (unsigned long)anything_saved;
setback_cr0(orig_cr0);
}
MODULE_LICENSE("GPL");
module_init(call_init);
module_exit(call_exit);
2、用户态下的验证程序:
#include
#include
int main()
{
unsigned long x = 0;
x = syscall(223); //用户态下的程序可以调用该函数直接访问系统调用
printf("Hello,%ld\n", x);
return 0;
}
阅读(3918) | 评论(1) | 转发(0) |