zoukankan      html  css  js  c++  java
  • 通过Bochs分析Lilo启动Linux内核的过程

    1. Bochs调试

    参考:http://www.cnblogs.com/long123king/p/3414884.html

    http://bochs.sourceforge.net/cgi-bin/topper.pl?name=New+Bochs+Documentation&url=http://bochs.sourceforge.net/doc/docbook

    类似的文章:http://www.cnblogs.com/wanghj-dz/archive/2011/05/12/2044862.html

    关于IO端口的列表:

    http://bochs.sourceforge.net/techspec/PORTS.LST

    http://wiki.osdev.org/I/O_Ports

    2. 环境简单描述

    通过VS2012+Bochs进行Bochs源码以及API两个级别的调试。

    Linux的版本, TinyCore

    3. 调试步骤

    初始断点:

       1: [0x0000fffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0
       2: <bochs:1> u /10
       3: 000ffff0: (                    ): jmp far f000:e05b         ; ea5be000f0
       4: 000ffff5: (                    ): xor word ptr ds:[bx+di], si ; 3131
       5: 000ffff7: (                    ): das                       ; 2f
       6: 000ffff8: (                    ): xor word ptr ds:[bx+di], si ; 3131
       7: 000ffffa: (                    ): das                       ; 2f
       8: 000ffffb: (                    ): xor word ptr ss:[bp+si], si ; 3132
       9: 000ffffd: (                    ): add ah, bh                ; 00fc
      10: 000fffff: (                    ): retf                      ; cb
      11: 00100000: (                    ): int 0xcd                  ; cdcd
      12: 00100002: (                    ): int 0xcd                  ; cdcd

    可以看到,初始断点在0xFFFF0处断了下来,这里对应的是BIOS程序。

    然后会跳转到f000:e05b(000fe05b)处执行,看一下这里的代码

       1: <bochs:8> u /16
       2: 000fe05b: (                    ): xor ax, ax                ; 31c0
       3: 000fe05d: (                    ): out 0x0d, al              ; e60d
       4: 000fe05f: (                    ): out 0xda, al              ; e6da
       5: 000fe061: (                    ): mov al, 0xc0              ; b0c0
       6: 000fe063: (                    ): out 0xd6, al              ; e6d6
       7: 000fe065: (                    ): mov al, 0x00              ; b000
       8: 000fe067: (                    ): out 0xd4, al              ; e6d4
       9: 000fe069: (                    ): mov al, 0x0f              ; b00f
      10: 000fe06b: (                    ): out 0x70, al              ; e670
      11: 000fe06d: (                    ): in al, 0x71               ; e471
      12: 000fe06f: (                    ): mov bl, al                ; 88c3
      13: 000fe071: (                    ): mov al, 0x0f              ; b00f
      14: 000fe073: (                    ): out 0x70, al              ; e670
      15: 000fe075: (                    ): mov al, 0x00              ; b000
      16: 000fe077: (                    ): out 0x71, al              ; e671
      17: 000fe079: (                    ): mov al, bl                ; 88d8

    涉及的端口有0x0d, 0xd4, 0xd6, 0xda,这些都是DMA处理器的端口;还有0x70, 0x71,这是读写CMOS RAM的端口,这里是读取RTC时钟。

    再跳转

       1: (0) [0x0000000fe0a3] f000:e0a3 (unk. ctxt): cli                       ; fa
       2: <bochs:32> u /10
       3: 000fe0a3: (                    ): cli                       ; fa
       4: 000fe0a4: (                    ): mov ax, 0xfffe            ; b8feff
       5: 000fe0a7: (                    ): mov sp, ax                ; 89c4
       6: 000fe0a9: (                    ): xor ax, ax                ; 31c0
       7: 000fe0ab: (                    ): mov ds, ax                ; 8ed8
       8: 000fe0ad: (                    ): mov ss, ax                ; 8ed0
       9: 000fe0af: (                    ): mov byte ptr ds:0x4b0, bl ; 881eb004
      10: 000fe0b3: (                    ): cmp bl, 0xfe              ; 80fbfe
      11: 000fe0b6: (                    ): jnz .+3                   ; 7503
      12: 000fe0b8: (                    ): jmp .-18142               ; e922b9

    这里是设置栈

    0x7c00

    我们知道,MBR里面的汇编代码会被BIOS加载到0x7c00处运行,因此我们在这里设置断点

       1: lb 0x7c00
       2: c
       1: (0) Breakpoint 1, 0x0000000000007c00 in ?? ()
       2: Next at t=73755932
       3: (0) [0x000000007c00] 0000:7c00 (unk. ctxt): cli                       ; fa
       4: <bochs:4> u /10
       5: 00007c00: (                    ): cli                       ; fa
       6: 00007c01: (                    ): jmp .+108                 ; eb6c
       7: 00007c03: (                    ): add byte ptr ds:[bx+si], al ; 0000
       8: 00007c05: (                    ): add byte ptr ds:[si+73], cl ; 004c49
       9: 00007c08: (                    ): dec sp                    ; 4c
      10: 00007c09: (                    ): dec di                    ; 4f
      11: 00007c0a: (                    ): add word ptr ds:[bx+si], ax ; 0100
      12: 00007c0c: (                    ): adc al, 0x00              ; 1400
      13: 00007c0e: (                    ): pop dx                    ; 5a
      14: 00007c0f: (                    ): add byte ptr ds:[bx+si], al ; 0000

    既然是MBR,我们可以验证一下分区表

       1: <bochs:8> x/16wx 0x7c00+446
       2: [bochs]:
       3: 0x0000000000007dbe <bogus+       0>:    0x00010100      0x31510383      0x00000011      0x00005137
       4: 0x0000000000007dce <bogus+      16>:    0x00000000      0x00000000      0x00000000      0x00000000
       5: 0x0000000000007dde <bogus+      32>:    0x00000000      0x00000000      0x00000000      0x00000000
       6: 0x0000000000007dee <bogus+      48>:    0x00000000      0x00000000      0x00000000      0x00000000

    分区表表明,只有一个分区,起始的逻辑扇区号为0x0000,包含的扇区个数的0x5137,因此该分区大小为:

    0x5137 * 0x200 / 1024 = 10395 KB = 10.4MB,与我们使用的img大小相符

       1: 2014/02/14  11:45        10,653,696 hd10meg.img

    下面我们分析MBR中的代码

       1: <bochs:13> u /20
       2: 00007c6f: (                    ): mov ax, 0x07c0            ; b8c007
       3: 00007c72: (                    ): mov ds, ax                ; 8ed8
       4: 00007c74: (                    ): mov word ptr ds:0x6a, es  ; 8c066a00
       5: 00007c78: (                    ): mov word ptr ds:0x68, si  ; 89366800
       6: 00007c7c: (                    ): mov word ptr ds:0x6c, bx  ; 891e6c00
       7: 00007c80: (                    ): mov byte ptr ds:0x6e, dl  ; 88166e00
       8: 00007c84: (                    ): mov ax, 0x8a00            ; b8008a
       9: 00007c87: (                    ): mov es, ax                ; 8ec0
      10: 00007c89: (                    ): mov cx, 0x0100            ; b90001
      11: 00007c8c: (                    ): sub si, si                ; 29f6
      12: 00007c8e: (                    ): sub di, di                ; 29ff
      13: 00007c90: (                    ): cld                       ; fc
      14: 00007c91: (                    ): rep movsw word ptr es:[di], word ptr ds:[si] ; f3a5
      15: 00007c93: (                    ): 
    jmp far 8a00:0098
             ; ea9800008a
      16: 00007c
    98
    : (                    ): cli                       ; fa
      17: 00007c99: (                    ): mov ds, ax                ; 8ed8
      18: 00007c9b: (                    ): mov es, ax                ; 8ec0
      19: 00007c9d: (                    ): mov sp, 0xb000            ; bc00b0
      20: 00007ca0: (                    ): mov ax, 0x8000            ; b80080
      21: 00007ca3: (                    ): mov ss, ax                ; 8ed0

    参考:http://blog.csdn.net/jphaoren/article/details/6954376

    rep movsw的功能是将ds:si中的一段内存拷贝到es:di中去,一共拷贝cx个word。

    而ds:0x6a, ds:0x68, ds:0x6c, ds:0x6e的值分别都是0

       1: Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       2:  
       3: 00000060   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 B8                  ?

    所以,这段代码的作用是,将0x7c00处的内存拷贝到0x8a000处,一共拷贝0x100个word:

    0x100 * sizeof(word) = 0x200 = 512Bytes=1扇区

    相当于把整个MBR都拷贝到0x8a000处。

    然后跳转指令jmp far 8a00:0098实际上就是恰好跳转到下一条指令cli处去执行。

       1: <bochs:14> lb 0x8a098
       2: <bochs:15> c
       3: (0) Breakpoint 2, 0x000000000008a098 in ?? ()
       4: Next at t=73756203
       5: (0) [0x00000008a098] 8a00:0098 (unk. ctxt): cli                       ; fa
       6: <bochs:16> u /10
       7: 0008a098: (                    ): cli                       ; fa
       8: 0008a099: (                    ): mov ds, ax                ; 8ed8
       9: 0008a09b: (                    ): mov es, ax                ; 8ec0
      10: 0008a09d: (                    ): mov sp, 0xb000            ; bc00b0
      11: 0008a0a0: (                    ): mov ax, 0x8000            ; b80080
      12: 0008a0a3: (                    ): mov ss, ax                ; 8ed0
      13: 0008a0a5: (                    ): sti                       ; fb
      14: 0008a0a6: (                    ): mov al, 0x0d              ; b00d
      15: 0008a0a8: (                    ): call .+87                 ; e85700
      16:
    0008a0ab: ( ): mov al, 0x0a ; b00a

    ax里面的值依然是0x8a00,因此,将数据段ds, es的段基址都设置成0x8a00,即MBR的起始地址;将栈段设置为0x8000:0xb000,即栈底为0x8b000,栈是向低地址方向扩展。

    传递一个0x0d作为参数,然后调用call .+87,这是新的栈设置后的第一个函数。

       1: (0) [0x00000008a0a8] 8a00:00a8 (unk. ctxt): call .+87 (0x0008a102)    ; e85700
       2: <bochs:28> x/17bx 0x8aff0
       3: [bochs]:
       4: 0x000000000008aff0 <bogus+       0>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
       5: 0x000000000008aff8 <bogus+       8>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
       6: 0x000000000008b000 <bogus+      16>:    0x00
       7: <bochs:29> s
       8: Next at t=73756212
       9: (0) [0x00000008a102] 8a00:0102 (unk. ctxt): xor bh, bh                ; 30ff
      10: <bochs:30> x/17bx 0x8aff0
      11: [bochs]:
      12: 0x000000000008aff0 <bogus+       0>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
      13: 0x000000000008aff8 <bogus+       8>:    0x00    0x00    0x00    0x00    0x00    0x00    
    0xab 0x00
      14: 0x000000000008b000 <bogus+      16>:    0x00

    可见,返回地址0x00ab,即对应着0x0008a0ab,被压到了栈中。

       1: <bochs:31> u /16
       2: 0008a102: (                    ): xor bh, bh                ; 30ff
       3: 0008a104: (                    ): mov ah, 0x0e              ; b40e
       4: 0008a106: (                    ): int 0x10                  ; cd10
       5: 0008a108: (                    ): ret                       ; c3

    这个函数做了什么?

    INT 10H是BIOS系统调用指令,参考:http://en.wikipedia.org/wiki/INT_10

    这个函数的功能是“打印”al中的字符,这里通过al传入的是0x0a,因此是打印了一个‘ ’字符。

    再看下面的代码

       1: <bochs:38> u /10
       2: 0008a0ab: (                    ): mov al, 0x0a              ; b00a
       3: 0008a0ad: (                    ): call .+82                 ; e85200
       4: 0008a0b0: (                    ): mov al, 0x4c              ; b04c
       5: 0008a0b2: (                    ): call .+77                 ; e84d00
       6: 0008a0b5: (                    ): mov si, 0x0034            ; be3400
       7: 0008a0b8: (                    ): mov bx, 0x1000            ; bb0010

    又分别打印了‘ ’‘L’两个字符。

    看一下屏幕

    image

    真的打出了L字符。

    我们不能再继续深入了,因为我们关心的是Linux内核的加载,而不是Lilo的代码实现。

  • 相关阅读:
    CQUOJ 10819 MUH and House of Cards
    CQUOJ 9920 Ladder
    CQUOJ 9906 Little Girl and Maximum XOR
    CQUOJ 10672 Kolya and Tandem Repeat
    CQUOJ 9711 Primes on Interval
    指针试水
    Another test
    Test
    二分图匹配的重要概念以及匈牙利算法
    二分图最大匹配
  • 原文地址:https://www.cnblogs.com/long123king/p/3549267.html
Copyright © 2011-2022 走看看