【0】写在前面

  • 0.1)本代码旨在演示 从 ring0 转移到 ring3(即,从高特权级 转移到 低特权级)
  • 0.2)本文 只对 与 门相关的 代码进行简要注释,言简意赅;
  • 0.3)文末的个人总结是干货,前面代码仅供参考的,且source code from orange’s implemention of a os.

  1. ; ==========================================
  2. ; pmtest5a.asm
  3. ; 编译方法:nasm pmtest5a.asm -o pmtest5a.com
  4. ; ==========================================
  5. %include "pm.inc" ; 常量, 宏, 以及一些说明
  6. org 0100h
  7. jmp LABEL_BEGIN

;[SECTION .gdt]

  1. ; GDT
  2. ; 段基址, 段界限 , 属性
  3. LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
  4. LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
  5. LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32; 非一致代码段,32
  6. LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段,16
  7. LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32

; ring3的代码段描述符的定义 [add]

  1. LABEL_DESC_CODE_RING3: Descriptor 0,SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
  2. LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; Data
  3. LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32;Stack, 32

; ring3的堆栈段描述符的定义 [add]

  1. LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, DA_DRWA+DA_32+DA_DPL3
  2. LABEL_DESC_LDT: Descriptor 0, LDTLen-1, DA_LDT ; LDT

; ring3的视频段描述符的DPL属性设置为 3 [add]

  1. LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW+DA_DPL3
  2. ; 目标选择子,偏移,DCount, 属性
  3. LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate+DA_DPL0
  4. ; GDT 结束
  5. GdtLen equ $ - LABEL_GDT ; GDT长度
  6. GdtPtr dw GdtLen - 1 ; GDT界限
  7. dd 0 ; GDT基地址
  8. ; GDT 选择子
  9. SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
  10. SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
  11. SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
  12. SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT

; ring3的代码段描述符的选择子定义 [add]

  1. SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + SA_RPL3
  2. SelectorData equ LABEL_DESC_DATA - LABEL_GDT
  3. SelectorStack equ LABEL_DESC_STACK - LABEL_GDT

; ring3的堆栈段描述符的选择子定义 [add]

  1. SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + SA_RPL3
  2. SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT

;ring3的视频段描述符(DPL==3)的选择子定义 [add]

  1. SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
  2. SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT
  3. ; END of [SECTION .gdt]

[SECTION .data1] ; 数据段

  1. ALIGN 32
  2. [BITS 32]
  3. LABEL_DATA:
  4. SPValueInRealMode dw 0
  5. ; 字符串
  6. PMMessage: db "In Protect Mode now. ^-^", 0 ; 进入保护模式后显示此字符串
  7. OffsetPMMessage equ PMMessage - $$
  8. StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
  9. OffsetStrTest equ StrTest - $$
  10. DataLen equ $ - LABEL_DATA
  11. ; END of [SECTION .data1]

; 全局堆栈段

[SECTION .gs]

  1. ALIGN 32
  2. [BITS 32]
  3. LABEL_STACK:
  4. times 512 db 0
  5. TopOfStack equ $ - LABEL_STACK - 1
  6. ; END of [SECTION .gs]

; 堆栈段ring3的定义

[SECTION .s3]

  1. ALIGN 32
  2. [BITS 32]
  3. LABEL_STACK3:
  4. times 512 db 0
  5. TopOfStack3 equ $ - LABEL_STACK3 - 1
  6. ; END of [SECTION .s3]

