程涵 

原创博客

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

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


知识点整理

一、用户态、内核态和中断

处理过程

1. 通过库函数完成系统调用:库函数将系统调用封装起来。

2. 用户态与内核态

  • 内核态:一般现代CPU有几种指令执行级别。在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别对应着内核态。
  • 用户态:在相应的低级别执行状态下,代码的掌控范围有限,只能在对应级别允许的范围内活动。

3. 区分用户态与内核态的方法

  • cs寄存器的最低两位表明了当前代码特权级
  • CPU每条指令的读取都是通过cs:eip这两个寄存器

4. 中断处理

  • 中断处理是从用户态到内核态的主要方式(系统调用是中断的一种特殊形式)
  • 系统调用只是一种特殊的中断
  • 寄存器上下文,从用户态切换到内核态时:必须保存用户态的寄存器上下文,同时将内核态的寄存器相应的值放入当前CPU
  • 中断/int指令会在堆栈上保存一些寄存器的值:如用户态栈顶地址、当前的状态字、当时cs:eip的值(当前中断程序的入口)

5. 保护现场与恢复现场

  • 保护现场:进入中断程序,保存需要用到的寄存器的数据(中断发生后的第一件事)

    #define SAVE_ALL  //将其他寄存器的值push到内核堆栈中
  • 恢复现场:退出中断程序,恢复保存寄存器的数据(中断处理结束前最后一件事)

    
    
    #RESTORE_ALL  //将用户态保存的寄存器pop到当前CPU中
  • iret指令:iret指令与中断信号(包括int指令)发生时的CPU的动作相反

6. 中断处理的完整过程

  • 第一步

    interrupt(ex:int 0x80)-save //int 0x80指系统调用
    cs:eip/ss:esp/eflags(current)to kernel stack,then //中断将cs:eip、ss:esp(当前堆栈段栈顶)、eflags(当前标志寄存器)保存到内核堆栈中
    load cs:eip(entry of a specific ISR)and //将当前中断信号相关联的中断服务入口加载到cs:eip
    ss:esp(point to kernel stack). //同时将当前指向内核信息的的堆栈段和esp也加载到CPU中
  • 第二步

    SAVE_ALL
    -... //内核代码,完成中断服务,(完成中断服务后可能)发生进程调度
    //如果发生了进程调度,则当前的状态都会暂时保存在系统中。当其他进程调度切换回当前进程时,则接着执行RESTORE_ALL
  • 第三步

    RESTORE_ALL
  • 第四步

    iret -pop cs:eip/ss:esp/eflags from kernel stack

二、系统调用概述和系统调用的三层皮

1. 系统调用概述

系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口。

意义:

  • 把用户从底层的硬件编程中解放出来
  • 极大的提高了系统的安全性
  • 使用户程序具有可移植性(用户程序与具体硬件被抽象的接口替代,没有非常紧密的关系)

2. API和系统调用

  • 应用程序接口(API)与系统调用不同

    • API只是一个函数定义
    • 系统调用通过软件中断trap向内核发出一个明确的请求
  • Libc库定义的一些API引用了封装例程(唯一目的是发布系统调用,直接调用函数就可以出发系统调用)
    • 一般每个系统调用对应一个封装例程
    • 库再用这些封装例程定义出给用户的API
  • 不是每个API都对应一个特定的系统调用
    • API可能直接提供用户态的服务,如一些数学函数
    • 一个单独的API可能调用几个系统调用
    • 不同的API可能调用了同一个系统调用
  • 返回值
    • 大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用
    • -1在多数情况下表示内核不能满足进程的请求
    • Libc中定义的errno变量包含特定的出错码

3. 系统调用的三层皮

xyz(API)、system_ call(中断向量)、sys_xyz(中断向量对应的中断服务程序)

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

  • 当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数

    • 在Linux中是通过执行int $0x80来执行系统调用的, 这条汇编指令产生向量为128的编程异常
    • Intel Pentium II中引入了sysenter指令(快速系统调用),2.6已经支持
    • (系统调用号将xyz和sys_xyz关联起来)
  • 传参:内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数
    • 使用eax寄存器

5. 参数传递

  • 系统调用也需要输入输出参数,例如

    • 实际的值
    • 用户态进程地址空间的变量的地址
    • 甚至是包含指向用户态函数的指针的数据结构的地址
  • system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号
    • 一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即_NRfork)。
    • 这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号
    • 进入sys_call之后,立即将eax的值压入内核堆栈
  • 寄存器传递参数具有如下限制:
    • 每个参数的长度不能超过寄存器的长度,即32位
    • 在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)
    • 超过6个则将某一个寄存器作为一个指针指向一块内存,进入内存态后可以访问所有地址空间,可以通过那块内存传递数据

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

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

time.c

#include<stdio.h>
#include<time.h> int main()
{
time_t tt;
struct tm *t;//构造一个结构体,方便读取
tt = time(NULL);//time系统调用
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n", t->tm_year+, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return ;
}

2.使用C代码中嵌入汇编代码触发系统调用获取系统当前时间

