高特权级代码段转向低特权级代码段(利用 ret(retf) 指令实现 jmp from ring0 to ring3)
【0】写在前面
- 0.1)本代码旨在演示 从 ring0 转移到 ring3(即,从高特权级 转移到 低特权级)
- 0.2)本文 只对 与 门相关的 代码进行简要注释,言简意赅;
- 0.3)文末的个人总结是干货,前面代码仅供参考的,且source code from orange’s implemention of a os.
; ==========================================
; pmtest5a.asm
; 编译方法:nasm pmtest5a.asm -o pmtest5a.com
; ==========================================
%include    "pm.inc"    ; 常量, 宏, 以及一些说明
org 0100h
 jmp    LABEL_BEGIN
;[SECTION .gdt]
; GDT
;                           段基址,       段界限     , 属性
LABEL_GDT:             Descriptor 0,                0, 0         ; 空描述符
LABEL_DESC_NORMAL:     Descriptor 0,           0ffffh, DA_DRW    ; Normal 描述符
LABEL_DESC_CODE32:     Descriptor 0,   SegCode32Len-1, DA_C+DA_32; 非一致代码段,32
LABEL_DESC_CODE16:     Descriptor 0,           0ffffh, DA_C      ; 非一致代码段,16
LABEL_DESC_CODE_DEST:  Descriptor 0, SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32
; ring3的代码段描述符的定义 [add]
LABEL_DESC_CODE_RING3: Descriptor 0,SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_DATA:       Descriptor 0,        DataLen-1, DA_DRW    ; Data
LABEL_DESC_STACK:      Descriptor 0,       TopOfStack, DA_DRWA+DA_32;Stack, 32 位
; ring3的堆栈段描述符的定义 [add]
LABEL_DESC_STACK3:     Descriptor 0,      TopOfStack3, DA_DRWA+DA_32+DA_DPL3
LABEL_DESC_LDT:        Descriptor 0,         LDTLen-1, DA_LDT    ; LDT
; ring3的视频段描述符的DPL属性设置为 3 [add]
LABEL_DESC_VIDEO:      Descriptor 0B8000h,     0ffffh, DA_DRW+DA_DPL3
; 门                               目标选择子,偏移,DCount, 属性
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,   0,     0, DA_386CGate+DA_DPL0
; 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
SelectorCodeDest    equ LABEL_DESC_CODE_DEST    - LABEL_GDT
; ring3的代码段描述符的选择子定义 [add]
SelectorCodeRing3   equ LABEL_DESC_CODE_RING3   - LABEL_GDT + SA_RPL3
SelectorData     equ    LABEL_DESC_DATA  - LABEL_GDT
SelectorStack    equ    LABEL_DESC_STACK    - LABEL_GDT
; ring3的堆栈段描述符的选择子定义 [add]
SelectorStack3   equ    LABEL_DESC_STACK3   - LABEL_GDT + SA_RPL3
SelectorLDT  equ    LABEL_DESC_LDT   - LABEL_GDT
;ring3的视频段描述符(DPL==3)的选择子定义 [add]
SelectorVideo    equ    LABEL_DESC_VIDEO    - LABEL_GDT
SelectorCallGateTest    equ LABEL_CALL_GATE_TEST    - LABEL_GDT
; END of [SECTION .gdt]
[SECTION .data1] ; 数据段
ALIGN   32
[BITS   32]
LABEL_DATA:
SPValueInRealMode   dw  0
; 字符串
PMMessage:   db "In Protect Mode now. ^-^", 0   ; 进入保护模式后显示此字符串
OffsetPMMessage  equ    PMMessage - $$
	StrTest:	 db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
	OffsetStrTest	 equ	StrTest - $$
DataLen  equ    $ - LABEL_DATA
; END of [SECTION .data1]
; 全局堆栈段 
    [SECTION .gs]
ALIGN   32
[BITS   32]
LABEL_STACK:
 times 512 db 0
TopOfStack  equ $ - LABEL_STACK - 1
; END of [SECTION .gs]
; 堆栈段ring3的定义 
    [SECTION .s3]
ALIGN   32
[BITS   32]
LABEL_STACK3:
 times 512 db 0
