利用调用门实现特权级间跳转 — 实战篇
1. 引言
上一篇文章中,我们详细介绍了操作系统特权级,以及利用调用门、TSS 实现不同特权级之间的跳转。
利用调用门实现特权级间跳转 — 原理篇
经过上一篇文章对原理的深入剖析,我们从 Ring0 跳到 Ring3 再跳转回来的代码就已经呼之欲出了。
接下来,我们就先从此前代码中进入 Ring3,然后通过调用门,实现从 Ring3 到 Ring0 的跳转。
2. 原理回顾
这里我们再对特权级跳转的实现原理进行一个简要的介绍。
2.1. 从 Ring0 到 Ring3
毋庸置疑,操作系统是启动在最高特权级的 Ring0 下的,那么,在操作系统中如何实现从 Ring0 特权级跳转到应用程序所在的 Ring3 特权级的呢?
上一篇文章已经讲到,是通过先将目标地址对应的 cs、eip、ss、esp 四个寄存器压栈,模拟长调用,再通过长返回就可以实现到 Ring3 的跳转了。
2.2. 从 Ring3 到 Ring0
从 Ring3 特权级跳转到 Ring0 特权级,需要借助调用门,只要调用门描述符的 DPL 大于 CPL 与 RPL,就可以实现从低特权级跳转到门描述符所指定的高特权级目标代码了。
但是,在跳转的过程中,CPU 自动进行了栈空间的切换,每个特权级都必须对应不同的栈基址与栈指针,通过 TSS 来进行描述,只要在门描述符中指定参数数量,CPU 会自动完成栈切换以及在这一过程中的参数复制工作。
3. 创建 TSS
现在,我们已经有了用于 Ring0 的全局堆栈段,那么我们就可以直接创建 TSS 了。
3.1. TSS 内存结构定义
除了 0 级堆栈对应的堆栈基址、栈指针外,其他字段填写默认的 0 即可:
; ----------------------------- TSS --------------------------
[SECTION .tss]
ALIGN 32
[BITS 32]
LABEL_TSS:
DD 0 ; Back
DD TopOfStack ; 0 级堆栈栈顶
DD SelectorStack ; 0 级堆栈基址
DD 0 ; 1 级堆栈
DD 0 ;
DD 0 ; 2 级堆栈
DD 0 ;
DD 0 ; CR3
DD 0 ; EIP
DD 0 ; EFLAGS
DD 0 ; EAX
DD 0 ; ECX
DD 0 ; EDX
DD 0 ; EBX
DD 0 ; ESP
DD 0 ; EBP
DD 0 ; ESI
DD 0 ; EDI
DD 0 ; ES
DD 0 ; CS
DD 0 ; SS
DD 0 ; DS
DD 0 ; FS
DD 0 ; GS
DD 0 ; LDT
DW 0 ; 调试陷阱标志
DW $ - LABEL_TSS + 2 ; I/O位图基址
DB 0ffh ; I/O位图结束标志
TSSLen equ $ - LABEL_TSS
3.2. 在 GDT 中创建 TSS 对应的描述符与选择子
3.2.1. 描述符
LABEL_DESC_TSS: Descriptor 0, TSSLen-1, 89h ; TSS
3.2.2. 选择子
SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT
3.2.3. 初始化 TSS 描述符段基址
; 初始化 TSS 描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_TSS
mov word [LABEL_DESC_TSS + 2], ax
shr eax, 16
mov byte [LABEL_DESC_TSS + 4], al
mov byte [LABEL_DESC_TSS + 7], ah
3.3. 加载 TSS
ltr 指令用于将 TSS 选择子载入 TSS 段寄存器 TR。
mov ax, SelectorTSS
ltr ax ; 设置任务状态段寄存器 TR
4. 创建 Ring3 堆栈段
4.1. 开辟 512 字节内存空间
; Ring3 堆栈段
[SECTION .s3]
ALIGN 32
[BITS 32]
LABEL_STACK3:
times 512 db 0
TopOfStack3 equ $ - LABEL_STACK3 - 1
4.2. 创建描述符
在 GDT 中添加 Ring3 堆栈段描述符。
LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, 4093h + 3 ; Ring3 堆栈段,DPL = 3
4.3. 创建选择子
SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + 3 ; RPL = 3
4.4. 初始化 Ring3 堆栈段描述符基地址
; 初始化堆栈段描述符(ring3)
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK3
mov word [LABEL_DESC_STACK3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK3 + 4], al
mov byte [LABEL_DESC_STACK3 + 7], ah
5. 将显示函数放入一致代码段
页面展示函数要在本次的程序中被 Ring0、Ring3 程序分别调用,最简单的方法就是让他成为 Ring0 的一致代码段。
我们做如下修改。
5.1. 创建代码段
[SECTION .display] ; 32 位代码段. 由实模式跳入.
[BITS 32]
DISPLAY_STRING_LABEL:
push eax
mov ah, 8Ch ; 0000: 黑底 1100: 红字
cld
.loop_label:
lodsb
test al, al
jz .over_print
mov [gs:edi], ax
add edi, 2
jmp .loop_label
.over_print:
pop eax
retf
DisplayLen equ $ - DISPLAY_STRING_LABEL
5.2. 创建 GDT 描述符与选择子
LABEL_DESC_DISPLAY: Descriptor 0, DisplayLen - 1, 409Ch ; 一致代码段
SelectorDisplay equ LABEL_DESC_DISPLAY - LABEL_GDT
5.3. 初始化描述符段基址
; 初始化一致代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, DISPLAY_STRING_LABEL
mov word [LABEL_DESC_DISPLAY + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DISPLAY + 4], al
mov byte [LABEL_DESC_DISPLAY + 7], ah
5.4. 修改调用方式
原本的调用需要改为:
xor edi, edi
mov edi, 80 * 2 * 2 ; 屏幕第 2 行, 第 0 列
xor esi, esi
mov esi, OffsetBootMessage
call SelectorDisplay:0
6. 编写调用门目标代码段
6.1. 目标代码编写
我们要通过调用门从 Ring3 跳转到 Ring0,然后打印一行字“Now I’m in Ring0 By CallGate”。
并从这里返回实地址模式,并退出程序。
如果我们不从这里返回实地址模式,而是返回到此前调用的 Ring3 再进行向实地址模式的跳转,就会因为 CPL 与 DPL 不同而报错:
check_cs(0x0020): non-conforming code seg descriptor dpl != cpl, dpl=0, cpl=3
[SECTION .sdest]; 调用门目标段
[BITS 32]
LABEL_SEG_CODE_DEST:
xor edi, edi
mov edi, 80 * 4 * 2 ; 屏幕第 4 行, 第 0 列
xor esi, esi
mov esi, OffsetGateDestMessage
call SelectorDisplay:0
jmp SelectorCode16:0 ; 跳回实地址模式
SegCodeDestLen equ $ - LABEL_SEG_CODE_DEST
6.2. 在 GDT 中创建目标代码段描述符与选择子
LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, 4098h ; 非一致代码段
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
6.3. 初始化描述符中目标段基址
; 初始化测试调用门的代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE_DEST
mov word [LABEL_DESC_CODE_DEST + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_DEST + 4], al
mov byte [LABEL_DESC_CODE_DEST + 7], ah
7. 创建调用门
7.1. 创建调用门描述符宏
; ----------------- 门描述符宏 -----------------
; 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 Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子 (2 字节)
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 (2 字节)
dw ((%2 >> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 共 8 字节
7.2. 在 GDT 中创建门描述符与选择子
LABEL_CALL_GATE: Gate SelectorCodeDest, 0, 0, 0dch
SelectorCallGate equ LABEL_CALL_GATE - LABEL_GDT + 3
8. 编写 Ring3 代码段
8.1. 编写 Ring3 执行代码
我们在 Ring3 代码中调用上面的显示函数,实现“Now, I’m in Ring3”字符串的打印。
为了读取到字符串,需要将全局数据段的 DPL 更改为 3,并添加对应的变量,这里就不赘述了。
[SECTION .ring3]
ALIGN 32
[BITS 32]
LABEL_CODE_RING3:
xor edi, edi
mov edi, 80 * 4 * 2 ; 屏幕第 4 行, 第 0 列
xor esi, esi
mov esi, OffsetRing3Message
call SelectorDisplay:0
call SelectorCallGate:0
SegCodeRing3Len equ $ - LABEL_CODE_RING3
8.2. 创建 GDT 描述符与选择子
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, 40e8h ; Ring3 代码段
SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + 3
8.3. 初始化 GDT 描述符段基址
; 初始化Ring3描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_CODE_RING3
mov word [LABEL_DESC_CODE_RING3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_RING3 + 4], al
mov byte [LABEL_DESC_CODE_RING3 + 7], ah
9. 长返回 — 从 Ring0 跳转到 Ring3
做了一切准备,只剩下代码最开始进入的地方 — 如何从 Ring0 跳转到 Ring3 了。
根据上面说过的原理,只需要一个长返回即可:
push SelectorStack3
push TopOfStack3
push SelectorCodeRing3
push 0
retf ; 长返回,Ring0 -> Ring3
10. 执行结果
运行代码,可以看到:
11. 微信公众号
欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤。
12. 附录 — 完整代码
; 段描述符
; 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 (2 字节)
dw %1 & 0FFFFh ; 段基址 1 (2 字节)
db (%1 >> 16) & 0FFh ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节)
db (%1 >> 24) & 0FFh ; 段基址 3 (1 字节)
%endmacro ; 共 8 字节
; 门描述符
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子 (2 字节)
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 (2 字节)
dw ((%2 >> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 共 8 字节
; ------------ DOS 加载初始内存地址 -----------
org 0100h
jmp LABEL_BEGIN
; ------------------- GDT ---------------------
[SECTION .gdt]
; GDT
; 段基址, 段界限, 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, 92h ; Normal描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, 4098h ; 32 位非一致代码段
LABEL_DESC_DISPLAY: Descriptor 0, DisplayLen - 1, 409ch ; 32 位一致代码段
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, 98h ; 16 位非一致代码段
LABEL_DESC_DATA: Descriptor 0, DataLen-1, 0e2h ; Ring3 数据段,DPL = 3
LABEL_DESC_STACK: Descriptor 0, TopOfStack, 4093h ; 32 位全局堆栈段,可读写数据段,指针默认使用 esp
LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, 40e3h ; Ring3 堆栈段,DPL = 3
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, 40e8h ; Ring3 代码段
LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, 4098h ; 非一致代码段
LABEL_DESC_TSS: Descriptor 0, TSSLen-1, 89h ; TSS
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, 0e2h ; 显存首地址
LABEL_CALL_GATE: Gate SelectorCodeDest, 0, 0, 0dch ; 调用门描述符
; ------------------ END OF GDT ----------------
GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0 ; GDT基地址
; ------------------ GDT 选择子 -----------------
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + 3 ; RPL = 3
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
SelectorDisplay equ LABEL_DESC_DISPLAY - LABEL_GDT
SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + 3
SelectorCallGate equ LABEL_CALL_GATE - LABEL_GDT + 3
; --------------- END OF 段选择子 ----------------
[SECTION .data1] ; 数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
BootMessage: db "Hello World my OS!", 0
OffsetBootMessage equ BootMessage - $$
Ring3Message: db "Now I'm in Ring3", 0
OffsetRing3Message equ Ring3Message - $$
GateDestMessage: db "Now I'm in Ring0 By CallGate", 0
OffsetGateDestMessage equ GateDestMessage - $$
DataLen equ $ - LABEL_DATA
; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $ - LABEL_STACK - 1
; Ring3 堆栈段
[SECTION .s3]
ALIGN 32
[BITS 32]
LABEL_STACK3:
times 512 db 0
TopOfStack3 equ $ - LABEL_STACK3 - 1
; ----------------------------- TSS --------------------------
[SECTION .tss]
ALIGN 32
[BITS 32]
LABEL_TSS:
DD 0 ; Back
DD TopOfStack ; 0 级堆栈栈顶
DD SelectorStack ; 0 级堆栈基址
DD 0 ; 1 级堆栈
DD 0 ;
DD 0 ; 2 级堆栈
DD 0 ;
DD 0 ; CR3
DD 0 ; EIP
DD 0 ; EFLAGS
DD 0 ; EAX
DD 0 ; ECX
DD 0 ; EDX
DD 0 ; EBX
DD 0 ; ESP
DD 0 ; EBP
DD 0 ; ESI
DD 0 ; EDI
DD 0 ; ES
DD 0 ; CS
DD 0 ; SS
DD 0 ; DS
DD 0 ; FS
DD 0 ; GS
DD 0 ; LDT
DW 0 ; 调试陷阱标志
DW $ - LABEL_TSS + 2 ; I/O位图基址
DB 0ffh ; I/O位图结束标志
TSSLen equ $ - LABEL_TSS
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
; 初始化段基址寄存器
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
mov [LABEL_GO_BACK_TO_REAL+3], ax
mov [SPValueInRealMode], sp
; 初始化 16 位代码段描述符
mov ax, cs
movzx eax, ax
shl eax, 4
add eax, LABEL_SEG_CODE16
mov word [LABEL_DESC_CODE16 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16 + 4], al
mov byte [LABEL_DESC_CODE16 + 7], ah
; 初始化非一致代码段描述符
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
; 初始化一致代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, DISPLAY_STRING_LABEL
mov word [LABEL_DESC_DISPLAY + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DISPLAY + 4], al
mov byte [LABEL_DESC_DISPLAY + 7], ah
; 初始化Ring3描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_CODE_RING3
mov word [LABEL_DESC_CODE_RING3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_RING3 + 4], al
mov byte [LABEL_DESC_CODE_RING3 + 7], ah
; 初始化测试调用门的代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE_DEST
mov word [LABEL_DESC_CODE_DEST + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_DEST + 4], al
mov byte [LABEL_DESC_CODE_DEST + 7], ah
; 初始化数据段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_DATA
mov word [LABEL_DESC_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah
; 初始化堆栈段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DESC_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK + 4], al
mov byte [LABEL_DESC_STACK + 7], ah
; 初始化堆栈段描述符(ring3)
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK3
mov word [LABEL_DESC_STACK3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK3 + 4], al
mov byte [LABEL_DESC_STACK3 + 7], ah
; 初始化 TSS 描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_TSS
mov word [LABEL_DESC_TSS + 2], ax
shr eax, 16
mov byte [LABEL_DESC_TSS + 4], al
mov byte [LABEL_DESC_TSS + 7], ah
; 准备加载 GDTR
xor eax, eax ; 清空 eax 寄存器
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; 计算出 GDT 基地址的物理地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
; 加载 GDTR
lgdt [GdtPtr]
; 关闭硬件中断
cli
; 打开 A20 地址总线
in al, 92h
or al, 00000010b
out 92h, al
; 置位 PE 标志位,打开保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
; 跳转进入保护模式
jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs,
; 并跳转到 Code32Selector:0 处
LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, [SPValueInRealMode]
; 关闭 A20 地址线
in al, 92h
and al, 0fdh
out 92h, al
; 打开硬件中断
sti
; 触发 BIOS int 21h 中断,回到实地址模式
mov ax, 4c00h
int 21h
[SECTION .s32] ; 32 位代码段. 由实模式跳入.
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorData
mov ds, ax ; 数据段选择子
mov ax, SelectorVideo
mov gs, ax ; 赋值视频段选择子
mov ax, SelectorStack
mov ss, ax ; 堆栈段选择子
mov esp, TopOfStack
xor edi, edi
mov edi, 80 * 2 * 2 ; 屏幕第 2 行, 第 0 列
xor esi, esi
mov esi, OffsetBootMessage
call SelectorDisplay:0
; Load TSS
mov ax, SelectorTSS
ltr ax ; 设置任务状态段寄存器 TR
push SelectorStack3
push TopOfStack3
push SelectorCodeRing3
push 0
retf ; 长返回,Ring0 -> Ring3
SegCode32Len equ $ - LABEL_SEG_CODE32
[SECTION .display]
[BITS 32]
DISPLAY_STRING_LABEL:
; mov ax, SelectorVideo
; mov gs, ax ; 视频段选择子(目的)
push eax
mov ah, 8Ch ; 0000: 黑底 1100: 红字
cld
.loop_label:
lodsb
test al, al
jz .over_print
mov [gs:edi], ax
add edi, 2
jmp .loop_label
.over_print:
pop eax
retf
DisplayLen equ $ - DISPLAY_STRING_LABEL
; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 跳回实模式:
mov ax, SelectorNormal
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 0feh
mov cr0, eax
LABEL_GO_BACK_TO_REAL:
jmp word 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
Code16Len equ $ - LABEL_SEG_CODE16
; END of [SECTION .s16code]
[SECTION .ring3]
ALIGN 32
[BITS 32]
LABEL_CODE_RING3:
xor edi, edi
mov edi, 80 * 3 * 2 ; 屏幕第 3 行, 第 0 列
xor esi, esi
mov esi, OffsetRing3Message
call SelectorDisplay:0
call SelectorCallGate:0
SegCodeRing3Len equ $ - LABEL_CODE_RING3
[SECTION .sdest]; 调用门目标段
[BITS 32]
LABEL_SEG_CODE_DEST:
xor edi, edi
mov edi, 80 * 4 * 2 ; 屏幕第 4 行, 第 0 列
xor esi, esi
mov esi, OffsetGateDestMessage
call SelectorDisplay:0
jmp SelectorCode16:0 ; 跳回实地址模式
SegCodeDestLen equ $ - LABEL_SEG_CODE_DEST
阅读原文
《利用调用门实现特权级间跳转 — 实战篇》来自互联网,仅为收藏学习,如侵权请联系删除。本文URL:http://www.bookhoes.com/2120.html