学习笔记 --- LINUX 应用调试之添加系统调用
2014-04-04 12:27
831 查看
一、原理
要想自制系统调用,当然首相要做的就是明白系统调用的过程:
我们拿open函数来举个例子:当用户空间执行open函数时,会通过glibc函数库的作用最终去调用sys_open函数,sys_open函数最终又会调用我们具体注册的open函数!那么这里最主要的就是glibc函数库干了些什么呢?其实它的作用就是当用户空间执行open函数时,会去执行一条swi #val指令,这条指令会使cpu发生异常,并跳转到异常向量入口:vector_swi处去执行,之后的代码会根据引发异常的指令取出其中的参数,并根据这个参数调用对应的处理函数!sys_open、sys_read、sys_write这些函数是放在一个数组里面的,就是根据取出的这个val值为下标找到sys_open函数!
说的有点乱,我们来理一理:app调用open->swi #val->引发cpu异常->跳转到异常向量入口处->根据引发异常的指令调用对应的处理函数!
那么我们自制系统调用的话,需要实现两点:
1、写一个应用函数:swi #val
2、在内核里面仿sys_xxx写一个函数,放入数组!
前者用于引发异常,后者用于具体实现!
二、实现
1、内核函数
(1)在arch/arm/kernel/call.S文件里面的CALL()列表的最后添加一项,如:CALL(sys_hello)
这里是用来调用sys_hello
(2)在fs/read_write.c文件里加入如下代码:
(3)在include/linux/syscalls.h文件里加入如下代码:
asmlinkage void sys_hello(const char __user * buf, size_t count);
这是对函数sys_hello的声明
以上我们分别实现了函数的定义、声明和调用!这样内核里面的工作就完成了!
2、应用程序
in app, call hello
当我们用新内核启动的时候,打印信息如下:
in app, call hello
sys_hello:wangshuchang frank
成功了!
这里我们只是实现了一个系统调用,那么系统调用在应用调试里面如何应用?下面这种调试方法不需要太知道了,一般不用,太变态了。一般知道系统调用的原理就OK啦!
——————————————————————————————————————————————————————————————————--——————————
一、步骤
1、修改应用程序的可执行文件,替换某个位置的代码为swi val
2、执行程序
3、进入到sys_hello->在sys_hello里面打印信息->执行原来的指令->返回
二、具体实现
我们的应用程序是:
(1)编译:arm-linux-gcc test_sc.c -o test_sc
(2)反汇编:arm-linux-objdump -D test_sc > test_sc.dis
(3)我们打开上面得到的可执行文件和反汇编文件
比如我们想在C函数的i=i+2;处打断点的话,我们先在反汇编文件里面找到对应的指令:
84d4: e2833002 add r3, r3, #2 ; 0x2
其中:e2833002是机器码,这是我们所需要的!
我们去可执行文件里面去搜索这个机器码,在可执行文件里它对应的机器码应该是:02 30 83 e2
我们将此机器码改为swi指令的机器码!
我们可以将之前的文件反汇编一下,然后得到swi的机器码为:ef900160
(4)经过上面的修改,当程序执行到i=i+2;这条指令时,会产生系统调用,最终执行sys_hello函数。
在sys_hello函数里面,我们可以最一些必要的工作,具体程序如下:
(5)编译内核,用新内核启动
(6)运行测试程序(在此之前要修改测试程序的权限:chmod 777 test_sc_swi),输出信息如下:
Hello, cnt = 0, i = 0
sys_hello: cnt = 1
sys_hello: i = 0
Hello, cnt = 1, i = 2
sys_hello: cnt = 2
sys_hello: i = 2
Hello, cnt = 2, i = 4
sys_hello: cnt = 3
sys_hello: i = 4
测试成功!
本节讲的调试方法比较晦涩,一般不会采用!
要想自制系统调用,当然首相要做的就是明白系统调用的过程:
我们拿open函数来举个例子:当用户空间执行open函数时,会通过glibc函数库的作用最终去调用sys_open函数,sys_open函数最终又会调用我们具体注册的open函数!那么这里最主要的就是glibc函数库干了些什么呢?其实它的作用就是当用户空间执行open函数时,会去执行一条swi #val指令,这条指令会使cpu发生异常,并跳转到异常向量入口:vector_swi处去执行,之后的代码会根据引发异常的指令取出其中的参数,并根据这个参数调用对应的处理函数!sys_open、sys_read、sys_write这些函数是放在一个数组里面的,就是根据取出的这个val值为下标找到sys_open函数!
说的有点乱,我们来理一理:app调用open->swi #val->引发cpu异常->跳转到异常向量入口处->根据引发异常的指令调用对应的处理函数!
那么我们自制系统调用的话,需要实现两点:
1、写一个应用函数:swi #val
2、在内核里面仿sys_xxx写一个函数,放入数组!
前者用于引发异常,后者用于具体实现!
二、实现
1、内核函数
(1)在arch/arm/kernel/call.S文件里面的CALL()列表的最后添加一项,如:CALL(sys_hello)
这里是用来调用sys_hello
(2)在fs/read_write.c文件里加入如下代码:
asmlinkage void sys_hello(const char __user * buf, size_t count) { char ker_buf[100]; if(buf) { copy_from_user(ker_buf,buf,(count<100) ? count:100); ker_buf[99]='\0'; printk("sys_hello:%s\n",ker_buf); } }这里是sys_hello的具体实现
(3)在include/linux/syscalls.h文件里加入如下代码:
asmlinkage void sys_hello(const char __user * buf, size_t count);
这是对函数sys_hello的声明
以上我们分别实现了函数的定义、声明和调用!这样内核里面的工作就完成了!
2、应用程序
#include <errno.h> #include <unistd.h> #define __NR_SYSCALL_BASE 0x900000 void hello(char *buf, int count) { /* swi */ asm ("mov r0, %0\n" /* save the argment in r0 */ "mov r1, %1\n" /* save the argment in r0 */ "swi %2\n" /* do the system call */ : : "r"(buf), "r"(count), "i" (__NR_SYSCALL_BASE + 352) : "r0", "r1"); } int main(int argc, char **argv) { printf("in app, call hello\n"); hello("wangshuchang frank", 19);//这个函数会进行系统调用 return 0; }当我们用原来的内核启动的时候,打印信息如下:
in app, call hello
当我们用新内核启动的时候,打印信息如下:
in app, call hello
sys_hello:wangshuchang frank
成功了!
这里我们只是实现了一个系统调用,那么系统调用在应用调试里面如何应用?下面这种调试方法不需要太知道了,一般不用,太变态了。一般知道系统调用的原理就OK啦!
——————————————————————————————————————————————————————————————————--——————————
一、步骤
1、修改应用程序的可执行文件,替换某个位置的代码为swi val
2、执行程序
3、进入到sys_hello->在sys_hello里面打印信息->执行原来的指令->返回
二、具体实现
我们的应用程序是:
//file:test_sc.c #include <stdio.h> int cnt = 0; void C(void) { int i = 0; while (1) { printf("Hello, cnt = %d, i = %d\n", cnt, i); cnt++; i = i + 2; sleep(5); } } void B(void) { C(); } void A(void) { B(); } int main(int argc, char **argv) { A(); return 0; }具体步骤:
(1)编译:arm-linux-gcc test_sc.c -o test_sc
(2)反汇编:arm-linux-objdump -D test_sc > test_sc.dis
(3)我们打开上面得到的可执行文件和反汇编文件
比如我们想在C函数的i=i+2;处打断点的话,我们先在反汇编文件里面找到对应的指令:
84d4: e2833002 add r3, r3, #2 ; 0x2
其中:e2833002是机器码,这是我们所需要的!
我们去可执行文件里面去搜索这个机器码,在可执行文件里它对应的机器码应该是:02 30 83 e2
我们将此机器码改为swi指令的机器码!
我们可以将之前的文件反汇编一下,然后得到swi的机器码为:ef900160
(4)经过上面的修改,当程序执行到i=i+2;这条指令时,会产生系统调用,最终执行sys_hello函数。
在sys_hello函数里面,我们可以最一些必要的工作,具体程序如下:
//fs/read_write.c asmlinkage void sys_hello(const char __user * buf, int count) { static int cnt = 0; int val; int ret; struct pt_regs *regs; /* 1. 输出一些调试信息 */ /* 应用程序test_sc的反汇编里: 000107c8 <cnt>: */ copy_from_user(&val, (const void __user *)0x000107c8, 4); printk("sys_hello: cnt = %d\n", val); /* 2. 执行被替换的指令: add r3, r3, #2 ; 0x2 */ /* 搜 pt_regs , 在它的结果里再搜 current */ //获取当前进程的struct pt_regs结构体,这个函数需要本文件包含一个头文件:#include<asm/processor.h> regs = task_pt_regs(current); regs->ARM_r3 += 2;//相当于我们自己实现了那个被替换的指令 /* 打印局部变量i */ copy_from_user(&val, (const void __user *)(regs->ARM_fp - 16), 4); printk("sys_hello: i = %d\n", val); /* 3. 返回 */ if (++cnt == 5) { copy_from_user(&val, (const void __user *)0x8504, 4); printk("[0x8504] code = 0x%x\n", val); printk("regs->ARM_lr = 0x%x\n", regs->ARM_lr); val = 0xe2833002; ret = access_process_vm(current, 0x8504, &val, 4, 1); printk("access_process_vm ret = %d\n", ret); cnt = 0; } return; }此外还需要在:include/linux/syscalls.h 文件里将函数声明改为:asmlinkage void sys_hello(const char __user * buf, int count);
(5)编译内核,用新内核启动
(6)运行测试程序(在此之前要修改测试程序的权限:chmod 777 test_sc_swi),输出信息如下:
Hello, cnt = 0, i = 0
sys_hello: cnt = 1
sys_hello: i = 0
Hello, cnt = 1, i = 2
sys_hello: cnt = 2
sys_hello: i = 2
Hello, cnt = 2, i = 4
sys_hello: cnt = 3
sys_hello: i = 4
测试成功!
本节讲的调试方法比较晦涩,一般不会采用!
相关文章推荐
- 学习笔记 --- LINUX 应用调试之使用strace命令追踪系统调用
- Linux-0.11内核学习笔记【2】:添加系统调用
- LS8-linux系统调用方式文件编程之学习笔记
- 学习笔记 --- LINUX应用调试之使用GDB
- 个人学习笔记---linux系统调用怎么陷入内核空间
- Unix原理与应用(第四版)学习笔记2--系统调用之文件篇
- 【Linux学习笔记】Linux/UNIX系统调用详解(资料整理)
- Linux内核学习笔记四——系统调用
- 学习笔记 --- LINUX应用调试之修改内核输出应用程序的段错误信息
- linux系统调用学习笔记
- 学习笔记 --- LINUX 应用调试之编写之事件记录与自动重现
- ARM2440学习笔记--Linux系统下串口调试和USB调试环境搭建
- Linux内核学习笔记1——系统调用原理【转】
- 学习笔记 --- LINUX 驱动调试之定位系统僵死
- Linux系统调用过程学习笔记
- Linux学习笔记——Btrfs文件系统的管理及应用
- Tiny6410学习ing—(二)、嵌入式Linux应用开发—(2)、文件编辑(系统调用方式访问文件)—①
- Linux学习笔记之网络通信命令、系统关机、sehll应用技巧....
- Unix原理与应用(第四版)学习笔记3--系统调用之进程篇
- linux应用编程笔记(5)系统调用文件编程方法实现文件复制