16位实模式切换32位保护模式过程详解
本文为看雪论坛精华文章
看雪论坛作者ID:34r7hm4n
1
实模式和保护模式
2
实模式到保护模式的切换
1、屏蔽中断
2、初始化全局描述符表(GDT)
3、将CR0寄存器最低位置1
4、执行远跳转
5、初始化段寄存器和栈指针
1、屏蔽中断
cli
2、初始化GDT
Pr: Present bit. This must be 1 for all valid selectors.
Privl: Privilege, 2 bits. Contains the ring level, 0 = highest (kernel), 3 = lowest (user applications).
S: Descriptor type. This bit should be set for code or data segments and should be cleared for system segments (eg. a Task State Segment)
Ex: Executable bit. If 1 code in this segment can be executed, ie. a code selector. If 0 it is a data selector.
DC: Direction bit/Conforming bit.
If 1 code in this segment can be executed from an equal or lower privilege level. For example, code in ring 3 can far-jump to conforming code in a ring 2 segment. The privl-bits represent the highest privilege level that is allowed to execute the segment. For example, code in ring 0 cannot far-jump to a conforming code segment with privl==0x2, while code in ring 2 and 3 can. Note that the privilege level remains the same, ie. a far-jump form ring 3 to a privl==2-segment remains in ring 3 after the jump.
If 0 code in this segment can only be executed from the ring set in privl.
RW: Readable bit/Writable bit.
Ac: Accessed bit. Just set to 0. The CPU sets this to 1 when the segment is accessed.
Gr: Granularity bit,粒度位,如果Gr置0则段界限limit是以字节为单位的,即段的大小为1B到1MB,如果Gr置0则以4KB为单位,段的大小从4KB到4GB。
Sz: Size bit,置0表示该段的偏移地址或操作数是16位的,比如说代码段该位置0,则取指令时会使用16位的指令指针寄存器IP来取指令,而栈段在进行栈操作时,会使用SP寄存器;置1表示使用的偏移地址或操作数是32位的,使用EIP和ESP寄存器。
gdt_start:
; 第一个描述符必须是空描述符
gdt_null:
dd 0
dd 0
gdt_end:
gdt_start:
; 第一个描述符必须是空描述符
gdt_null:
dd 0
dd 0
; 代码段描述符
gdt_code:
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10011010b ; Access Byte
db 11001111b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
; 数据段描述符
gdt_data:
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10010010b ; Access Byte
db 11001111b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
gdt_end:
代码段:
Access byte 10011010b
Pr = 1 ; 标记为有效的段
Privl = 0 ; ring3权限
S = 1 ; 代码段或数据段
Ex = 1 ; 可执行,即代码段
DC = 0 ; 标记该段只能在Privl权限(即ring3权限)下执行
RW = 1 ; 可读
Ac = 0 ; 该段的内容是否被修改,默认为0,由CPU置位
Flags 1100b
Gr = 1 ; 粒度为4KB
Sz = 1 ; 32位模式
数据段:
Access byte 10010010b
Pr = 1 ; 标记为有效的段
Privl = 0 ; ring3权限
S = 1 ; 代码段或数据段
Ex = 0 ; 不可执行,即数据段
DC = 0 ; 标记该段只能在Privl权限(即ring3权限)下执行
RW = 1 ; 可写
Ac = 0 ; 该段的内容是否被修改,默认为0,由CPU置位
Flags 1100b
Gr = 1 ; 粒度为4KB
Sz = 1 ; 32位模式
gdt_start:
; 第一个描述符必须是空描述符
gdt_null:
dd 0
dd 0
; 代码段描述符
gdt_code:
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10011010b ; Access Byte
db 11001111b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
; 数据段描述符
gdt_data:
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10010010b ; Access Byte
db 11001111b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
; 栈段描述符
gdt_stack:
dw 0x7c00 ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10010010b ; Access Byte
db 01000000b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
gdt_end:
栈段:
Access byte 10010010b
Pr = 1 ; 标记为有效的段
Privl = 0 ; ring3权限
S = 1 ; 代码段或数据段
Ex = 0 ; 不可执行,即数据段
DC = 0 ; 标记该段只能在Privl权限(即ring3权限)下执行
RW = 1 ; 可写
Ac = 0 ; 该段的内容是否被修改,默认为0,由CPU置位
Flags 1100b
Gr = 0 ; 粒度为1B
Sz = 1 ; 32位模式
; GDT descriptior
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; Size of our GDT, always less one of the true size
dd gdt_start ; Start address of our GDT
lgdt [gdt_descriptor]
3、将CR0最低位置1
; 把 cr0 的最低位置为 1,开启保护模式
mov eax, cr0
or eax, 0x1
mov cr0, eax
4、执行远跳转
jmp 08h:PModeMain
[bits 32]
PModeMain:
5、初始化段寄存器和栈指针
[bits 32]
PModeMain:
mov ax, 0x10 ; 将数据段寄存器ds和附加段寄存器es置为0x10
mov ds, ax
mov es, ax
mov fs, ax ; fs和gs寄存器由操作系统使用,这里统一设成0x10
mov gs, ax
mov ax, 0x18 ; 将栈段寄存器ss置为0x18
mov ss, ax
mov ebp, 0x7c00 ; 现在栈顶指向 0x7c00
mov esp, ebp
3
测试:32位保护模式下的打印
; 打印字符串
; @param 被打印的字符串
print:
push ebp
mov ebp, esp
mov ebx, [ebp+8] ; [ebp+8]即传入的字符串参数
xor ecx, ecx
mov ah, 0x0f ; ah为打印的颜色属性,0x0f为白字黑底
mov edx, 0xb8000 ; 显存的地址
loop1_begin:
mov al, [ebx] ; al为被打印的字符
cmp al, 0 ; 若al为0,结束打印
je loop1_end
mov [edx], ax ; 向显存中写入字符及其颜色属性(2字节)
inc ebx
add edx, 2
jmp loop1_begin
loop1_end:
mov esp, ebp
pop ebp
ret
PModeTest:
push TEST_STRING ; 被打印字符的地址
call print
PModePause:
hlt
jmp PModePause
TEST_STRING db 'We are in protected mode!', 0
4
完整代码
org 09000h
cli ; 屏蔽中断
lgdt [gdt_descriptor] ; 初始化GDT
; 把 cr0 的最低位置为 1,开启保护模式
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp 08h:PModeMain
[bits 32]
PModeMain:
mov ax, 0x10 ; 将数据段寄存器ds和附加段寄存器es置为0x10
mov ds, ax
mov es, ax
mov fs, ax ; fs和gs寄存器由操作系统使用,这里统一设成0x10
mov gs, ax
mov ax, 0x18 ; 将栈段寄存器ss置为0x18
mov ss, ax
mov ebp, 0x7c00 ; 现在栈顶指向 0x7c00
mov esp, ebp
jmp PModeTest
PModeTest:
push TEST_STRING ; 被打印字符的地址
call print
PModePause:
hlt
jmp PModePause
; 打印字符串
; @param 被打印的字符串
print:
push ebp
mov ebp, esp
mov ebx, [ebp+8] ; [ebp+8]即传入的字符串参数
xor ecx, ecx
mov ah, 0x0f ; ah为打印的颜色属性,0x0f为白字黑底
mov edx, 0xb8000 ; 显存的地址
loop1_begin:
mov al, [ebx] ; al为被打印的字符
cmp al, 0 ; 若al为0,结束打印
je loop1_end
mov [edx], ax ; 向显存中写入字符及其颜色属性(2字节)
inc ebx
add edx, 2
jmp loop1_begin
loop1_end:
mov esp, ebp
pop ebp
ret
TEST_STRING db 'We are in protected mode!', 0
gdt_start:
; 第一个描述符必须是空描述符
gdt_null:
dd 0
dd 0
; 代码段描述符
gdt_code:
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10011010b ; Access Byte
db 11001111b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
; 数据段描述符
gdt_data:
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10010010b ; Access Byte
db 11001111b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
; 栈段描述符
gdt_stack:
dw 0x7c00 ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10010010b ; Access Byte
db 01000000b ; Flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
gdt_end:
; GDT descriptior
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; Size of our GDT, always less one of the true size
dd gdt_start ; Start address of our GDT
5
A20地址线
PModeTest:
mov edi, 0x112345
mov esi, 0x012345
mov [esi], esi
mov [edi], edi
mov eax, [esi]
cmp eax, edi
je A20_DISABLE
push aA20_ENABLE
call print
jmp PModePause
A20_DISABLE:
push aA20_DISABLE
call print
PModePause:
hlt
jmp PModePause
aA20_ENABLE db 'A20 Enable', 0
aA20_DISABLE db 'A20 Disable', 0
in al, 92h
or al, 00000010b
out 92h, al
看雪ID:34r7hm4n
https://bbs.pediy.com/user-home-910514.htm
官网:https://www.bagevent.com/event/6334937
# 往期推荐
3.CVE-2017-17215(华为HG532远程命令执行漏洞)复现学习
6. CVE-2012-3569 VMware OVF Tool格式化字符串漏洞分析
球分享
球点赞
球在看
点击“阅读原文”,了解更多!