一、实验要求
1.学号末尾为59,故采用59号系统调用execve。
2.通过汇编指令触发系统调用
3.通过gdb跟踪该系统调用的内核处理过程
4.重点阅读分析系统调用入口的保存现场和恢复现场
二、环境准备
1. 安装环境
1 sudo apt install build-essential 2 sudo apt install qemu # install QEMU 3 sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev 4 sudo apt install axel
2. 下载linux5.4.34源码
1 axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz 2 xz -d linux-5.4.34.tar.xz 3 tar -xvf linux-5.4.34.tar cd linux-5.4.34
3. 配置内核编译选项
1 make defconfig #Default configuration is based on ‘x86_64_defconfig‘ 2 make menuconfig 3 #打开debug相关选项 4 Kernel hacking ---> 5 Compile-time checks and compiler options ---> 6 [*] Compile the kernel with debug info 7 [*] Provide GDB scripts for kernel debugging [*] Kernel debugging 8 #关闭KASLR,否则会导致打断点失败 9 Processor type and features ----> 10 [] Randomize the address of the kernel image (KASLR)
4. 编译内核
1 make -j$(nproc) 2 # 测试一下内核能不能正常加载运行,因为没有文件系统最终会kernel panic 3 qemu-system-x86_64 -kernel arch/x86/boot/bzImage
5. 制作根文件系统
下载
1 axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2 2 tar -jxvf busybox-1.31.1.tar.bz2 3 cd busybox-1.31.1
制作根文件系统
1 make menuconfig 2 记得要编译成静态链接,不用动态链接库。 3 Settings ---> 4 [*] Build static binary (no shared libs) 5 然后编译安装,默认会安装到源码目录下的 _install 目录中。 6 make -j$(nproc) && make install
6. 制作内存根文件系统镜像
1 mkdir rootfs 2 cd rootfs 3 cp ../busybox-1.31.1/_install/* ./ -rf 4 mkdir dev proc sys home 5 sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
7. 准备init脚本文件放在根文件系统跟目录下(rootfs/init),添加如下内容到init文件。
1 #!/bin/sh 2 mount -t proc none /proc mount -t sysfs none /sys 3 echo "Wellcome MengningOS!" echo "--------------------" 4 cd home 5 /bin/sh
1 chmod +x init
1 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
1 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz
三、查看系统调用并编写调用汇编代码
1. 打开/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl
2. 编写普通汇编调用代码
在rootfs/home目录下编写代码
因为execve需要调用其他的程序,因此编写了一个Helloworld程序供调用。
hello.c
1 #include<stdio.h> 2 3 int main() { 4 printf("Hello World\n"); 5 }
execve.c (execve正常运行无返回值)
1 #include <stdio.h> 2 #include <unistd.h> 3 int main() 4 { 5 char *data[2]; 6 data[0] = "./hello"; 7 data[1] = NULL; 8 char * const* null = NULL; 9 int res; 10 //execve(data[0], data, null); 11 asm volatile( 12 "movq %3, %%rdx\n\t" // 参数3 13 "movq %2, %%rsi\n\t" // 参数2 14 "movq %1, %%rdi\n\t" // 参数1 15 "movl $0x3B,%%eax\n\t" // 传递系统调用号 16 "syscall\n\t" // 系统调用 17 "movq %%rax,%0\n\t" // 结果存到%0 就是str_len中 18 :"=m"(res) // 输出 19 :"a"(data[0]),"b"(data),"c"(null) 20 21 ); 22 //printf("%d", res); 23 }
gcc编译(这里需要静态编译)
gcc hello.c -o hello -static gcc execve.c -o execve -static
3. 重新制作根文件系统
1 ?nd . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
四、gdb调试
1. 纯命令行启动qemu
1 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
此时停留在以下界面
2. 打开另一个终端
在linux源码目录下运行
1 gdb vmlinux
在gdb中运行
1 target remote:1234
给对应的系统调用打上断点
输入c(continue)继续运行,因为系统启动过程中也会多次调用execve,因此需要多次输入c
运行编写好的调用系统调用的代码
gdb单步调试
五、 结果分析
1. 汇编指令syscall 触发系统调用,通过MSR寄存器找到了中断函数入口
2. 跳转获得系统调用号,执行系统调用的内容
3. 后续关键调用
4. 调用结束,恢复到用户态执行syscall_return_slowpath
函数要为恢复现场做准备。