orange's一个操作系统的实现--学习过程 第三章:a代码反汇编分析

本文通过分析汇编语言编译的二进制程序,详细解释了如何利用bochs调试器逐步执行指令,并观察全局描述符表(GDT)的变化过程。文章重点介绍了如何设置和加载GDT,以及如何从实模式切换到保护模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为汇编语言编译的二进制程序在内存中和本身文件是一样的,把汇编出来的pmtest1.bin文件的16进制复制出来分析一下
(区别是文件只能看到静态结果,无法执行)

可以通过bochs调试模式,一步一步的执行,观察gdt数据结构的变化。
虽然是CPU支持保护模式的gdt方式,但是需要程序自己构造数据结构保存数据,程序自己动态的使用自己的数据。


--汇编出来的二进制文件pmtest1.bin文件
00000000h: E9 21 00 00 00 00 00 00 00 00 00 00 14 00 00 00 ; ?..............
00000010h: 00 98 40 00 FF FF 00 80 0B 92 00 00 17 00 00 00 ; .楡..€.?.....
00000020h: 00 00 00 00 8C C8 8E D8 8E C0 8E D0 BC 00 01 66 ; ....屓庁幚幮?.f
00000030h: 31 C0 8C C8 66 C1 E0 04 66 05 80 7C 00 00 A3 0E ; 1缹萬拎.f.€|..?
00000040h: 7C 66 C1 E8 10 A2 10 7C 88 26 13 7C 66 31 C0 8C ; |f凌.?|?.|f1缹
00000050h: D8 66 C1 E0 04 66 05 04 7C 00 00 66 A3 1E 7C 0F ; 豧拎.f..|..f?|.
00000060h: 01 16 1C 7C FA E4 92 0C 02 E6 92 0F 20 C0 66 83 ; ...|?.鎾. 纅?
00000070h: C8 01 0F 22 C0 66 EA 00 00 00 00 08 00 00 00 00 ; ?."纅?........
00000080h: 66 B8 10 00 8E E8 BF 7E 07 00 00 B4 0C B0 50 65 ; f?.庤縹...?癙e
00000090h: 66 89 07 EB FE                                  ; f?膻


----------使用bochs调试模式进行反汇编
--前3个字节是一个jmp指令  
00007c00: (                    ): jmp .+33                  ; e92100
E9 21 00 
--多了一个字节00 不知道为什么    ----------因为段是4字节对齐的即段的长度是4字节的倍数。NASM对于每个SECTION,默认按4字节对齐。 
--对应源码
org    07c00h
    jmp    LABEL_BEGIN

--bochs调试
<bochs:1> b 0x7c00
<bochs:2> c
(0) Breakpoint 1, 0x0000000000007c00 in ?? ()
Next at t=65538738
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): jmp .+33 (0x00007c24)     ; e92100
<bochs:3> u /16 0x7c00
00007c00: (                    ): jmp .+33                  ; e92100
00007c03: (                    ): add byte ptr ds:[bx+si], al ; 0000
00007c05: (                    ): add byte ptr ds:[bx+si], al ; 0000
00007c07: (                    ): add byte ptr ds:[bx+si], al ; 0000

--3个gdt(每个8个字节)
00 00 00 00 00 00 00 00 
14 00 00 00 00 98 40 00
FF FF 00 80 0B 92 00 00

<bochs:8> x /24bx  0x7c04
[bochs]:
0x0000000000007c04 <bogus+       0>:    0x00    0x00    0x00    0x00    0x00  0x00    0x00    0x00
0x0000000000007c0c <bogus+       8>:    0x14    0x00    0x00    0x00    0x00  0x98    0x40    0x00
0x0000000000007c14 <bogus+      16>:    0xff    0xff    0x00    0x80    0x0b  0x92    0x00    0x00

--源码
LABEL_GDT:           Descriptor       0,                0, 0           ; 空描述符
LABEL_DESC_CODE32:     Descriptor       0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_VIDEO:      Descriptor 0B8000h,           0ffffh, DA_DRW         ; 显存首地址
; GDT 结束                          

DA_32        EQU    4000h    ; 32 位段
DA_C        EQU    98h    ; 存在的只执行代码段属性值
DA_DRW        EQU    92h    ; 存在的可读写数据段属性值


; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
    dw    %2 & 0FFFFh                ; 段界限1
    dw    %1 & 0FFFFh                ; 段基址1
    db    (%1 >> 16) & 0FFh            ; 段基址2
    dw    ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)    ; 属性1 + 段界限2 + 属性2
    db    (%1 >> 24) & 0FFh            ; 段基址3
%endmacro ; 共 8 字节


; 图示二