[SECTION .s16]

  1. [BITS 16]
  2. LABEL_BEGIN:
  3. mov ax, cs
  4. mov ds, ax
  5. mov es, ax
  6. mov ss, ax
  7. mov sp, 0100h
  8. mov [LABEL_GO_BACK_TO_REAL+3], ax
  9. mov [SPValueInRealMode], sp
  10. ; 初始化 16 位代码段描述符
  11. mov ax, cs
  12. movzx eax, ax
  13. shl eax, 4
  14. add eax, LABEL_SEG_CODE16
  15. mov word [LABEL_DESC_CODE16 + 2], ax
  16. shr eax, 16
  17. mov byte [LABEL_DESC_CODE16 + 4], al
  18. mov byte [LABEL_DESC_CODE16 + 7], ah
  19. ; 初始化 32 位代码段描述符
  20. xor eax, eax
  21. mov ax, cs
  22. shl eax, 4
  23. add eax, LABEL_SEG_CODE32
  24. mov word [LABEL_DESC_CODE32 + 2], ax
  25. shr eax, 16
  26. mov byte [LABEL_DESC_CODE32 + 4], al
  27. mov byte [LABEL_DESC_CODE32 + 7], ah
  28. ; 初始化测试调用门的代码段描述符
  29. xor eax, eax
  30. mov ax, cs
  31. shl eax, 4
  32. add eax, LABEL_SEG_CODE_DEST
  33. mov word [LABEL_DESC_CODE_DEST + 2], ax
  34. shr eax, 16
  35. mov byte [LABEL_DESC_CODE_DEST + 4], al
  36. mov byte [LABEL_DESC_CODE_DEST + 7], ah
  37. ; 初始化数据段描述符
  38. xor eax, eax
  39. mov ax, ds
  40. shl eax, 4
  41. add eax, LABEL_DATA
  42. mov word [LABEL_DESC_DATA + 2], ax
  43. shr eax, 16
  44. mov byte [LABEL_DESC_DATA + 4], al
  45. mov byte [LABEL_DESC_DATA + 7], ah
  46. ; 初始化堆栈段描述符
  47. xor eax, eax
  48. mov ax, ds
  49. shl eax, 4
  50. add eax, LABEL_STACK
  51. mov word [LABEL_DESC_STACK + 2], ax
  52. shr eax, 16
  53. mov byte [LABEL_DESC_STACK + 4], al
  54. mov byte [LABEL_DESC_STACK + 7], ah

; 初始化堆栈段描述符(Ring3) [add]

  1. xor eax, eax
  2. mov ax, ds
  3. shl eax, 4
  4. add eax, LABEL_STACK3
  5. mov word [LABEL_DESC_STACK3 + 2], ax
  6. shr eax, 16
  7. mov byte [LABEL_DESC_STACK3 + 4], al
  8. mov byte [LABEL_DESC_STACK3 + 7], ah
  9. ; 初始化 LDT GDT 中的描述符
  10. xor eax, eax
  11. mov ax, ds
  12. shl eax, 4
  13. add eax, LABEL_LDT
  14. mov word [LABEL_DESC_LDT + 2], ax
  15. shr eax, 16
  16. mov byte [LABEL_DESC_LDT + 4], al
  17. mov byte [LABEL_DESC_LDT + 7], ah
  18. ; 初始化 LDT 中的描述符
  19. xor eax, eax
  20. mov ax, ds
  21. shl eax, 4
  22. add eax, LABEL_CODE_A
  23. mov word [LABEL_LDT_DESC_CODEA + 2], ax
  24. shr eax, 16
  25. mov byte [LABEL_LDT_DESC_CODEA + 4], al
  26. mov byte [LABEL_LDT_DESC_CODEA + 7], ah

; 初始化Ring3描述符 [add]

  1. xor eax, eax
  2. mov ax, ds
  3. shl eax, 4
  4. add eax, LABEL_CODE_RING3
  5. mov word [LABEL_DESC_CODE_RING3 + 2], ax
  6. shr eax, 16
  7. mov byte [LABEL_DESC_CODE_RING3 + 4], al
  8. mov byte [LABEL_DESC_CODE_RING3 + 7], ah
  9. ; 为加载 GDTR 作准备
  10. xor eax, eax
  11. mov ax, ds
  12. shl eax, 4
  13. add eax, LABEL_GDT ; eax <- gdt 基地址
  14. mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
  15. ; 加载 GDTR
  16. lgdt [GdtPtr]
  17. ; 关中断
  18. cli
  19. ; 打开地址线A20
  20. in al, 92h
  21. or al, 00000010b
  22. out 92h, al
  23. ; 准备切换到保护模式
  24. mov eax, cr0
  25. or eax, 1
  26. mov cr0, eax
  27. ; 真正进入保护模式
  28. jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0
  29. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  30. LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
  31. mov ax, cs
  32. mov ds, ax
  33. mov es, ax
  34. mov ss, ax
  35. mov sp, [SPValueInRealMode]
  36. in al, 92h ;
  37. and al, 11111101b ; 关闭 A20 地址线
  38. out 92h, al ;
  39. sti ; 开中断
  40. mov ax, 4c00h ;
  41. int 21h ; ┛回到 DOS
  42. ; END of [SECTION .s16]

