操作系统真象-保护模式
Column: September 12, 2021
Tags: learning experience, 操作系统
一点写汇编的新知识:
可以在代码中用[bits xx]来指定生成xx位的shellcode, 如果在接触到下一个[bits xx]之前出现了不符合当前位数的内存/寄存器操作, 则会在机器码之前添加0x66(反转操作数大小前缀)和0x67(反转寻址方式签字), 然后汇编如何在机器码方面体现操作数的位数不同就交给硬件吧!
如何进入保护模式:
- 打开A20地址线
- 加载gdt
- 将cr0的pe位置1
PS. 这三个步骤可以不顺序, 不连续, 且具体实现方式可以用好多不同的小步骤
GDT:
GDT全局描述表, 为loader加载到内存中, 其作用为记录内存段的属性, 使得保护模式下的地址分段赋予权限等, 每个表项为64位: 记录了有段基址, 段界限(这俩在表内居然不是连续的位…为了兼容的说), G(颗粒度, 段界限的单位: byte/4kb ), S(系统段/非系统段), TYPE(细分的段类型), AVL(用户的操作系统可以自由使用这个段), D/B(也是为了兼容的玩意), L(是否64位代码段), 前面这种东西, 谁记得住啊(悲
GDT表索引范围为0-8192, 其中0号不可用, GDT的地址被一个特殊寄存器GDTR(48位)记录, 而且在保护模式中还可以重新加载GDT, 因为可能要挪个位置2333
LDT局部描述表, 基本用不上的玩意就懒得看了
选择子
之前16位的段寄存器虽然现在不用用*4的偏移了, 所以也不直接记录段地址了, 现在用来记录选择子, 0-1位记录请求的特权级, 第2位判断GDT/LDT, 剩下的是在描述中的索引值
个人感觉上面的玩意知道概念就行, 真得自己打工实操的时候再查字典吧2333
代码:
mbr: 这玩意, sector count改成4就行了
boot.inc: 书上那么多0属实顶不住, 疯狂数错
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
DESC_G_4K equ 1_000_000_000_000_000_000_000_00b
DESC_D_32 equ 1_000_000_000_000_000_000_000_0b
DESC_L equ 0_000_000_000_000_000_000_000b
DESC_AVL equ 0_000_000_000_000_000_000_00b
DESC_LIMIT_CODE2 equ 1111_0000_0000_0000_0000b
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0000_000_000_000_000_000b
DESC_P equ 1_000_000_000_000_000b
DESC_DPL_0 equ 00_000_000_000_000_0b
DESC_DPL_1 equ 01_000_000_000_000_0b
DESC_DPL_2 equ 10_000_000_000_000_0b
DESC_DPL_3 equ 11_000_000_000_000_0b
DESC_S_CODE equ 1_000_000_000_000b
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys equ 0_000_000_000_000b
DESC_TYPE_CODE equ 1000_0000_0000b
DESC_TYPE_DATA equ 0010_0000_0000b
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + \
DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \
DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
loader.S:
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
GDT_BASE: dd 0x00000000
dd 0x00000000
CODE_DESC: dd 0x0000ffff
dd DESC_CODE_HIGH4
DATA_STACK_DESC: dd 0x0000ffff
dd DESC_DATA_HIGH4
VIDEO_DESC: dd 0x80000007
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0
SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0
SELECTOR_DATA equ (0X0002<<3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0X0003<<3) + TI_GDT + RPL0
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
loadermsg db '2 loader in real', 0x0a
loader_start:
mov sp, LOADER_BASE_ADDR
mov bp, loadermsg
mov cx, 18
mov ax, 0x1301
mov bx, 0x001f
mov dx, 0x1600
int 0x10
;ready to go to protect
;open A20
in al, 0x92
or al, 0000_0010B
out 0x92, al
;load GDT
lgdt [gdt_ptr]
;change cr0
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
jmp dword SELECTOR_CODE:p_mode_start
[bits 32]
p_mode_start:
mov ax, SELECTOR_DATA
mov ds, ax
mov es, ax
mov ss, ax
mov esp, LOADER_STACK_TOP
mov ax, SELECTOR_VIDEO
mov gs, ax
mov byte [gs:160], 'P'
jmp $
坑:
说来好玩, 我一开始dd loader到hd60M之后突然发现loader_start中的代码没有写入硬盘中, 以为是汇编写错了, 然后才发现dd的count也要写到4, 要不然字节数不足以支持写完所有内容
偶对,突然还想起来之前曾经为16位的实模式下写入32位寄存器而困惑,其实是这样的,俺们实际后面运行的是32位环境,而它硬件什么的向下兼容,导致可以在16位实模式下使用32位的寄存器