zoukankan      html  css  js  c++  java
  • 基于Linux-3.9.4内核的GDB跟踪系统调用实验

    382 + 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/

    一、实验环境

      win10 -> VMware -> Ubuntu16.04 + GDB -> QEMU -> linux-3.9.4 + MenuOS

    二、实验目的

      1、了解glibc提供的系统调用函数API,int 0x80、系统调用号及参数传递过程

      2、了解保现场和恢复现场的过程

      3、使用库函数API和C代码中嵌入式汇编代码两种方式使用同一个系统调用

      3、分析system_call中断处理过程

    三、实验过程及结果

      1、系统内核及系统调用函数的选取

        按照实验要求,本次实验需要采用的内核为kernel-5.0版本,在本人实际的实验过程中,一直出现GDB添加断点成功,但是程序运行时并未在断点处停止的现象,经过多次尝试无果之后,决定暂且先使用kernel-3.9.4.的内核,详细问题需要之后解决。

        按照实验要求,对于系统调用函数的选取,应该选取和学号末尾数字相同的系统调用,经过查询之后,基于kernel-3.9.4的系统调用中,未含系统调用号为382的系统调用(arch:x86),决定暂且先使用系统调用号为20的系统调用sys_getpid。

    图 1 系统调用号最大为350

    图 2 系统调用号为20的sys_getpid

      2、库函数API和C代码中嵌入式汇编代码两种方式对系统调用的实现

        2.1 库函数API的实现

    图 3 基于库函数API对sys_getpid的调用实现

        需要注意的是,在调用getpid()函数时,需要加上对头文件的引用申明"#include <unistd.h>",不然可能编译阶段出现失败。

        2.2 C代码中嵌入式汇编代码的实现

    图 4 基于嵌入式汇编的方式对sys_getpid的调用实现

        在上图中,立即数$0x14为sys_getpid在相应的系统调用表中的偏移量(20),"int $0x80 ",该语句的作用是触发系统调用中断,跳转至系统调用入口处。    

      3、对应两种方式的GDB跟踪

        3.1 环境搭建的大致流程

        在搭建实验环境的时候,需要准备的软件环境有:QEMU、GDB、内核的压缩镜像、根文件系统。

        在内核镜像的make过程中,首先先执行“make i386_defconfig”,之后再使用"make menuconfig",基于界面对编译进行配置,为了使得GDB能够跟踪到内核的调试信息,需要选中kernel hacking ->compile the kernel with debug info一栏。保存退出,执行make。

        根文件系统的制作,过程相对比较容易理解,对着相关教程执行问题并不大,这里就不再赘述。

        在QEMU模拟过程中,对于GDB的参数设置,具体为:"file linux-3.9.4/vmlinux"、"target remote:1234"。

        3.2 断点设置

    图 5 设置的两个端点断点信息

    图 6 MenuOS支持的命令    

        3.3 对基于库函数API方式实现过程的跟踪

        在MenuOS命令行下输入mysyscall,会发现其在相应的断点处停止。

    图 7 执行getpid()被中断

    图 8 进入kernel/timer.c中的SYSCALL_DEFINE0(getpid)函数

    图 9 进入arch/x86/include/asm/current.h

    图 10 进入inlude/linux/sched.h,之后进入kernel/pid.c

    图 11 SYSCALL_DEFINE0(getpid)函数返回

        在这时候,GDB继续跟踪的话,效果不明显,经常性出现“Cannot find bounds of current function”,GDB对汇编指令的跟踪效果并不是很好。

        3.4 对基于嵌入式汇编方式实现过程的跟踪

        在MenuOS命令行下输入mysyscall_asm,会发现其在相应的断点处停止。

    图 12 执行被断点1中断

    图 13 断点1上下文

    图 14 继续执行,停止在断点2,之后的过程与3.3小节内容相似

    图 15 两次运行的结果

    四、问题与总结

      1、问题

        在实际的实验过程中,对于mysyscall函数以及mysyscall_asm函数的执行,在第一次执行的时候均可以在断点处停止运行,之后的执行就会无视断点,直接运行结束。对于这个问题,目前的想法是可能对GDB的使用并不是很熟悉。

        在实验kernel-5.0的时候,在使用GDB进行调试的过程中,出现一些问题,在解决大部分问题之后,卡在了其中的一个问题之上。

    图 16 GDB连接失败(已按照提示解决)

    图 17 GDB连接失败(未解决)

        对于图17的错误,目前的想法是:可能是配套的python版本不对,在对应的./gdbinit文件中,多次尝试修改失败。

      2、总结

        对于system_call的过程,其源码如下。  

    ENTRY(system_call)
         RING0_INT_FRAME   # can't unwind into user space anyway
         ASM_CLAC
         pushl_cfi %eax   # save orig_eax
         SAVE_ALL
         GET_THREAD_INFO(%ebp)
                                 # system call tracing in operation  emulation
         testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
         jnz syscall_trace_entry
         cmpl $(NR_syscalls), %eax
         jae syscall_badsys
    syscall_call:
         call *sys_call_table(,%eax,4)
         movl %eax,PT_EAX(%esp)  # store the return value
    syscall_exit:
         LOCKDEP_SYS_EXIT
         DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
            # setting need_resched or sigpending
              # between sampling and the iret
         TRACE_IRQS_OFF
         movl TI_flags(%ebp), %ecx
         testl $_TIF_ALLWORK_MASK, %ecx # current->work
         jne syscall_exit_work
    restore_all:
         TRACE_IRQS_IRET
    restore_all_notrace:
         movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS
         # Warning: PT_OLDSS(%esp) contains the wrong/random values if we
         # are returning to the kernel.
         # See comments in process.c:copy_thread() for details.
         movb PT_OLDSS(%esp), %ah
         movb PT_CS(%esp), %al
         andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
         cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
         CFI_REMEMBER_STATE
         je ldt_ss   # returning to user-space with LDT SS
    restore_nocheck:
        RESTORE_REGS 4   # skip orig_eax/error_code
    irq_return:
        INTERRUPT_RETURN
        对于基于库函数API对函数的调用,一般是在用户模式下的getpid()函数中触发内核态的系统调用,之后进入内核态的系统调用处理入口,根据相关的偏移量,找到实际执行的内核功能函数,执行并返回。
        对于基于嵌入式汇编方式的调用方式,不需要经过库函数提供的API函数进行系统调用的触发,直接跳转至系统调用处理入口,根据偏移量找到相应的函数,执行并返回。
        下图为system_call的大致流程图。
    图 18 system_call流程图
  • 相关阅读:
    表单验证 validate 两种 一种是callback配合外部变量,当同步用。第2中是 then async await 这种 真正$api也适用
    Nginx 同时支持 http 和 https SSL 为了能有权限调取摄像头
    printJS 打印 无头无尾 style 加 @page { margin: 0; } body { padding: 100px;}
    Nginx 打不开 80端口占用 netstat -aon|findstr "80" 看有没有80占用 有的话 net stop http
    Selenium IDE 自动化测试 bug 会在console里面出 DevTools failed to load SourceMap 很不好,用完记得关掉这个程序
    async await $api vue
    electron打包踩过的坑总结 好文
    err => { err.name + ' ' + err.message 报错信息的调用
    跨域! dev: 'http://192.168.40.81:9090/xxx-api/' 平台和项目 两个都要改 要不会跨域!跨域!跨域!
    can do / will do / should do 情态动词
  • 原文地址:https://www.cnblogs.com/wyt123/p/10556568.html
Copyright © 2011-2022 走看看