[SECTION .s32]; 32 位代码段. 由实模式跳入.

  1. [BITS 32]
  2. LABEL_SEG_CODE32:
  3. mov ax, SelectorData
  4. mov ds, ax ; 数据段选择子
  5. mov ax, SelectorVideo
  6. mov gs, ax ; 视频段选择子
  7. mov ax, SelectorStack
  8. mov ss, ax ; 堆栈段选择子
  9. mov esp, TopOfStack
  10. ; 下面显示一个字符串
  11. mov ah, 0Ch ; 0000: 黑底 1100: 红字
  12. xor esi, esi
  13. xor edi, edi
  14. mov esi, OffsetPMMessage ; 源数据偏移
  15. mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 0 列。
  16. cld
  17. .1:
  18. lodsb
  19. test al, al
  20. jz .2
  21. mov [gs:edi], ax
  22. add edi, 2
  23. jmp .1
  24. .2: ; 显示完毕
  25. call DispReturn

; ; 以下代码分步骤演示了如何将A的堆栈拷贝到B的堆栈(A==调用者,B=被调用者)

; 1.将ring3的堆栈段描述符的选择子压栈 [add]

  1. push SelectorStack3

; 2.将ring3的堆栈压栈 [add]

  1. push TopOfStack3

; 3.将ring3的堆栈值 压栈 [add]

  1. push SelectorCodeRing3
  2. push 0

; 4.retf 弹出ip + 弹出cs;此时ip==0, cs==SelectorCodeRing3,然后CPU自动解析出SelectorCodeRing3 索引(定位)的段描述符, 该描述符存储有

; 4.ring3下代码段LABEL_CODE_RING3 的基地址, 让我们转向 代码末尾的 LABEL_CODE_RING3 代码段吧;

  1. retf
  2. ; 测试调用门(无特权级变换),将打印字母 'C'
  3. call SelectorCallGateTest:0
  4. ;call SelectorCodeDest:0
  5. ; Load LDT
  6. mov ax, SelectorLDT
  7. lldt ax
  8. jmp SelectorLDTCodeA:0 ; 跳入局部任务,将打印字母 'L'
  9. ; ------------------------------------------------------------------------
  10. DispReturn:
  11. push eax
  12. push ebx
  13. mov eax, edi
  14. mov bl, 160
  15. div bl
  16. and eax, 0FFh
  17. inc eax
  18. mov bl, 160
  19. mul bl
  20. mov edi, eax
  21. pop ebx
  22. pop eax
  23. ret
  24. ; DispReturn 结束---------------------------------------------------------
  25. SegCode32Len equ $ - LABEL_SEG_CODE32
  26. ; END of [SECTION .s32]

[SECTION .sdest]; 调用门目标段

  1. [BITS 32]
  2. LABEL_SEG_CODE_DEST:
  3. mov ax, SelectorVideo
  4. mov gs, ax ; 视频段选择子(目的)
  5. mov edi, (80 * 12 + 0) * 2 ; 屏幕第 12 行, 0 列。
  6. mov ah, 0Ch ; 0000: 黑底 1100: 红字
  7. mov al, 'C'
  8. mov [gs:edi], ax
  9. retf
  10. SegCodeDestLen equ $ - LABEL_SEG_CODE_DEST
  11. ; END of [SECTION .sdest]

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式

