代码改变世界

实验四:扒开系统调用的三层皮(上)

2016-03-20 13:05  20135114王朝宪  阅读(283)  评论(0编辑  收藏  举报

 

注:作者:王朝宪,原创作品转载请注明出处,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

 

一、用户态、内核态和中断处理过程

1、权限级别

为什么有权限级别的划分?

当系统中所有程序员编写的代码都可以使用特权指令,系统很容易崩溃。

也是让系统更稳定的机制。

2、在Linux代码中如何区分用户态和内核态?

判断cs:eip的值:

在内核态中,cs:eip可以是任意的值,在32位的x86系统中有4G的地址空间。

3、中断处理是从用户态进入内核态的主要方式。

例:硬件中断,系统调用(是一种特殊的中断)。

4、从用户态切换到内核态时:必须保存用户态的寄存器上下文!

 中断发生后的第一件事就是保存现场。

保护现场就是进入中断程序,保存需要用到的寄存器的数据;

恢复现场就是退出中断程序,恢复保存寄存器的数据。

iret指令与中断信号(包括int指令)发生时的CPU做的动作正好相反。

5、中断处理的完整过程

int0x80:指系统调用

保存:将当前的cs:eip、ss:esp、eflags保存到内核堆栈中。

加载:把当前的中断信号或系统调用相关联的中断处理程序的入口加载到cs:eip中,同时将当前的堆栈段ss和esp也加载到CPU中。

 

二、系统调用概述

1、系统调用的意义

2、操作系统提供的API和系统调用的关系

3、应用程序、封装例程、系统调用处理程序及系统调用服务例程之间的关系

系统调用的三层皮:API,中断向量对应的中断服务程序,系统调用服务程序。

例:xyz、system_call和sys_xyz。

4、系统调用程序及服务例程

中断向量0x80与system_call绑定起来。

系统调用号将xyz和sys_xyz关联起来了。

5、系统调用的参数传递方法

注:函数调用传递参数使用的是压栈的方式。

 若超过6个,就将某一个寄存器作为指针,指向一块内存,在进入内核态之后,可以访问所有的地址空间,通过内存来传递参数。

 

三、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用

1、使用库函数API来获取系统当前时间

 

命令:

编写time程序:vi time.c

编译:gcc time.c -o time -m32

执行:./time

 

2、C代码中嵌入汇编代码的写法

命令:

vi asm_time.c

gcc asm_time.c -o asm_time -m32

./asm_time

 

 

 

实验:

1、库函数API中的getuid函数

代码如下

截图:

 

 

 

2、在asm_getuid.c代码中嵌入汇编代码

 

总结:

     总结,我认为系统调用就是内核将常用的用户需要使用底层硬件或特权级操作的相关代码,封装成服务例程,给每个例程一个编号(系统调用号),当用户需要执行相应功能时,进程产生软中断int 0X80,装系统调用号作为参数传入eax寄存器。完成相应的功能。