Linux内核分析实验(二)——深入理解系统调用

一、实验内容

  • 找一个系统调用,系统调用号为学号最后2位相同的系统调用
  • 通过汇编指令触发该系统调用
  • 通过gdb跟踪该系统调用的内核处理过程
  • 重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化

二、实验环境配置

1、安装开发工具

sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev
sudo apt install qemu

2、下载安装多线程下载工具axel并使用axel下载内核源代码

sudo apt install axel
axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz

3、解压内核源代码

xz -d linux-5.4.34.tar.xz
tar -xvf linux-5.4.34.tar
cd linux-5.4.34

4、编译内核并配置编译选项

make defconfig
make menuconfig #打开配置选项,如下图所示

Linux内核分析实验(二)——深入理解系统调用_第1张图片

修改相关选项如下:

Kernel hacking --->
      Compile-time checks and compiler options --->
          [*] Compile the kernel with debug info
          [*] Provide GDB scripts for kernel debugging [*] Kernel debugging

Processor type and features ---->
      [] Randomize the address of the kernel image (KASLR)

5、编译

make -j$(nproc)
#在linux-5.4.34文件夹下使用qemu测试内核是否能加载,结果如下图所示显示“kernel panic”
qemu-system-x86_64 -kernel arch/x86/boot/bzImage

Linux内核分析实验(二)——深入理解系统调用_第2张图片

6、使用busybox制作根文件系统,我直接在linux-5.4.34文件夹下下载并解压的

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
#打开debug选项,选择静态链接 Settings
---> [*] Build static binary (no shared libs)

编译安装到busybox-1.31.1下的_install目录下

make -j$(nproc) && make install

7、制作内存根文件镜像,在busybox-1.31.1目录下新键rootfs文件并将busybox-1.31.1下的_install文件中的内容复制到rootfs中,新建四个文件夹,在dev文件下新建七个文档。

mkdir rootfs
cd rootfs
cp ../_install/* ./ -rf
mkdir dev proc sys home
sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/

8、在rootfs文件夹下新建一个init脚本文件,并在init中添加如下内容:

#!/bin/sh
mount -t proc none /proc mount -t sysfs none /sys
echo "Wellcome MengningOS!" echo "--------------------"
cd home
/bin/sh

9、给init文件赋予可执行权限,此时init文件名变为绿色,说明其为可执行文件了

chmod +x init

10、在rootfs文件中打包成内存根文件系统镜像,打包完后在busybox-1.31.1文件夹中就生成了rootfs.cpio.gz镜像文件,在busybox-1.31.1文件中用qemu测试挂载根文件系统,显示“Wellcome MengningOS!”

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
qemu-system-x86_64 -kernel ../arch/x86/boot/bzImage -initrd rootfs.cpio.gz

11、结果

Linux内核分析实验(二)——深入理解系统调用_第3张图片

三、编写汇编指令触发系统调用

1、查看学号后两位所对应的系统调用函数

从inux-5.4.34/arch/x86/entry/syscalls/syscall_32.tbl中找到要调用的学号后两位的63号系统调用。(由于我的虚拟机为32位所以我查找syscall_32.tbl文件,如果是64位的虚拟机则查找syscall_64.tbl文件)

由下文件截图可以看出63号对应的系统调用为sys_dup2。

2、32位Linux系统调用dup2()功能

dup2()函数可以用来复制一个文件描述符,它的原型为:

int dup2(int oldfd, int newfd);

函数执行成功返回新的文件描述符,失败则返回-1。同时它可以用newfd来指定新描述符的数值,如果newfd指向的文件已经被打开则会先将其关闭,如果newfd等于oldfd,就不关闭newfd,newfd和oldfd共同指向同一份文件。这也是linux系统的重定向实现方法。

3、编写调用代码

为了能够触发sys_dup2,新建一个test.c文件,内容编写如下:

int main()
{
    asm volatile(
    "movl $0x3F,%eax\n\t" 
    "syscall\n\t" 
    );
    return 0;
}

使用gcc对文件进行编译,生成可执行文件test,注意要使用静态编译。

gcc test.c -o test -static

将生成的文件复制到rootfs下的home文件夹中

4、在rootfs文件中重新打包在busybox-1.31.1中生成内存根文件系统镜像rootfs.cpio.gz

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

四、gdb跟踪该系统调用的内核处理过程

1、启动qemu

在busybox-1.31.1文件中纯命令行启动qemu

qemu-system-x86_64 -kernel ../arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"

此时会停留在如下图界面不动。

2、启动gdb

在linux-5.4.34中打开另一个新的终端,运行如下代码,查看sys_dup2的断点在fs/file.c的第948行处。

gdb vmlinux
(gdb) target remote:1234
(gdb) b sys_dup2

Linux内核分析实验(二)——深入理解系统调用_第4张图片

输入c继续,回到初试终端运行home下刚编译好的可执行文件test

Linux内核分析实验(二)——深入理解系统调用_第5张图片

3、调试结果

不停输入n进行单步gdb调试,由结果可得触发的是函数__se_sys_dup2()。

Linux内核分析实验(二)——深入理解系统调用_第6张图片

Linux内核分析实验(二)——深入理解系统调用_第7张图片

Linux内核分析实验(二)——深入理解系统调用_第8张图片

五、总结

通过以上gdb调试,entry_sysenter_332()是整个系统调用的入口处,它负责保存现场,调用对应的内核处理函数,并恢复现场,完成系统调用的返回等一系列工作。并且它的保存现场主要是通过swapgs和压栈完成的。

do_syscall_32函数可以在相应寄存器中获取到系统调用号。

然后执行dup2()函数。

最后两个pop指令恢复了相应寄存器

你可能感兴趣的:(Linux内核分析实验(二)——深入理解系统调用)