实验二 深入理解系统调用

一.实验环境准备

安装工具:

sudo apt install build-essential
sudo apt install qemu # install QEMU 
sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev

下载内核源码:

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:

制作根文件系统

# 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$(nproc) && make install
#pwd = ~ 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/

  

二.系统调用

1.选择自己的系统调用,本人学号091;所以选择91号;

实验二 深入理解系统调用_第1张图片

fchmod函数说明:

头文件:#include   #include

定义函数:int fchmod(int fildes, mode_t mode);

函数说明:fchmod()会依参数mode 权限来更改参数fildes 所指文件的权限。参数fildes 为已打开文件的文件描述词。参数mode 请参考chmod ()。

返回值:权限改变成功则返回0, 失败返回-1, 错误原因存于errno.

 

 

mode 说明
S_ISUID 
S_ISGID 
S_ISVTX
执行时设置用户ID 
执行时设置组ID 
保存正文(粘住位)
S_IRWXU 
     S_IRUSR 
     S_IWUSR 
     S_IXUSR
用户(所有者)读、写和执行 
用户(所有者)读 
用户(所有者)写 
用户(所有者)执行
S_IRWXG 
     S_IRGRP 
     S_IWGRP 
     S_IXGRP
组读、写和执行 
组读 
组写 
组执行
S_IRWXO 
     S_IROTH 
     S_IWOTH 
     S_IXOTH
其他读、写和执行 
其他读 
其他写 
其他执行

 

汇编改写手动触发系统调用
新建文件 fchmod-test.c

 

#include
#include
int main()
{
int fd;
fd = open ("/etc/passwd",O_RDONLY);
fchmod(fd,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
close(fd);
return 0; }

 

运行fchmod-test.c 更改后的权限:

 

 更改fchmod-test.c

#include
#include
int main()
{
int fd;
fd = open ("/etc/passwd",O_RDONLY);
asm volatile(
		"movq %1, %%rsi\n\t"   //  参数2
		"movq $0x1, %%rdi\n\t"  //  参数1 
		"movl $0x58,%%eax\n\t" //  传递系统调用号
		"syscall\n\t"          //  系统调用
		"movq %%rax,%0\n\t"    //  结果存到%0 就是str_len中
		:"=m"(str_len) // 输出
		:"g"(vec) // 输入
	);
close(fd);
return 0;
} 

gdb跟踪:

qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
file vmlinux
cd linux-5.4.34 # 启动 
gdb vmlinux
target remote:1234 # 在semctl调用处打断点
b __x64_sys_semctl

  

函数的调用顺序为entry_SYSCALL_64 () —> do_syscall_64() —> __x64_sys_gettimeofday()

(gdb) n
do_syscall_64 (nr=140730116323824, regs=0xffffc900001b7f58)
    at arch/x86/entry/common.c:300
300        syscall_return_slowpath(regs);
(gdb) n
301    }
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:184
184        movq    RCX(%rsp), %rcx
(gdb) n
185        movq    RIP(%rsp), %r11
(gdb) n
187        cmpq    %rcx, %r11    /* SYSRET requires RCX == RIP */
(gdb) n
188        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
205        shl    $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
(gdb) n
206        sar    $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
(gdb) n
210        cmpq    %rcx, %r11
(gdb) n
211        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
213        cmpq    $__USER_CS, CS(%rsp)        /* CS must match SYSRET */
(gdb) n
214        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
216        movq    R11(%rsp), %r11
(gdb) n
217        cmpq    %r11, EFLAGS(%rsp)        /* R11 == RFLAGS */
(gdb) n
218        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
238        testq    $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
(gdb) n
239        jnz    swapgs_restore_regs_and_return_to_usermode
(gdb) n
243        cmpq    $__USER_DS, SS(%rsp)        /* SS must match SYSRET */
(gdb) n
244        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
253        POP_REGS pop_rdi=0 skip_r11rcx=1
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:259
259        movq    %rsp, %rdi
(gdb) n
260        movq    PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:262
262        pushq    RSP-RDI(%rdi)    /* RSP */
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:263
263        pushq    (%rdi)        /* RDI */
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:271
271        SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
(gdb) n
273        popq    %rdi
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:274
274        popq    %rsp
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:275
275        USERGS_SYSRET64
(gdb) n
0x0000000000448b97 in ?? ()
(gdb) c
Continuing.
(gdb)

  

 

实验总结:

Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。

  系统调用是怎么工作的?

    其原理是进程先用适当的值填充寄存器,然后调用一个特殊的指令,这个指令会跳到一个事先定义的内核中的一个位置。在Intel CPU中,这个由中断0x80实现。硬件知道一旦你跳到这个位置,你就不是在限制模式下运行的用户,而是作为操作系统的内核--由用户态转为内核态。

    进程可以跳转到的内核位置叫做sysem_call。这个过程检查系统调用号,这个号码告诉内核进程请求哪种服务。然后,它查看系统调用表(sys_call_table)找到所调用的内核函数入口地址。接着,就调用函数,等返回后,做一些系统检查,最后返回到进程(或到其他进程,如果这个进程时间用尽)。

    进程号是由eax寄存器存储的,参数一般是由ebx、ecx、edx、esl、edl、ebp来存储的。  

 

你可能感兴趣的:(实验二 深入理解系统调用)