TopOfStack3 equ $ - LABEL_STACK3 - 1
; END of [SECTION .s3]
[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
 ; 初始化 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
 ; 初始化测试调用门的代码段描述符
 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) [add]
 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
 ; 初始化 LDT 在 GDT 中的描述符
 xor    eax, eax
 mov    ax, ds
 shl    eax, 4
 add    eax, LABEL_LDT
 mov    word [LABEL_DESC_LDT + 2], ax
 shr    eax, 16
 mov    byte [LABEL_DESC_LDT + 4], al
 mov    byte [LABEL_DESC_LDT + 7], ah
 ; 初始化 LDT 中的描述符
 xor    eax, eax
 mov    ax, ds
 shl    eax, 4
 add    eax, LABEL_CODE_A
 mov    word [LABEL_LDT_DESC_CODEA + 2], ax
 shr    eax, 16
 mov    byte [LABEL_LDT_DESC_CODEA + 4], al
 mov    byte [LABEL_LDT_DESC_CODEA + 7], ah
; 初始化Ring3描述符 [add]
 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
 ; 为加载 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  处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY:    ; 从保护模式跳回到实模式就到了这里
 mov    ax, cs
 mov    ds, ax
 mov    es, ax
 mov    ss, ax
 mov    sp, [SPValueInRealMode]
 in al, 92h  ; ┓
 and    al, 11111101b   ; ┣ 关闭 A20 地址线
 out    92h, al  ; ┛
 sti     ; 开中断
 mov    ax, 4c00h   ; ┓
 int    21h  ; ┛回到 DOS
; END of [SECTION .s16]
[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
 ; 下面显示一个字符串
 mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
 xor    esi, esi
 xor    edi, edi
 mov    esi, OffsetPMMessage    ; 源数据偏移
 mov    edi, (80 * 10 + 0) * 2  ; 目的数据偏移。屏幕第 10 行, 第 0 列。
 cld
.1:
 lodsb
 test   al, al
 jz .2
 mov    [gs:edi], ax
 add    edi, 2
 jmp    .1
.2: ; 显示完毕
 call   DispReturn
;  ; 以下代码分步骤演示了如何将A的堆栈拷贝到B的堆栈(A==调用者,B=被调用者) 
    ; 1.将ring3的堆栈段描述符的选择子压栈 [add]
 push   SelectorStack3
; 2.将ring3的堆栈压栈 [add]
 push   TopOfStack3
; 3.将ring3的堆栈值 压栈 [add]
 push   SelectorCodeRing3
 push   0
; 4.retf 弹出ip + 弹出cs;此时ip==0, cs==SelectorCodeRing3,然后CPU自动解析出SelectorCodeRing3 索引(定位)的段描述符, 该描述符存储有 
    ; 4.ring3下代码段LABEL_CODE_RING3 的基地址, 让我们转向 代码末尾的 LABEL_CODE_RING3 代码段吧;
 retf
 ; 测试调用门(无特权级变换),将打印字母 'C'
 call   SelectorCallGateTest:0
 ;call  SelectorCodeDest:0
 ; Load LDT
 mov    ax, SelectorLDT
 lldt   ax
 jmp    SelectorLDTCodeA:0  ; 跳入局部任务,将打印字母 'L'。
; ------------------------------------------------------------------------
DispReturn:
 push   eax
 push   ebx
 mov    eax, edi
 mov    bl, 160
 div    bl
 and    eax, 0FFh
 inc    eax
 mov    bl, 160
 mul    bl
 mov    edi, eax
 pop    ebx
 pop    eax
 ret
; DispReturn 结束---------------------------------------------------------
SegCode32Len    equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
[SECTION .sdest]; 调用门目标段
[BITS   32]
LABEL_SEG_CODE_DEST:
 mov    ax, SelectorVideo
 mov    gs, ax   ; 视频段选择子(目的)
 mov    edi, (80 * 12 + 0) * 2  ; 屏幕第 12 行, 第 0 列。
 mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
 mov    al, 'C'
 mov    [gs:edi], ax
 retf
SegCodeDestLen  equ $ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]
; 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, 11111110b
 mov    cr0, eax
LABEL_GO_BACK_TO_REAL:
 jmp    0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值
Code16Len   equ $ - LABEL_SEG_CODE16
; END of [SECTION .s16code]
; LDT 
    [SECTION .ldt]
