深入理解系统调用
实验内容:
- 找一个系统调用,系统调用号为学号最后 2位相同的系统调用【即 97号系统调用】
- 通过汇编指令触发该系统调用
- 通过 gdb 跟踪该系统调用的内核处理过程
- 重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化
实验环境:
VMWare虚拟机下的Ubuntu18.04.4,实验采用的内核版本为linux-5.4.34。
1 环境准备
1.1 内核编译
回退实验一的补丁操作:
cd linux-5.4.34
patch -R -p1 < ../mykernel-2.0_for_linux-5.4.34.patch
make defconfig
修改内核编译配置重新编译:
#打开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 menuconfig
make -j$(nproc)
启动内核,此时内核无法正常运行,提示Kernel panic报错:
qemu-system-x86_64 -kernel arch/x86/boot/bzImage
根据报错提示,可以看出是缺少必要的根文件系统,导致内核无法挂载。
1.2 制作根文件系统
电脑加电启动首先由bootloader加载内核,内核紧接着需要挂载内存根文件系统,其中包含必要的设备驱动和工具。
为了简化实验环境,仅借助 BusyBox 制作极简内存根文件系统,提供基本的用户态可执行程序。
首先从https://www.busybox.net下载 busybox源代码解压,解压完成后,配置编译并安装。
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
编译安装,默认会安装到源码目录下的 _install 目录中。
make -j$(nproc) && make install
制作内存根文件系统镜像:
mkdir rootfs
cd rootfs
cp ../busybox-1.31.1/_install/* ./ -rf
mkdir dev proc sys home
sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
在根文件系统目录下添加init脚本文件(rootfs/init),init内容如下:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Wellcome MengningOS!"
echo "--------------------"
cd home
/bin/sh
给init脚本添加可执行权限:
chmod +x init
打包成内存根文件系统镜像:
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
测试挂载根文件系统,看内核启动完成后是否执行init脚本:
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz
bootloader成功加载根文件系统到内存中后,内核会将其挂载到根目录下。
然后运行根文件系统中 init 脚本执行一些启动任务,最后才挂载真正的磁盘根文件系统。
2 系统调用
2.1 查找系统调用
在 linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl 文件中找到相应的系统调用:
2.2 触发系统调用
getrlimit用于获得每个进程能够创建的各种系统资源的限制使用量。
在rootfs/home/目录下新建getrlimit_test.c进行测试:
#include <stdio.h>
#include <sys/resource.h>
int main()
{
struct rlimit limit;
int ret = getrlimit(RLIMIT_NOFILE, &limit);
printf("ret = %d,\tcur = %ld,\tmax = %ld\n",
ret, limit.rlim_cur, limit.rlim_max);
return 0;
}
函数执行成功返回0,失败返回1。
其中,RLIMIT_NOFILE表示每个进程能打开的最多文件数。
limit.rlim_cur为当前软件限制,limit.rlim_max为最大硬件限制。
采用静态编译:
gcc -o getrlimit_test getrlimit_test.c -static
代码测试结果如下:
getrlimit测试成功后,通过编写汇编代码来触发系统调用:
#include <stdio.h>
#include <sys/resource.h>
int main()
{
struct rlimit limit;
int ret = -1;
asm volatile(
"movq %2, %%rsi\n\t"
"movl %1, %%edi\n\t"
"movl $0x61, %%eax\n\t"
"syscall\n\t"
"movq %%rax,%0\n\t"
:"=m"(ret)
:"a"(RLIMIT_NOFILE), "b"(&limit)
);
printf("ret = %d,\tcur = %ld,\tmax = %ld\n",
ret, limit.rlim_cur, limit.rlim_max);
return 0;
}
2.3 跟踪系统调用内核处理过程
重新制作根文件系统:
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
纯命令行启动qemu:
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
开启新的terminal进行gdb调试:
cd linux-5.4.34
gdb vmlinux
target remote:1234
c
添加断点测试:
b __x64_sys_getrlimit
2.4 系统调用过程分析
重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,重点关注系统调用过程中内核堆栈状态的变化
原文地址:https://www.cnblogs.com/ustca/p/12965967.html
- Java设计模式-模板方式模式
- 由一条create语句的问题对比mysql和oracle中的date差别 (r7笔记第26天)
- Java设计模式-命令模式
- 关于执行计划中的%CPU的含义 (r7笔记第25天)
- Java设计模式-享元模式
- 简单易学的机器学习算法——线性可分支持向量机
- 基于gensim的Doc2Vec简析,以及用python 实现简要代码
- 关于db link权限分配的苦旅(一) (r7笔记第42天)
- Java设计模式-责任链模式
- Java设计模式-策略模式
- CPU 100%负载的性能优化分析(r7笔记第40天)
- Sklearn-train_test_split随机划分训练集和测试集
- 使用shell来定制dbms_sqltune(r7笔记第39天)
- 简单易学的机器学习算法——分类回归树CART
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 精品连载丨安卓 App 逆向课程之五 frida 注入 Okhttp 抓包下篇
- Excel VBA常用功能加载宏——工作表隐藏
- VBE菜单——CommandBars对象
- Java|屏幕截图
- VBA解压缩ZIP文件07——length和distance扩展
- Excel VBA常用功能加载宏——打开活动工作簿所在文件夹
- 常用功能加载宏——拆分工作表
- MyVBA加载宏——添加自定义菜单04——功能实现
- CS学习笔记 | 14、powerup提权的方法
- VBA解压缩ZIP文件05——Huffman树
- JavaScript|jQuery基础语法
- VBA解压缩ZIP文件03——解压准备工作
- VBA解压缩ZIP文件04——解析ZIP文件结构
- 开发|Springboot简单实现文件上传
- VBA解压缩ZIP文件01——实现的功能