; 高地址………………………………………………………………………低地址; |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0    |
; |7654321076543210765432107654321076543210765432107654321076543210|    <- 共 8 字节
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24┃   (见下图)   ┃     段基址(23..0)    ┃ 段界限(15..0)┃
; ┃      ┃              ┃                      ┃              ┃
; ┃ 基址2┃③│②│    ①  ┃基址1b │   基址1a     ┃    段界限1   ┃
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; ┃   %6 ┃  %5  ┃  %4  ┃  %3  ┃     %2       ┃       %1     ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
;         │                \_________
;         │                          \__________________
;         │                                             \________________________________________________
;         │                                                                                              \
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;         ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃
;         ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
;         ┃ G  ┃ D  ┃ 0  ┃ AVL┃   段界限 2 (19..16)  ┃  P ┃   DPL    ┃ S  ┃       TYPE           ┃
;         ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
;         ┃      ③: 属性 2      ┃    ②: 段界限 2      ┃                   ①: 属性1                  ┃
;         ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
;       高地址                                                                                          低地址
;
;
--文件内容
17 00 00 00 00 00
--源码
GdtLen        equ    $ - LABEL_GDT    ; GDT长度
GdtPtr        dw    GdtLen - 1    ; GDT界限 ==23
        dd    0        ; GDT基地址


--从开始段已经过去34个字节  有2个字节的00  ----------因为每个段都是4字节对齐的即段的长度是4字节的倍数
00 00 

--已经过去36个字节即0x24个字节 反编译一下   是第一个指令跳转过来的地方
<bochs:23> u /16 0x7c24  
00007c24: (                    ): mov ax, cs                ; 8cc8
00007c26: (                    ): mov ds, ax                ; 8ed8
00007c28: (                    ): mov es, ax                ; 8ec0
00007c2a: (                    ): mov ss, ax                ; 8ed0
00007c2c: (                    ): mov sp, 0x0100            ; bc0001

--源码
[SECTION .s16]
[BITS    16]
LABEL_BEGIN:
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, 0100h

--二进制文件内容
8C C8 8E D8 8E C0 8E D0 BC 00 01 

--从0x2f个字节 反编译一下
<bochs:12> u /16 0x7c2f
00007c2f: (                    ): xor eax, eax              ; 6631c0
00007c32: (                    ): mov ax, cs                ; 8cc8
00007c34: (                    ): shl eax, 0x04             ; 66c1e004
00007c38: (                    ): add eax, 0x00007c80       ; 6605807c0000
00007c3e: (                    ): mov word ptr ds:0x7c0e, ax ; a30e7c
00007c41: (                    ): shr eax, 0x10             ; 66c1e810
00007c45: (                    ): mov byte ptr ds:0x7c10, al ; a2107c
00007c48: (                    ): mov byte ptr ds:0x7c13, ah ; 8826137c


--源码
    ; 初始化 32 位代码段描述符
    xor    eax, eax
    mov    ax, cs
    shl    eax, 4
    add    eax, LABEL_SEG_CODE32
    mov    word [LABEL_DESC_CODE32 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE32 + 4], al
    mov    byte [LABEL_DESC_CODE32 + 7], ah


--二进制文件内容
66 31 C0 8C C8 66 C1 E0 04 66 05 80 7C 00 00 A3 0E 
7C 66 C1 E8 10 A2 10 7C 88 26 13 7C 

 

--从0x4c个字节 反编译一下
<bochs:13> u /16 0x7c4c
00007c4c: (                    ): xor eax, eax              ; 6631c0
00007c4f: (                    ): mov ax, ds                ; 8cd8
00007c51: (                    ): shl eax, 0x04             ; 66c1e004
00007c55: (                    ): add eax, 0x00007c04       ; 6605047c0000
00007c5b: (                    ): mov dword ptr ds:0x7c1e, eax ; 66a31e7c
00007c5f: (                    ): lgdt ds:0x7c1c            ; 0f01161c7c
00007c64: (                    ): cli                       ; fa
00007c65: (                    ): in al, 0x92               ; e492
00007c67: (                    ): or al, 0x02               ; 0c02
00007c69: (                    ): out 0x92, al              ; e692
00007c6b: (                    ): mov eax, cr0              ; 0f20c0
00007c6e: (                    ): or eax, 0x00000001        ; 6683c801
00007c72: (                    ): mov cr0, eax              ; 0f22c0
00007c75: (                    ): jmpf 0x0008:00000000      ; 66ea000000000800