ALIGN   32
LABEL_LDT:
;                                         段基址       段界限     ,   属性
LABEL_LDT_DESC_CODEA:   Descriptor         0,     CodeALen - 1,   DA_C + DA_32  ; Code, 32 位
LDTLen   equ    $ - LABEL_LDT
; LDT 选择子
SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL
; END of [SECTION .ldt]
; CodeA (LDT, 32 位代码段) 
    [SECTION .la]
ALIGN   32
[BITS   32]
LABEL_CODE_A:
 mov    ax, SelectorVideo
 mov    gs, ax   ; 视频段选择子(目的)
 mov    edi, (80 * 13 + 0) * 2  ; 屏幕第 13 行, 第 0 列。
 mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
 mov    al, 'L'
 mov    [gs:edi], ax
 ; 准备经由16位代码段跳回实模式
 jmp    SelectorCode16:0
CodeALen    equ $ - LABEL_CODE_A
; END of [SECTION .la]
; CodeRing3的定义(ring3下的代码段) 
    ; 显然, ring3 中的任务执行完后, jmp $ 这句代码使得程序 就会 永久停留在该处;
[SECTION .ring3]
ALIGN   32
[BITS   32]
LABEL_CODE_RING3:
 mov    ax, SelectorVideo
 mov    gs, ax
 mov    edi, (80 * 14 + 0) * 2
 mov    ah, 0Ch
 mov    al, '3'
 mov    [gs:edi], ax
 jmp    $
SegCodeRing3Len equ $ - LABEL_CODE_RING3
; END of [SECTION .ring3]
【总结-Conclusion】
- C1)实现ring0 jmp 到ring3, 使用的是ret 指令,代码如下:(核心代码) - ; 以下代码分步骤演示了如何将A的堆栈拷贝到B的堆栈(A==调用者,B=被调用者) 
 ; 1.将ring3的堆栈段描述符的选择子压栈 [add](堆栈段描述符 存储有新堆栈的基地址ss)- push SelectorStack3
 - ; 2.将ring3的堆栈压栈[add](新栈顶指针esp压栈) - push TopOfStack3
 - ; 3.将ring3的堆栈值 压栈 [add] (将ring3 下的任务代码段基地址cs 压栈) - push SelectorCodeRing3
 ; (偏移量ip == 0 压栈)
 push 0
 
; 4.retf 弹出ip + 弹出cs;此时ip==0, cs==SelectorCodeRing3,然后CPU自动解析出SelectorCodeRing3 索引(定位)的段描述符, 该描述符存储有 
    ; 4.ring3下代码段LABEL_CODE_RING3 的基地址;
 retf
要知道: retf == [ pop cs, pop ip ] , ret == pop ip
- C2)指令ret 用于执行近返回、同特权级远返回和不同特权级的远返回;
- C2.1)近返回仅在当前代码段中转移程序控制权, 因此CPU 仅仅进行界限检查;
- C2.2)对于同特权级远返回, CPU同时从堆栈中弹出返回代码段的选择子和返回指令指针;
- C2.3)会发生特权级改变的远返回仅允许返回到低特权级程序中, 即返回到的代码段DPL要大于CPL; 
- 当执行远返回时, CPU执行以下步骤(也即是上述过程的第4步——指令retf): 
- 1)检查保存的cs 中的RPL 以判断返回时是否要变换特权级;
- 2)加载被调用者堆栈上的cs 和eip;
- 3)如果ret 指令含有参数,则增加esp 跳过参数,然后esp 将指向被保存过的调用者ss 和 esp ;ret的参数个数对应 调用门中的 Param Count的值;
- 4)加载ss 和 esp , 切换到调用者堆栈,被调用者的ss 和 esp 被丢弃;
- 5)如果ret 指令含有参数, 增加esp 的值以跳过参数;
- 6)检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL 小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器;  
 
 
 
高特权级代码段转向低特权级代码段(利用 ret(retf) 指令实现 jmp from ring0 to ring3)的更多相关文章
- JavaScript  执行环境(执行上下文) 变量对象 作用域链 上下文 块级作用域 私有变量和特权方法
		总结自<高程三>第四章 理解Javascript_12_执行模型浅析 JS的执行环境与作用域 javascript高级程序第三版学习笔记[执行环境.作用域] 在javascript ... 
- C/C++学习笔记---高地址、低地址、大段字节序、小段字节序
		字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端.大端两种字节顺序. 小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处: 大端字节序是高字节数据存放在低地址 ... 
