转一篇很经典的文章 ,通常模块替换方法有两类:
第一类是修改源码,重新编译内核,网上有很多,这里给出其中一个链接:http://zakir.is-programmer.com/posts/22294.html
第二类则是通过模块的方式,网上也有一些,但是在一些linux的内核中
c0592150 R sys_call_table
因此sys_call_table在模块中又是不可修改的,这样要么重新编译内核,修改sys_call_table符号属性,但这显然不是我们想要的。
下面就有一篇另类的解决方法:
转自:http://hi.baidu.com/fan_ying_fei/blog/item/e8893d9259cf547055fb969c.html
首先查看系统没有使用的系统调用号,可以通过查看/arch/x86/include/asm/unistd_32.h进行查看 ,相对的代码如下:
230 #define__NR_fcntl64 221
231 /* 223 is unused*/
232 #define__NR_gettid 224
233 #define__NR_readahead 225
可以看出223本系统没有使用,因为该系统调用号没有使用,所以可以更改该系统调用的服务例程为添加的系统嗲用,不会影响系统原本的功能
具体替换的原理如下:
在模块加载的时候保存原本的的系统调用223服务例程的地址,再者更改该系统服务例程地址为你自己添加的系统函数
在模块卸载的时候将原本保存的223系统服务例程地址改回来。
在编写用户的测试程序,在加载该程序后进行测试该系统调用,若成功后,进行卸载,取消该系统调用
在该过程中要注意的问题,因为在编写成功进行编译时系统会不让使用sys_call_table的地址,说是unknowsymbol,可以通过查看/proc/kallsyms文件察看sys_call_table地址进行直接使用,又因为该地址是只读的所以必须通过更改控制寄存器cr0进行更改为可写权限,在需要跟该系统服务例程地址的时候,在更改完成后在改回原本的只读权限,cr0中含有控制处理器操作模式和状态的系统控制标志,其中的第16位,可控制读写
具体的实验代码如下:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/unistd.h>
#include<asm/uaccess.h>
#include<linux/sched.h>
#define susu 223
#definesys_call_table_adress 0xc1513160
unsigned intclear_and_return_cr0(void);
voidsetback_cr0(unsigned int val);
asmlinkage longsys_mycall(void);
int orig_cr0;
unsigned long*sys_call_table = 0;
static int(*anything_saved)(void);
unsigned intclear_and_return_cr0(void)//清cr0的第16位
{
unsigned int cr0 =0;
unsigned int ret;
asm volatile("movl%%cr0, %%eax"
: "=a"(cr0)
);
ret = cr0;
cr0 &=0xfffeffff;
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/unistd.h>
#include<asm/uaccess.h>
#include<linux/sched.h>
#define susu 223
#definesys_call_table_adress 0xc1513160
unsigned intclear_and_return_cr0(void);
voidsetback_cr0(unsigned int val);
asmlinkage longsys_mycall(void);
int orig_cr0;
unsigned long*sys_call_table = 0;
static int(*anything_saved)(void);
unsigned intclear_and_return_cr0(void)
{
unsigned int cr0 =0;
unsigned int ret;
asmvolatile("movl %%cr0,%%eax"
:"=a"(cr0)
);
ret = cr0;
cr0 &=0xfffeffff;
asm volatile("movl%%eax, %%cr0"//读取cr0的值到eax寄存器,再将eax寄存器的值放入cr0中
:
:"a"(cr0)
);
return ret;
}
voidsetback_cr0(unsigned int val)//读取val的值到eax寄存器,再将eax寄存器的值放入cr0中
{
asm volatile("movl%%eax, %%cr0"
:
:"a"(val)
);
}
static int __initinit_addsyscall(void)
{
printk("hello,kernel\n");
sys_call_table =(unsigned long *)sys_call_table_adress;//获取系统调用服务首地址
anything_saved =(int(*)(void)) (sys_call_table[susu]);//保存原始系统调用的地址
orig_cr0 =clear_and_return_cr0();//设置cr0可更改
sys_call_table[susu]= (unsigned long)&sys_mycall;//更改原始的系统调用服务地址
setback_cr0(orig_cr0);//设置为原始的只读cr0
return 0;
}
asmlinkage longsys_mycall(void)
{
printk("I ammycall.current->pid = %d , current->comm = %s\n",current->pid, current->comm);
return current->pid;
}
static void __exitexit_addsyscall(void)
{
orig_cr0 =clear_and_return_cr0();//设置cr0可更改
sys_call_table[susu]= (unsigned long)anything_saved;//恢复原始的系统调用服务地址
setback_cr0(orig_cr0);//设置为原始的只读cr0
printk("callexit \n");
}
MODULE_LICENSE("GPL");
module_init(init_addsyscall);
module_exit(exit_addsyscall);("movl%%eax, %%cr0"//读取cr0的值到eax寄存器,再将eax寄存器的值放入cr0中
:
:"a"(cr0)
);
return ret;
}
//改回cr0的第16位
voidsetback_cr0(unsigned int val)//读取val的值到eax寄存器,再将eax寄存器的值放入cr0中
{
asm volatile("movl%%eax, %%cr0"
:
:"a"(val)
);
}
static int __initinit_addsyscall(void)
{
printk("hello,kernel\n");
sys_call_table =(unsigned long *)sys_call_table_adress;//获取系统调用服务首地址
anything_saved =(int(*)(void)) (sys_call_table[susu]);//保存原始系统调用的地址
orig_cr0 =clear_and_return_cr0();//设置cr0可更改
sys_call_table[susu]= (unsigned long)&sys_mycall;//更改原始的系统调用服务地址
setback_cr0(orig_cr0);//设置为原始的只读cr0
return 0;
}
asmlinkage longsys_mycall(void)
{
printk("I ammycall.current->pid = %d , current->comm = %s\n",current->pid, current->comm);
return current->pid;
}
static void __exitexit_addsyscall(void)
{
orig_cr0 =clear_and_return_cr0();//设置cr0可更改
sys_call_table[susu]= (unsigned long)anything_saved;//恢复原始的系统调用服务地址
setback_cr0(orig_cr0);//设置为原始的只读cr0
printk("callexit \n");
}
MODULE_LICENSE("GPL");
module_init(init_addsyscall);
module_exit(exit_addsyscall);
简单的用户测试程序:
#include <stdio.h>
#include <stdlib.h>
int main()
{
unsigned long x;
x = syscall(223);
printf("hello%ld\n", x);
return 0;
}
程序加载后 运行用户测试程序 使用dmesg命令结果是
[10779.563481] hello,kernel
[10784.425524] I ammycall.current->pid = 4872 , current->comm = test
卸载后内核打印callexit
在该的代码中asm使用或许有的人会看不明白,是内联汇编其结构为
asm( assembler template // 汇编程序模板是一组插入到C程序中的汇编指令
:output operands (optional)//输出运算对象 可选
:input operands (optional)//输入运算对象 可选
:list of clobbered registers (optional)
);
asmvolatile("movl %%eax, %%cr0" //汇编指令将eax寄存器的值存入cr0寄存器
:
:"a"(cr0)输入操作对象无符号数据cr0,a代表输入到eax寄存器
);
若在第三行写为”b”则表示将输入操作对象无符号数据cr0,b代表输入到ebx寄存器
同理第三行写为”c、d”则表示将输入操作对象无符号数据cr0,分别代表输入到ecx、edx寄存器