--源码
    ; 为加载 GDTR 作准备
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_GDT        ; eax <- gdt 基地址
    mov    dword [GdtPtr + 2], eax    ; [GdtPtr + 2] <- gdt 基地址

    ; 加载 GDTR
    lgdt    [GdtPtr]

    ; 关中断
    cli

    ; 打开地址线A20
    in    al, 92h
    or    al, 00000010b
    out    92h, al

    ; 准备切换到保护模式
    mov    eax, cr0
    or    eax, 1
    mov    cr0, eax

    ; 真正进入保护模式
    jmp    dword SelectorCode32:0    ; 执行这一句会把 SelectorCode32 装入 cs,
                    ; 并跳转到 Code32Selector:0  处
; END of [SECTION .s16]
--二进制文件内容
66 31 C0 8C 
D8 66 C1 E0 04 66 05 04 7C 00 00 66 A3 1E 7C 0F 
01 16 1C 7C FA E4 92 0C 02 E6 92 0F 20 C0 66 83 
C8 01 0F 22 C0 
66 EA 00 00 00 00 08 00 

--因为每个段都是4字节对齐的  补位3个字节0x00
00 00 00 

 

 

 

--从0x80个字节 反编译一下

由于从这儿开始是32位代码段,需要主动指定反编译代码段的大小

<bochs:18> u size=32
<bochs:19> u /16  0x7c80
00007c80: (                    ): mov ax, 0x0010            ; 66b81000
00007c84: (                    ): mov gs, ax                ; 8ee8
00007c86: (                    ): mov edi, 0x0000077e       ; bf7e070000
00007c8b: (                    ): mov ah, 0x0c              ; b40c
00007c8d: (                    ): mov al, 0x50              ; b050
00007c8f: (                    ): mov word ptr gs:[edi], ax ; 65668907
00007c93: (                    ): jmp .-2                   ; ebfe
00007c95: (                    ): add byte ptr ds:[eax], al ; 0000
00007c97: (                    ): add byte ptr ds:[eax], al ; 0000
00007c99: (                    ): add byte ptr ds:[eax], al ; 0000


--源码
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS    32]

LABEL_SEG_CODE32:
    mov    ax, SelectorVideo
    mov    gs, ax            ; 视频段选择子(目的)

    mov    edi, (80 * 11 + 79) * 2    ; 屏幕第 11 行, 第 79 列。
    mov    ah, 0Ch            ; 0000: 黑底    1100: 红字
    mov    al, 'P'
    mov    [gs:edi], ax

    ; 到此停止
    jmp    $

--二进制文件内容
66 B8 10 00 8E E8 BF 7E 07 00 00 B4 0C B0 50 65 
66 89 07 EB FE      

 

 

对x86架构的处理器,业界一向是褒贬不一。但是毫无疑问的是,x86架构的处理器是迄今为止在市场上最成功的处理器。它既催生了Intel、微软这样的业界巨头,也改变了普通人们的生活。到今天,虽然有arm的异军突起,但是大部分程序员所编写的程序依然在运行在x86架构上。 虽然很多上层的程序员绕过了对CPU架构本身的理解,而直接使用高级语言进行编程,但是对CPU本身的熟悉,其实依然是所有想被称为优秀的程序员所难以绕过的一道坎。对CPU的不熟悉,实际上限制了程序员的思维方式、对程序的理解和实际解决问题的能力。 对于普通的芯片,阅读几页的说明书就可以大致理解如何让它工作。CPU是比较复杂的一种,对于比较简单的CPU架构,阅读几十页的文档也能大致熟悉。然而 x86架构的CPU的说明可不是这么简单,其手册估计有一共有四、五千页之巨。不要说理解透彻,就是从头到尾翻一遍也不是一件简单的事情。并非Intel 有意将它做得复杂,这里有历史的原因。因为这个架构的应用实在太广了,全世界有无数的软件都在它的基础之上工作。为此它自身的升级也就变得举步维艰。每次升级都不得不要兼容之前的特性。这也就导致了新旧指令层层堆积,种种特性互相兼顾,最终变成如今的一团乱麻了。 对于入门级的选手,读完那些手册可不是一件容易的事情。但是于渊的这本《orange's:一个操作系统实现》却是一条难得的终南捷径。因为要理解如何让一个芯片正常工作,最简单的办法就是从头开始去写程序让它运行起来,然后操作它做自己想做的事情。如果是平时的编程,这些下层的工作都已经有操作系统帮你做了,对理解x86架构的帮助就大为有限。如果去读那几千页的文档,不但读起来很痛苦,中间又没有多少可以实际操作的工作来帮助你温故而知新,这其中的枯燥乏味,绝对不是一般人可以忍受的了。而且更重要的手册中虽然包含了x86所有的特性,然而其中有些特性是现代操作系统根本就没有用到的。努力的去理解的话,又是吃力不讨好了。如果每个读者都可以随着这本书的介绍,去逐步的实现一个操作系统,不但这中间其乐无穷,而且实现到最后,对x86架构的理解也就不在话下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值