您的位置:首页 > 其它

模块替换方式实现添加系统调用

2012-04-10 10:24 387 查看
转一篇很经典的文章 ,通常模块替换方法有两类:

第一类是修改源码,重新编译内核,网上有很多,这里给出其中一个链接:http://zakir.is-programmer.com/posts/22294.html

第二类则是通过模块的方式,网上也有一些,但是在一些linux的内核中

cat /proc/kallsyms |grep sys_call_table

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寄存器

另外源码的下载地址:http://download.csdn.net/detail/lucien_cc/4213052
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