[SECTION .s16code]

  1. ALIGN 32
  2. [BITS 16]
  3. LABEL_SEG_CODE16:
  4. ; 跳回实模式:
  5. mov ax, SelectorNormal
  6. mov ds, ax
  7. mov es, ax
  8. mov fs, ax
  9. mov gs, ax
  10. mov ss, ax
  11. mov eax, cr0
  12. and al, 11111110b
  13. mov cr0, eax
  14. LABEL_GO_BACK_TO_REAL:
  15. jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
  16. Code16Len equ $ - LABEL_SEG_CODE16
  17. ; END of [SECTION .s16code]

; LDT

[SECTION .ldt]

  1. ALIGN 32
  2. LABEL_LDT:
  3. ; 段基址 段界限 , 属性
  4. LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32
  5. LDTLen equ $ - LABEL_LDT
  6. ; LDT 选择子
  7. SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
  8. ; END of [SECTION .ldt]

; CodeA (LDT, 32 位代码段)

[SECTION .la]

  1. ALIGN 32
  2. [BITS 32]
  3. LABEL_CODE_A:
  4. mov ax, SelectorVideo
  5. mov gs, ax ; 视频段选择子(目的)
  6. mov edi, (80 * 13 + 0) * 2 ; 屏幕第 13 行, 0 列。
  7. mov ah, 0Ch ; 0000: 黑底 1100: 红字
  8. mov al, 'L'
  9. mov [gs:edi], ax
  10. ; 准备经由16位代码段跳回实模式
  11. jmp SelectorCode16:0
  12. CodeALen equ $ - LABEL_CODE_A
  13. ; END of [SECTION .la]

; CodeRing3的定义(ring3下的代码段)

; 显然, ring3 中的任务执行完后, jmp $ 这句代码使得程序 就会 永久停留在该处;

  1. [SECTION .ring3]
  2. ALIGN 32
  3. [BITS 32]
  4. LABEL_CODE_RING3:
  5. mov ax, SelectorVideo
  6. mov gs, ax
  7. mov edi, (80 * 14 + 0) * 2
  8. mov ah, 0Ch
  9. mov al, '3'
  10. mov [gs:edi], ax
  11. jmp $
  12. SegCodeRing3Len equ $ - LABEL_CODE_RING3
  13. ; END of [SECTION .ring3]



【总结-Conclusion】

  • C1)实现ring0 jmp 到ring3, 使用的是ret 指令,代码如下:(核心代码)

    ; 以下代码分步骤演示了如何将A的堆栈拷贝到B的堆栈(A==调用者,B=被调用者)

    ; 1.将ring3的堆栈段描述符的选择子压栈 [add](堆栈段描述符 存储有新堆栈的基地址ss)

    1. push SelectorStack3

    ; 2.将ring3的堆栈压栈[add](新栈顶指针esp压栈)

    1. push TopOfStack3

    ; 3.将ring3的堆栈值 压栈 [add] (将ring3 下的任务代码段基地址cs 压栈)

    1. push SelectorCodeRing3
    2. ; (偏移量ip == 0 压栈)
    3. push 0

; 4.retf 弹出ip + 弹出cs;此时ip==0, cs==SelectorCodeRing3,然后CPU自动解析出SelectorCodeRing3 索引(定位)的段描述符, 该描述符存储有

