• 利用bochs调试内核


    14.8 利用bochs调试内核
    Bochs具有非常强大的操作系统内核调试功能。这也是本文选择Bochs作为首选实验环境的主要原因之一。有关Bochs调试功能的说明参见前面14.2节,这里基于Linux 0.11内核来说明Windows环境下Bochs系统调试操作的基本方法。

    14.8.1 运行Bochs调试程序
    我们假设Bochs系统已被安装在目录“C:\Program Files\Bochs-2.1.1\”中,并且Linux 0.11系统的Bochs配置文件名称是bochsrc-hd.bxrc。现在在包含内核Image文件的目录下建立一个简单的批处理文件run.bat,其内容如下:

    "C:\Program Files\Bochs-2.1.1\bochsdbg" -q -f bochsrc-hd.bxrc

    其中bochsdbg是Bochs系统的调试执行程序。运行该批处理命令即可进入调试环境。此时Bochs的主显示窗口空白,而控制窗口将显示以下类似内容:

    C:\Documents and Settings\john1\桌面\Linux-0.11>"C:\Program Files\Bochs-2.1.1\bo
    chsdbg" -q -f bochsrc-hd.bxrc
    ========================================================================
                          Bochs x86 Emulator 2.1.1
                              February 08, 2004
    ========================================================================
    00000000000i[     ] reading configuration from bochsrc-hd.bxrc
    00000000000i[     ] installing win32 module as the Bochs GUI
    00000000000i[     ] Warning: no rc file specified.
    00000000000i[     ] using log file bochsout.txt
    Next at t=0
    (0) context not implemented because BX_HAVE_HASH_MAP=0
    [0x000ffff0] f000:fff0 (unk. ctxt): jmp f000:e05b             ; ea5be000f0
    <bochs:1>

    此时Bochs调试系统已经准备好开始运行,CPU执行指针已指向ROM BIOS中地址0x000fffff0处的指令处。其中'<bochs:1>'是命令输入提示符,其中的数字表示当前的命令序列号。在命令提示符'<bochs:1>'后面键入'help'命令,可以列出调试系统的基本命令。若要了解某个命令的具体使用方法,可以键入'help'命令并且后面跟随一个用单引号括住的具体命令,例如:“help 'vbreak'”,如下面所示。

    <bochs:1> help
    help - show list of debugger commands
    help 'command'- show short command description
    -*- Debugger control -*-
       help, q|quit|exit, set, instrument, show, trace-on, trace-off,
       record, playback, load-symbols, slist
    -*- Execution control -*-
       c|cont, s|step|stepi, p|n|next, modebp
    -*- Breakpoint management -*-
       v|vbreak, lb|lbreak, pb|pbreak|b|break, sb, sba, blist,
       bpe, bpd, d|del|delete
    -*- CPU and memory contents -*-
       x, xp, u|disas|disassemble, r|reg|registers, setpmem, crc, info, dump_cpu,
       set_cpu, ptime, print-stack, watch, unwatch, ?|calc
    <bochs:2> help 'vbreak'
    help vbreak
    vbreak seg:off - set a virtual address instruction breakpoint
    <bochs:3>

    为了让Bochs直接模拟执行到Linux的引导启动程序开始处,我们可以先使用断点命令在0x7c00处设置一个断点,然后让系统连续运行到0x7c00处停下来。执行的命令序列如下:

    <bochs:3> vbreak 0x0000:0x7c00
    <bochs:4> c
    (0) Breakpoint 1, 0x7c00 (0x0:0x7c00)
    Next at t=4409138
    (0) [0x00007c00] 0000:7c00 (unk. ctxt): mov ax, 0x7c0             ; b8c007
    <bochs:5>

    此时,CPU执行到boot.s程序开始处的第1条指令处,Bochs主窗口将显示出“Boot From floppy...”等一些信息。现在,我们可以利用单步执行命令's'或'n'(不跟踪进入子程序)来跟踪调试程序了。在调试时可以使用Bochs的断点设置命令、反汇编命令、信息显示命令等来辅助我们的调试操作。下面是一些常用命令的示例:

    <bochs:8> u /10                                    # 反汇编从当前地址开始的10条指令。
    00007c00: (                    ): mov ax, 0x7c0             ; b8c007
    00007c03: (                    ): mov ds, ax                ; 8ed8
    00007c05: (                    ): mov ax, 0x9000            ; b80090
    00007c08: (                    ): mov es, ax                ; 8ec0
    00007c0a: (                    ): mov cx, 0x100             ; b90001
    00007c0d: (                    ): sub si, si                ; 29f6
    00007c0f: (                    ): sub di, di                ; 29ff
    00007c11: (                    ): rep movs word ptr [di], word ptr [si] ; f3a5
    00007c13: (                    ): jmp 9000:0018             ; ea18000090
    00007c18: (                    ): mov ax, cs                ; 8cc8
    <bochs:9> info r                                  # 查看当前CPU寄存器的内容
    eax            0xaa55           43605
    ecx            0x110001         1114113
    edx            0x0              0
    ebx            0x0              0
    esp            0xfffe           0xfffe
    ebp            0x0              0x0
    esi            0x0              0
    edi            0xffe4           65508
    eip            0x7c00           0x7c00
    eflags         0x282            642
    cs             0x0              0
    ss             0x0              0
    ds             0x0              0
    es             0x0              0
    fs             0x0              0
    gs             0x0              0
    <bochs:10> print-stack                            # 显示当前堆栈的内容
      0000fffe [0000fffe]  0000
      00010000 [00010000]  0000
      00010002 [00010002]  0000
      00010004 [00010004]  0000
      00010006 [00010006]  0000
      00010008 [00010008]  0000
      0001000a [0001000a]  0000
    ...
    <bochs:11> dump_cpu                               # 显示CPU中的所有寄存器和状态值。
    eax:0xaa55
    ebx:0x0
    ecx:0x110001
    edx:0x0
    ebp:0x0
    esi:0x0
    edi:0xffe4
    esp:0xfffe
    eflags:0x282
    eip:0x7c00
    cs:s=0x0, dl=0xffff, dh=0x9b00, valid=1
    ss:s=0x0, dl=0xffff, dh=0x9300, valid=7
    ds:s=0x0, dl=0xffff, dh=0x9300, valid=1
    es:s=0x0, dl=0xffff, dh=0x9300, valid=1
    fs:s=0x0, dl=0xffff, dh=0x9300, valid=1
    gs:s=0x0, dl=0xffff, dh=0x9300, valid=1
    ldtr:s=0x0, dl=0x0, dh=0x0, valid=0
    tr:s=0x0, dl=0x0, dh=0x0, valid=0
    gdtr:base=0x0, limit=0x0
    idtr:base=0x0, limit=0x3ff
    dr0:0x0
    dr1:0x0
    dr2:0x0
    dr3:0x0
    dr6:0xffff0ff0
    dr7:0x400
    tr3:0x0
    tr4:0x0
    tr5:0x0
    tr6:0x0
    tr7:0x0
    cr0:0x60000010
    cr1:0x0
    cr2:0x0
    cr3:0x0
    cr4:0x0
    inhibit_mask:0
    done
    <bochs:12>

    由于Linux 0.11内核的32位代码是从绝对物理地址0处开始存放的,因此若想直接执行到32位代码开始处,即head.s程序开始处,我们可以在线性地址0x0000处设置一个断点并运行命令'c'执行到那个位置处。
    另外,当直接在命令提示符下打回车键时会重复执行上一个命令;按向上方向键会显示上一命令。其他命令的使用方法请参考'help'命令。

    14.8.2 定位内核中的变量或数据结构
    在编译内核时会产生一个system.map文件。该文件列出了内核Image (bootimage)文件中全局变量和各个模块中的局部变量的偏移地址位置。在内核编译完成后可以使用前面介绍的文件导出方法把system.map文件抽取到主机环境(windows)中。有关system.map文件的详细功能和作用请参见2.10.3节。system.map样例文件中的部分内容见如下所示。利用这个文件,我们可以在Bochs调试系统中快速地定位某个变量或跳转到指定的函数代码处。

    ...
    Global symbols:

     _dup: 0x16e2c
     _nmi: 0x8e08
     _bmap: 0xc364
     _iput: 0xc3b4
     _blk_dev_init: 0x10ed0
     _open: 0x16dbc
     _do_execve: 0xe3d4
     _con_init: 0x15ccc
     _put_super: 0xd394
     _sys_setgid: 0x9b54
     _sys_umask: 0x9f54
     _con_write: 0x14f64
     _show_task: 0x6a54
     _buffer_init: 0xd1ec
     _sys_settimeofday: 0x9f4c
     _sys_getgroups: 0x9edc
    ...

    同样,由于Linux 0.11内核的32位代码是从绝对物理地址0处开始存放的,system.map中全局变量的偏移位置值就是CPU中线性地址位置,因此我们可以直接在感兴趣的变量或函数名位置处设置断点,并让程序连续执行到指定的位置处。例如若我们想调试函数buffer_init(),那么从system.map文件中可以知道它位于0xd1ec处。此时我们可以在该处设置一个线性地址断点,并执行命令'c'让CPU执行到这个指定的函数开始处,见如下所示。

    <bochs:12> lb 0xd1ec                              # 设置线性地址断点。
    <bochs:13> c                                      # 连续执行。
    (0) Breakpoint 2, 0xd1ec in ?? ()
    Next at t=16689666
    (0) [0x0000d1ec] 0008:0000d1ec (unk. ctxt): push ebx                  ; 53
    <bochs:14> n                                      # 执行下一指令。
    Next at t=16689667
    (0) [0x0000d1ed] 0008:0000d1ed (unk. ctxt): mov eax, dword ptr ss:[esp+0x8] ; 8b442408
    <bochs:15> n                                      # 执行下一指令。
    Next at t=16689668
    (0) [0x0000d1f1] 0008:0000d1f1 (unk. ctxt): mov edx, dword ptr [ds:0x19958] ; 8b1558990100
    <bochs:16>

    程序调试是一种技能,需要多练习才能熟能生巧。上面介绍的一些基本命令需要组合在一起使用才能灵活地观察到内核代码执行的整体环境情况。

  • 相关阅读:
    [CTF]ROT5/13/18/47位移密码
    [CTF]维吉尼亚密码(维基利亚密码)
    [CTF]ACSII码
    kxtj2-1009驱动总结
    DC/DCLT1767EMS8E-5芯片
    485通信总结
    安装pymssql报错
    安装mysqlclient报错
    ERROR 1878
    Win10安装gopls
  • 原文地址:https://www.cnblogs.com/smwikipedia/p/1364201.html
Copyright © 2020-2023  润新知