time_asm.c

#include<stdio.h>
#include<time.h> int main()
{
time_t tt;
struct tm *t;
asm volatile(
"mov $0,%%ebx\n\t" # 把ebx清零,相当于传参数
"mov $0xd,%%eax\n\t" # 把0xd放入eax中,即系统调用号13,指time
"int $0x80\n\t"
"mov %%eax,%0\n\t" # 返回值是在eax中,%0指tt,把返回值放到tt中去。
: "=m" (tt)
);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n", t->tm_year+, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return ;
}

用户态向内核态:
1.传递了一个系统调用号 - eax
2.传递了参数 - ebx

实验过程及截图

要求:

  • 选择一个系统调用(13号系统调用time除外),系统调用列表参见http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
  • 参考视频中的方式使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

我选择的是24号getuid。

getuid.c

#include <unistd.h>
#include <stdio.h> int main()
{
pid_t uu;
uu=getuid();
printf("uu = %d \n", uu);
return ;
}

getuid_asm.c

#include <unistd.h>
#include <stdio.h> int main()
{ pid_t uu;
uu = getuid();
asm volatile(
"mov $0x24,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(uu)
);
printf("uu = %d \n",uu);
return ; }

执行结果:

分析过程:

我们使用int 0x80触发中断,然后中断处理程序保存现场,我们的进程陷入内核态。

系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数,由API、中断向量和中断处理程序协调完成。

而在系统调用的过程中,系统调用号使用eax寄存器传递参数。

寄存器在传递参数时的限制:

1)每个参数的长度不能超过寄存器的长度,即32位。

2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp),若超过6个,就将某一个寄存器作为指针,指向一块内存,在进入内核态之后,可以访问所有的地址空间,通过内存来传递参数。

【MOOC EXP】Linux内核分析实验四报告的更多相关文章

  1. 【MOOC EXP】Linux内核分析实验八报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的切换和系统的一般执行过程 知识点 ...

  2. 【MOOC EXP】Linux内核分析实验七报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 可执行程序的装载 知识点梳理 一.预处 ...

  3. 【MOOC EXP】Linux内核分析实验六报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的描述和进程的创建 知识点梳理: ...

  4. 【MOOC EXP】Linux内核分析实验一报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  [反汇编一个简单的C程序]   实验 ...

  5. 【MOOC EXP】Linux内核分析实验二报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  [操作系统是如何工作的]   教学内 ...

  6. 【MOOC EXP】Linux内核分析实验三报告

     程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [跟踪分析Linux内核的启动过程] ...

  7. 【MOOC EXP】Linux内核分析实验五报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 分析system_call中断处理过程 ...

  8. “Linux内核分析”实验一报告

    张文俊 + 原创作品转载请注明出处 + <Linux 内核分析> MOOC 课程 实验要求: 1.总结部分要求阐明自己对“计算机是如何工作的”理解: 2.博客中需要使用实验截图: 实验内容 ...

  9. “Linux内核分析”实验二报告

    张文俊 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.第二周学习内 ...

随机推荐

  1. 3.Solr4.10.3目录结构

    转载请出自出处:http://www.cnblogs.com/hd3013779515/ 1.整个目录结构 (1)bin:是脚本的启动目录 (2)contrib:第三方包存放的目录 (3)dist:编 ...

  2. 敲代码非常难之logstash之file input插件实现分析

    版权声明:本文为横云断岭原创文章,未经博主同意不得转载.微信公众号:横云断岭的专栏 https://blog.csdn.net/hengyunabc/article/details/25665877 ...

  3. bip39

    BIP: 39 (助记词) Layer: Applications Title: Mnemonic code for generating deterministic keys Author: Mar ...

  4. PReLU与ReLU

    PReLU激活函数,方法来自于何凯明paper <Delving Deep into Rectifiers:Surpassing Human-Level Performance on Image ...

  5. HDU1166

    https://vjudge.net/contest/66989#problem/A C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直 ...

  6. android asmack调用MultiUserChat.getHostedRooms方法出现空指针的异常解决方案

    今天在做即时通讯群聊时,调用MultiUserChat.getHostedRooms(conn, SmackTools.getInstance().conn.getServiceName());方法获 ...

  7. cloudstack secondary vm starting

    等1个小时,差不多可以进入虚拟机,看日志/var/log/cloud.log

  8. 如何一步一步建立CAN通讯

    如何一步一步建立CAN通讯 2016-04-12 20:38:14来源: eefocus 关键字:CAN通讯  硬件环境   收藏 评论(0) 分享到 微博 QQ 微信 LinkedIn CAN通讯的 ...

  9. Django Rest Framework源码剖析(八)-----视图与路由

    一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...

  10. 2017-2018-2 《网络对抗技术》20155322 Exp9 web安全基础

    [-= 博客目录 =-] 1-实践目标 1.1-实践介绍 1.2-实践内容 1.3-实践要求 2-实践过程 2.1-HTML 2.2-Injection Flaws 2.3-XSS 2.4-CSRF ...