本次实验主要内容如下:
- 编译内核5.0
- 选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析
一、编译内核5.0
首先下载5.0.1的内核源码,解压之后进入对应的目录,执行命令
make i386_defconfig
make menuconfig
但是这时候出现了各种各样的报错,因此需要根据报错安装相应的依赖,以下是本次过程安装的依赖:
sudo apt-get install flex
sudo apt-get install libncurses5-dev libssl-dev
sudo apt-get install bison
这时候make config成功,出现下面的界面:
然后
kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info
接下来make,但是依然出现很多问题,缺少很多东西,按照提示一个一个的装好了。中间还有很多小插曲,最后通过百度解决了。
缺少openssl,根据提示安装即可
编译完成
二、制作根目录
mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
gcc -pthread -o init linktable.c menu.c test.c -m32 -static
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
三、启动MenuOS系统
qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
四、跟踪调试内核启动
学号534, 所以找第34位系统调用号进行使用,该系统调用是__NR_nice,即nice()函数。
函数说明
【函 数 名】nice — 调整进程运行的优先级
【函数原型】int nice(int inc);
【相关头文件】unistd.h
【函数说明】
在Linux中,进程运行的优先级分为-20~19等40个级别,其中,数值越小运行优先级越高,数值越大运行优先级越低。显而易见,优先级 -20的运行优先级最高,优先级19的运行优先级最低。函数nice是将当前进程运行的优先级增加指定值,既用当前进程运行的优先级加上指定值得到新的优先级,然后用新的优先级运行该进程。当计算出来的值小于-20,则进程将以优先级-20运行;当计算出来的值大于19,则进程将以优先级19运行。若增加正值,则表示降低进程运行优先级;若增加负值,则表示升高进程运行优先级。但只有具有超级用户权限的用户才可以以负数作为函数的参数,否则该函数将返回错误。
【参数说明】
inc [IN] 指定优先级增加的值
【函数返回值】
若操作成功,函数将返回调整后的进程运行的优先级;
若操作失败,函数将返回-1。
注意:当函数返回-1时,不一定就是函数操作失败。因为若函数成功调整进程运行优先级后的优先级为-1,函数也返回-1,所以在判断函数是否操作失败时,除了判断函数返回的值是否为-1外,还需要查看 errno的值是否为相关错误码,详见“程序示例”。
【相关错误码】
EACCES 无操作权限,既非超级用户权限的用户用负值作为参数调用该函数。
【程序示例】
#include <stdio.h> /* printf */
#include <stdlib.h> /* atoi, system, exit */
#include <errno.h> /* errno */
#include <string.h> /* strerror */
#include <unistd.h> /* nice */
int main(int argc, char *argv[])
{
int adjustment = 0;
int ret;
if ( argc > 1 ) {
adjustment = atoi( argv[1] );
}
ret = nice( adjustment );
printf( "nice(%d):%d/n", adjustment, ret );
if ( -1 == ret ) {
if ( errno == EACCES ) {
printf( "Cannot set priority:%s./n", strerror( errno ) );
exit(-1);
}
}
system("nice");
exit(0);
}
函数测试
在test.c 中加入niceTest函数:
在此文件中的main函数里进行登记,即加入以下代码,并重新编译制作rootfs.img文件:
MenuConfig("testNice","Test Nice",testNice);
在gbd下跟踪系统调用
打开两个终端,在其中一个输入:
qemu -kernel linux-5.0/arch/x86/boot/bzImage -initrd rootfs.img -s -S -append nokaslr
在运行如上命令后,会弹出一个处于stop状态的qemu窗口:
再在另外一个终端下,进入linux-5.0.1目录,并输入以下命令进入gdb的脚本模式:
sudo gdb
在gdb脚本模式下,输入以下内容:
file vmlinux
target remote:1234
b sys_nice
c
在运行完以上命令后,处于stop状态的qemu终端就会开始运行,然后在其脚本模式下,输入testNice:
实验总结
- 系统调用就是一种用户态到内核态最后再到用户态的一种过程
- 调用一个系统调用时,执行int 0x80指令触发系统调用后系统从用户态进入内核态,system_call()函数通过系统调用号查找系统调用表来查找具体系统调用服务进程
内核态通过汇编代码直接操作内存和寄存器,由于涉及上下文的切换问题,在进入中断程序前,我们需要利用堆栈保护现场,完成了系统调用之后,从堆栈中获得数据恢复现场 - 用户态与内核态的栈堆不同,所以它们只能使用寄存器来传值,其中eax主要是存放系统调用号以及调用返回值,其他的eax、ebx、ecx、edx、esi和edi寄存器主要存放系统调用的参数
- 除了系统调用号(eax),参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp),若超过6个,可以把某一个寄存器作为指针指向一块内存,在进入内核态后访问该地址空间来传递参数