程涵 

原创博客

《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. Volley源码分析(三)NetWorkDispatcher分析

    NetWorkDispatcher分析 NetWorkDispatcher和CacheDispatcher一样,继承于Thread,在run方法中实现一个无限循环,代码如下 @Override pub ...

  2. android 登录效果

    <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android=&q ...

  3. 5、JUC--实现 Callable 接口

    Callable接口  Java 5.0 在 java.util.concurrent 提供了一个新的创建执行 线程的方式:Callable 接口  Callable 接口类似于 Runnable ...

  4. MetaMask/metamask-extension-provider

    用来探测你的浏览器中有没有安装metamask插件 https://github.com/MetaMask/metamask-extension-provider MetaMask Extension ...

  5. NFS及RPC讲解

    导读 NFS(Network File System)即网络文件系统,由Sun公司开发,于1984年向外公布.功能是通过网络让不同的机器.不同的操作系统能够彼此分享个别的数据,让应用程序在客户端通过网 ...

  6. 蓝桥杯 历届试题 网络寻路(dfs搜索合法路径计数)

    X 国的一个网络使用若干条线路连接若干个节点.节点间的通信是双向的.某重要数据包,为了安全起见,必须恰好被转发两次到达目的地.该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径 ...

  7. 四,ESP8266 TCP服务器(基于Lua脚本语言)

    我要赶时间赶紧写完所有的内容....朋友的东西答应的还没做完呢!!!!!!!没想到又来了新的事情,,....... 配置模块作为TCP服务器然后呢咱们连接服务器发指令控制LED亮灭 控制的指令呢咱就配 ...

  8. Python操作Saltstack

    1.代码 # -*- coding:utf-8 -*- import urllib.request import urllib.parse import json class saltAPI(): d ...

  9. 利用Cydia Substrate进行Android HOOK

    Cydia Substrate是一个代码修改平台.它可以修改任何主进程的代码,不管是用Java还是C/C++(native代码)编写的.而Xposed只支持HOOK app_process中的java ...

  10. 2017-2018-2 20155224『网络对抗技术』Exp7:网络欺诈防范

    基础问题回答 问:通常在什么场景下容易受到DNS spoof攻击? 同一局域网下,以及各种公共网络. 问:在日常生活工作中如何防范以上两攻击方法? 答:DNS欺骗攻击是很难防御的,因为这种攻击大多数本 ...