利用调用门实现特权级间跳转(下) -- 实战篇

2020-02-02 22:26:45   最后更新: 2020-02-02 22:28:16   访问数量:40




上一篇文章中,我们详细介绍了操作系统特权级,以及利用调用门、TSS 实现不同特权级之间的跳转

利用调用门实现特权级间跳转(上) -- 原理篇

 

经过这一篇文章对原理的深入剖析,我们从 Ring0 跳到 Ring3 再跳转回来的代码就已经呼之欲出了

接下来,我们就先从此前代码中进入 Ring3,然后通过调用门,实现从 Ring3 到 Ring0 的跳转

 

这里我们再对特权级跳转的实现原理进行一个简要的介绍

 

从 Ring0 到 Ring3

毋庸置疑,操作系统是启动在最高特权级的 Ring0 下的,那么,在操作系统中如何实现从 Ring0 特权级跳转到应用程序所在的 Ring3 特权级的呢?

上一篇文章已经讲到,是通过先将目标地址对应的 cs、eip、ss、esp 四个寄存器压栈,模拟长调用,再通过长返回就可以实现到 Ring3 的跳转了

 

从 Ring3 到 Ring0

从 Ring3 特权级跳转到 Ring0 特权级,需要借助调用门,只要调用门描述符的 DPL 大于 CPL 与 RPL,就可以实现从低特权级跳转到门描述符所指定的高特权级目标代码了

但是,在跳转的过程中,CPU 自动进行了栈空间的切换,每个特权级都必须对应不同的栈基址与栈指针,通过 TSS 来进行描述,只要在门描述符中指定参数数量,CPU 会自动完成栈切换以及在这一过程中的参数复制工作

 

 

现在,我们已经有了用于 Ring0 的全局堆栈段,那么我们就可以直接创建 TSS 了

 

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

 

 

在 GDT 中创建 TSS 对应的描述符与选择子

描述符

LABEL_DESC_TSS: Descriptor 0, TSSLen-1, 89h ; TSS

 

 

选择子

SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT

 

 

初始化 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

 

 

加载 TSS

ltr 指令用于将 TSS 选择子载入 TSS 段寄存器 TR

mov ax, SelectorTSS ltr ax ; 设置任务状态段寄存器 TR

 

 

开辟 512 字节内存空间

; Ring3 堆栈段 [SECTION .s3] ALIGN 32 [BITS 32] LABEL_STACK3: times 512 db 0 TopOfStack3 equ $ - LABEL_STACK3 - 1

 

 

创建描述符

在 GDT 中添加 Ring3 堆栈段描述符

LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, 4093h + 3 ; Ring3 堆栈段,DPL = 3

 

 

创建选择子

SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + 3 ; RPL = 3

 

 

初始化 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

 

 

页面展示函数要在本次的程序中被 Ring0、Ring3 程序分别调用,最简单的方法就是让他成为 Ring0 的一致代码段

我们做如下修改

 

创建代码段

[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

 

 

创建 GDT 描述符与选择子

LABEL_DESC_DISPLAY: Descriptor 0, DisplayLen - 1, 409Ch ; 一致代码段 SelectorDisplay equ LABEL_DESC_DISPLAY - LABEL_GDT

 

 

初始化描述符段基址

; 初始化一致代码段描述符 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

 

 

修改调用方式

原本的调用需要改为:

xor edi, edi mov edi, 80 * 2 * 2 ; 屏幕第 2 行, 第 0 列 xor esi, esi mov esi, OffsetBootMessage call SelectorDisplay:0

 

 

目标代码编写

我们要通过调用门从 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

 

 

在 GDT 中创建目标代码段描述符与选择子

LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, 4098h ; 非一致代码段 SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT

 

 

初始化描述符中目标段基址

; 初始化测试调用门的代码段描述符 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

 

 

创建调用门描述符宏

; ----------------- 门描述符宏 ----------------- ; 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 字节

 

 

在 GDT 中创建门描述符与选择子

LABEL_CALL_GATE: Gate SelectorCodeDest, 0, 0, 0dch SelectorCallGate equ LABEL_CALL_GATE - LABEL_GDT + 3

 

 

编写 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

 

 

创建 GDT 描述符与选择子

LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, 40e8h ; Ring3 代码段 SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + 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

 

 

做了一切准备,只剩下代码最开始进入的地方 -- 如何从 Ring0 跳转到 Ring3 了

根据上面说过的原理,只需要一个长返回即可:

push SelectorStack3 push TopOfStack3 push SelectorCodeRing3 push 0 retf ; 长返回,Ring0 -> Ring3

 

 

运行代码,可以看到:

 

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

 

; 段描述符 ; 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

 

 






gdt      特权级      调用门      tss      ring0      ring3      call_gate     


京ICP备15018585号