实现自己的系统调用(两种方式) 【转】

简介:

转自:http://blog.163.com/zhe_wang_2009/blog/static/1722821212012328104325531/

学习linux操作系统课,有一章讲“系统调用”,其中有个内容是在linux内核中添加一个自己的系统调用。
我调试了两次终于将其调通了,第一次不知道是什么原因,编译生成的内核总是加载不上。第二次成
功了。有点小成就感.实现自己的系统调用 - wanny - wanny   我现在将其过程记录下来。分享给大家。

方式一:编译内核的方式。


我本机的内核是linux-2.6.38.1添加的内核是linux-2.6.39.1
第一步:在arch/x86/include/asm/unistd_32.h文件中添加系统调用号。

 350 #define __NR_open_by_handle_at  342
351 #define __NR_clock_adjtime      343
352 #define __NR_syncfs             344
353 #define __NR_mysyscall          345  /*添加的部分*/
354 
355 #ifdef __KERNEL__
356 
357 #define NR_syscalls 346                 /*将系统调用总数重新更新*/

   
第二步:在系统调用表中添加相应的表项。位置:arch/x86/kernel/syscall_table_32.s

 344         .long sys_open_by_handle_at
345         .long sys_clock_adjtime
346         .long sys_syncfs
347         .long sys_mysyscall              /*345*/  /*添加部分*/ 

   
第三步:实现系统调用的服务历程。
理论上,这个函数在的位置没有固定,最好加在arch/x86/kernel/目录下的文件里面。
我这次是加在arch/x86/kernel/sys_i386_32.c文件中。
写了一个很简单的函数。

 27 asmlinkage long sys_mysyscall(long data)
28 {
29 return data;
30 }

第四步:重新编译内核。这可能要话很长的时间哦,最好是找个上课的时间,
或去吃饭的时间,写了小脚本。(以下都是root权限的操作阿)
首先,执行make menuconfig。使用默认配置,就是出现图形界面后,直接选择exit退出即可。
然后,执行如下一个小的脚本。

make
make modules
make modules_install
mkinitramfs -/boot/initrd-2.6.39.1.img 2.6.39.1
make install

编译完后,重启新的内核,发现鼠标用不了,估计是编译内核的时候没配置好。

第五步,在用户态测试是否成功。(注意内核安装好后,重启后选择新的内核)
我使用了C语言和汇编两种方式测试。

#include <linux/unistd.h>
#include <syscall.h>
#include <sys/types.h>
#include <stdio.h>

int main(void)
{
long id1 = 0;
id1 = syscall(345,190);
printf("%ld\n",id1);
return 0;
}

 

# hello.s
# display a string "Hello, world."

.section .rodata
msg:
.ascii "Hello, world.\n"

.section .text
.globl _start
_start:

movl $345,%eax
movl $15 ,%ebx
int $0x80

movl $4, %eax # system call
movl $1, %ebx # file descriptor
movl $msg, %ecx # string address
movl $14, %edx # string length
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80

注意汇编的时候要用gdb调试。用gdb调试汇编的方式见:
http://blog.163.com/zhe_wang_2009/blog/static/172282121201151175619458/

到这儿就结束了。这只是一个小的实验。这可以说算是迈出了一大步了。
以后可以在内核中加一些比较实用的系统调用了。
比如说:系统调用日志收集系统。用来监控系统调用的系统调用。
可以用来防止系统攻击



方式二:插入模块的方式。
方式二:http://hi.baidu.com/fan_ying_fei/blog/item/e8893d9259cf547055fb969c.html#lastcmt
通过插入模块的形式插入系统调用,免去了编译内核的这个比较费时的操作。

#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 my_syscall_num 223
//如下的这个值要到你机子上查。cat /proc/kallsyms | grep sys_call_table
#define sys_call_table_adress 0xc1511160


unsigned int clear_and_return_cr0(void);
void setback_cr0(unsigned int val);
asmlinkage long sys_mycall(void);

int orig_cr0;
unsigned long *sys_call_table = 0;
static int (*anything_saved)(void);

unsigned int clear_and_return_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret;
asm("movl %%cr0, %%eax":"=a"(cr0));
ret = cr0;
cr0 &= 0xfffeffff;
asm("movl %%eax, %%cr0"::"a"(cr0));
return ret;
}

void setback_cr0(unsigned int val) //读取val的值到eax寄存器,再将eax寄存器的值放入cr0中
{
asm volatile("movl %%eax, %%cr0"::"a"(val));
}

static int __init init_addsyscall(void)
{
printk("hello, kernel\n");
sys_call_table = (unsigned long *)sys_call_table_adress;//获取系统调用服务首地址
anything_saved = (int(*)(void)) (sys_call_table[my_syscall_num]);//保存原始系统调用的地址
orig_cr0 = clear_and_return_cr0();//设置cr0可更改

sys_call_table[my_syscall_num] = (unsigned long)&sys_mycall;//更改原始的系统调用服务地址 setback_cr0(orig_cr0);//设置为原始的只读cr0 return 0;}asmlinkage long sys_mycall(void){ printk("current->pid = %d , current->comm = %s\n", current->pid, current->comm); return current->pid;}static void __exit exit_addsyscall(void){ //设置cr0中对sys_call_table的更改权限。 orig_cr0 = clear_and_return_cr0();//设置cr0可更改 //恢复原有的中断向量表中的函数指针的值。 sys_call_table[my_syscall_num] = (unsigned long)anything_saved; //恢复原有的cr0的值 setback_cr0(orig_cr0); printk("call exit \n");}module_init(init_addsyscall);module_exit(exit_addsyscall);MODULE_LICENSE("GPL");





本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7060333.html,如需转载请自行联系原作者


相关文章
|
6月前
引入系统调用
引入系统调用
43 0
|
6月前
|
安全 Linux
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
|
6月前
|
缓存 Linux 编译器
C/C++ 函数调用以及Linux中系统调用 开销介绍:介绍C/C函数调用以及Linux中系统调用的开销情况
C/C++ 函数调用以及Linux中系统调用 开销介绍:介绍C/C函数调用以及Linux中系统调用的开销情况
85 0
|
6月前
|
算法 Unix Linux
进程原理及系统调用
进程原理及系统调用
|
6月前
|
安全 Java 程序员
“系统调用”究竟是不是个函数?
- **系统调用**和普通**函数**有何区别? - 什么是**内核态** 和 **用户态**? - 操作系统如何让CPU切换状态? - 内中断、外中断、软中断、硬中断是什么意思? - 库函数和系统调
|
算法 Linux 调度
进程原理及其系统调用(上)
进程原理及其系统调用
128 0
|
Unix Linux
进程原理及其系统调用(下)
进程原理及其系统调用
80 0
|
存储 API
驱动开发:内核文件读写系列函数
在应用层下的文件操作只需要调用微软应用层下的`API`函数及`C库`标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头即可在内核中使用,例如本章要讲解的文件与目录操作相关函数,多数ARK反内核工具都具有对文件的管理功能,实现对文件或目录的基本操作功能也是非常有必要的。
273 0
|
Linux C语言
Linux系统调用一、系统调用与C库函数的关系 —— 从进程虚拟地址空间和文件描述符的角度分析
Linux系统调用一、系统调用与C库函数的关系 —— 从进程虚拟地址空间和文件描述符的角度分析
213 0
Linux系统调用一、系统调用与C库函数的关系 —— 从进程虚拟地址空间和文件描述符的角度分析
其他系统调用
其他系统调用
82 0