汇编级理解Linux系统调用
1 操作说明
- 本次实验采用20号系统调用
wirtev
- 通过汇编指令触发该系统调用
- 通过gdb跟踪该系统调用的内核处理过程
- 阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及关注系统调用过程中内核堆栈状态的变化
2 知识预备
我们知道,中断是操作系统的一个重要概念,是操作系统并发操作的的基石。下面是中断的大致分类。
中断
- 外部中断(硬件中断)
- 内部中断(软件中断)/异常
- 故障(fault)
- 陷阱(trap)-----------系统调用从用户态进入内核态的方式
系统调用
系统调⽤的库函数就是我们使⽤的操作系统提供的 API(应⽤程序编程接⼝),API 只是 函数定义。系统调⽤是通过特定的软件中断(陷阱 trap) 向内核发出服务请求,int $0x80 和syscall指令的执⾏就会触发⼀个系统调⽤。C库函数内部使⽤了系统调⽤的封装例程, 其主要⽬的是发布系统调⽤,使程序员在写代码时不需要⽤汇编指令和寄存器传递参数来 触发系统调⽤。⼀般每个系统调⽤对应⼀个系统调⽤的封装例程,函数库再⽤这些封装例 程定义出给程序员调⽤的 API ,这样把系统调⽤终封装成⽅便程序员使⽤的C库函数。
Linux系统调用过程
- 当⽤户态进程调⽤⼀个系统调⽤时,
CPU切换到内核态
并开始执⾏system_call(entry_INT80_32或entry_SYSCALL_64)
汇编代码,其 中根据系统调⽤号
调⽤对应的内核处理函数 - 保存现场,执行中断函数,恢复现场,中断返回(简要来说就是这么些)
Linux系统调用传参(为编写嵌入式汇编做准备)
32位x86体系
结构下普通的函数调⽤
是通过将参数压栈
的⽅式传递的。系统调⽤从⽤户 态切换到内核态,在⽤户态和内核态这两种执⾏模式下使⽤的是不同的堆栈
,即进程的⽤户态堆栈和进程的内核态堆栈,传递参数⽅法⽆法通过参数压栈的⽅式,⽽是通过寄存器 传递参数的方式。
32位x86体系结构
下寄存器的⻓度⼤32位。除了EAX⽤于传递系统调⽤号
外,参数按顺序赋值给EBX、ECX、EDX、ESI、EDI、EBP,参数的个数不能超过6个, 即上述6个寄存器。如果超过6个就把某⼀个寄存器作为指针,指向内存,就可以通过内 存来传递更多的参数。
64位x86体系
结构下普通的函数调⽤和系统调⽤
都是通过寄存器传递参数
,RDI、RSI、RDX、RCX、R8、R9这6个寄存器⽤ 作函数/系统调⽤参数传递,依次对应第 1 参数到第 6 个参数。
3 环境准备
安装开发工具
sudo apt install build-essential
sudo apt install qemu # install QEMU
sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev
下载内核源代码
#pwd=~
sudo apt install axel
axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/ linux-5.4.34.tar.xz
xz -d linux-5.4.34.tar.xz
tar -xvf linux-5.4.34.tar
cd linux-5.4.34
配置内核选项
make defconfig # Default configuration is based on 'x86_64_defconfig'
make menuconfig
# 打开debug相关选项
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debugging
[*] Kernel debugging
# 关闭KASLR,否则会导致打断点失败
Processor type and features ---->
[] Randomize the address of the kernel image (KASLR)
编译和运行内核
make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统终会kernel panic
qemu-system-x86_64 -kernel arch/x86/boot/bzImage # 此时应该不能正常运行
制作根⽂件系统
# pwd = ~
axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2
tar -jxvf busybox-1.31.1.tar.bz2
cd busybox-1.31.1
make menuconfig
#记得要编译成静态链接,不⽤动态链接库。
Settings --->
[*] Build static binary (no shared libs)
#然后编译安装,默认会安装到源码⽬录下的 _install ⽬录中。
make -j