; 4.ring3下代码段LABEL_CODE_RING3 的基地址;

  1. 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)的更多相关文章

  1. JavaScript 执行环境(执行上下文) 变量对象 作用域链 上下文 块级作用域 私有变量和特权方法

    总结自<高程三>第四章  理解Javascript_12_执行模型浅析   JS的执行环境与作用域  javascript高级程序第三版学习笔记[执行环境.作用域] 在javascript ...

  2. C/C++学习笔记---高地址、低地址、大段字节序、小段字节序

    字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端.大端两种字节顺序. 小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处: 大端字节序是高字节数据存放在低地址 ...

  3. 一段显示隐藏列表HTML代码

    一段显示隐藏列表HTML代码, 技巧在于把页面上的元素(“返回首页”)和控制显示/隐藏的元素(id=navs-menu)放在一个共同的div上,并在该div上绑定onmouseover和onmouse ...

  4. SqlServer高版本数据库数据备份到低版本数据库上

    想要将Sqlserver2014高版本备份的数据还原到低版本SqlServer2012上去,但是这在SqlServer中是没法直接还原数据库的,通过以下方法可以顺利还原. 通过高版本生成sql脚本在低 ...

  5. 21 段实用便捷的 PHP 代码

    PHP 是目前使用最广泛的基于 Web 的编程语言,驱动着数以百万计的网站,其中也包括如 Facebook 等一些大型站点.这里收集了 21 段实用便捷的 PHP 代码摘录,对每种类型的 PHP 开发 ...

  6. linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  7. “段寄存器”的故事[转](彻底搞清内存段/elf段/实模式保护模式以及段寄存器)

    http://blog.csdn.net/michael2012zhao/article/details/5554023 一. 段寄存器的产生 段寄存器的产生源于Intel 8086 CPU体系结构中 ...

  8. VS开发中的代码编写小技巧——避免重复代码编写的几种方法

    上一篇文章中程序员的幸福生活--有你的日子,每天都是情人节,收到了大家的很多好评.鼓励和祝福,非常感动,真诚的谢谢大家.也希望每个朋友都能保持一个积极向上的心态,去迎接丰富多彩的人生. 在开发过程中, ...

  9. Java中静态代码块、构造代码块、构造函数、普通代码块

    在Java中,静态代码块.构造代码块.构造函数.普通代码块的执行顺序是一个笔试的考点,通过这篇文章希望大家能彻底了解它们之间的执行顺序. 1.静态代码块 ①.格式 在java类中(方法中不能存在静态代 ...

随机推荐

  1. 嵌入式 Linux线程锁详解pthread_mutexattr_t【转】

    转自:http://blog.sina.com.cn/s/blog_8795b0970101il6g.html 在Posix Thread中定义有一套专门用于线程同步的mutex函数. . 创建和销毁 ...

  2. 剖析CPU温度监控技术【转】

    转自:http://blog.csdn.net/hunanchenxingyu/article/details/46476545 迄今为止还没有一种cpu散热系统能保证永不失效.失去了散热系统保护伞的 ...

  3. JavaScript-性能优化,函数节流(throttle)与函数去抖(debounce)

    我在写一个类似百度搜索框的自动提示功能时候,使用了AJAX+keydown事件.调试时候我发现,当在搜索框中输入文字的时候,控制台在不停发送AJAX.这在本地服务器测试还好,如果我把它拿到运行环境,很 ...

  4. POJ 3264 Balanced Lineup RMQ ST算法

    题意:有n头牛,编号从1到n,每头牛的身高已知.现有q次询问,每次询问给出a,b两个数.要求给出编号在a与b之间牛身高的最大值与最小值之差. 思路:标准的RMQ问题. RMQ问题是求给定区间内的最值问 ...

  5. [转] makeFile文件作用

    源文件地址 makefile关系到了整个工程的编译规则.一个工程中的源文件不计数,其按类型.功能.模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后 ...

  6. Http头 Range、Content-Range(http断点续传原理)

    HTTP头中一般断点下载时才用到Range和Content-Range实体头,Range用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)Content-Rang ...

  7. 【IntelliJ Idea】idea快速创建maven spring项目

    想试试AOP在spring的web项目上的使用情况,所以想尽快使用idea快速的搭建一个spring的web项目,当然,是maven管理的项目 步骤如下: 1.打开idea 左上角file---> ...

  8. linux下crontab使用笔记

    1. 安装     service crond status     yum install vixie-cron     yum install crontabs 2. 实例         每分钟 ...

  9. java编程思想第四版第9章

    练习3: public class MainTest { public static void main(String args[]){ Bcycle b=new Bcycle(); b.print( ...

  10. MetaQ简单实用demo

    Metaq的生产者代码 import java.io.BufferedReader; import java.io.InputStreamReader; import com.taobao.metam ...