- 一段显示隐藏列表HTML代码
		一段显示隐藏列表HTML代码, 技巧在于把页面上的元素(“返回首页”)和控制显示/隐藏的元素(id=navs-menu)放在一个共同的div上,并在该div上绑定onmouseover和onmouse ... 
- SqlServer高版本数据库数据备份到低版本数据库上
		想要将Sqlserver2014高版本备份的数据还原到低版本SqlServer2012上去,但是这在SqlServer中是没法直接还原数据库的,通过以下方法可以顺利还原. 通过高版本生成sql脚本在低 ... 
- 21 段实用便捷的 PHP 代码
		PHP 是目前使用最广泛的基于 Web 的编程语言,驱动着数以百万计的网站,其中也包括如 Facebook 等一些大型站点.这里收集了 21 段实用便捷的 PHP 代码摘录,对每种类型的 PHP 开发 ... 
- linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】
		本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ... 
- “段寄存器”的故事[转](彻底搞清内存段/elf段/实模式保护模式以及段寄存器)
		http://blog.csdn.net/michael2012zhao/article/details/5554023 一. 段寄存器的产生 段寄存器的产生源于Intel 8086 CPU体系结构中 ... 
- VS开发中的代码编写小技巧——避免重复代码编写的几种方法
		上一篇文章中程序员的幸福生活--有你的日子,每天都是情人节,收到了大家的很多好评.鼓励和祝福,非常感动,真诚的谢谢大家.也希望每个朋友都能保持一个积极向上的心态,去迎接丰富多彩的人生. 在开发过程中, ... 
- Java中静态代码块、构造代码块、构造函数、普通代码块
		在Java中,静态代码块.构造代码块.构造函数.普通代码块的执行顺序是一个笔试的考点,通过这篇文章希望大家能彻底了解它们之间的执行顺序. 1.静态代码块 ①.格式 在java类中(方法中不能存在静态代 ... 
随机推荐
- Codeforces 475D CGCDSSQ 区间gcd值
			题目链接 题意 给定一个长度为 \(n\) 的数列 \(a_1,...,a_n\) 与 \(q\) 个询问 \(x_1,...,x_q\),对于每个 \(x_i\) 回答有多少对 \((l,r)\) ... 
- [CF665F]Four Divisors
			题目大意: 给定$n(n\leq10^{11})$,求$\displaystyle\sum_{i=1}^n[\tau(i)=4]$. 思路: 设$p,q$为不相等的质数,则满足$\tau(i)=4$的 ... 
- Ruby Time And DateTime之Time in Core
			今天遇到一个问题,就是在Ruby中对于Time和DateTime的使用,不是很明了,现在研究一下: 先说Time: 在Ruby2.0中关于Time有两处定义一个是在Core中,http://www.r ... 
- 串口调试利器--Minicom配置及使用详解.md
			因为现在电脑基本不配备串行接口,所以,usb转串口成为硬件调试时的必然选择.目前知道的,PL2303的驱动是有的,在dev下的名称是ttyUSB*. Minicom,是Linux下应用比较广泛的串口软 ... 
- 聊聊、Zookeeper Linux 启动
			Zookeeper 在 windows 下安装比较简单,属于无脑式安装,下载下来双击脚本就可以了.前面的文章中也有介绍,今天我来写写 Linux 下的安装,以及所碰到的坑. 首先,登陆 Linux 系 ... 
- Displaying Tabbed and Stacked Canvas Using Show_View In Oracle Forms
			Displays the indicated canvas at the coordinates specified by the canvas's X Position and Y Position ... 
- java编程思想第四版第9章
			练习3: public class MainTest { public static void main(String args[]){ Bcycle b=new Bcycle(); b.print( ... 
- context:exclude-filter spring事宜【经典-转】
			context:exclude-filter spring事务 如果带上事务,那么用annotation方式的事务注解和bean配置,事务会失效,要将service bean配置到xml文件中才行. ... 
- ElasticSearch命令增加字段总结
			1.建立一个String类型的字段 curl -XPUT http://192.168.46.163:9200/t_risk_case/_mapping/t_risk_case?pretty -d ' ... 
- TensorFlow笔记二:线性回归预测(Linear Regression)
			代码: import tensorflow as tf import numpy as np import xlrd import matplotlib.pyplot as plt DATA_FILE ... 
