保护模式之段相关的笔记

85 篇文章 6 订阅

段寄存器简介

段寄存器SelectorAttributeBaseLimit
ES++0023++可读可写00xFFFFFFFF
CS++001B++可读可执行00xFFFFFFFF
SS++0023++可读可写00xFFFFFFFF
DS++0023++可读可写00xFFFFFFFF
FS++003B++可读可写++ 0x7FFDE000++0xFFF
GSwindows没有使用---

注释:加了下划线可能会变!

探测Attribute存在

#include int "stdafx.h"
 int main(int argc,
 char* argu[])
{
__asm{
mov ax,ss;//cs不行,cs是不可写的
mov ds,ax
mov dword ptr ds:[var],eax;
}
}
 return 0;

探测Base存在

int var = 1;                    
__asm                   
{                   
    mov ax,fs               
    mov gs,ax               
    mov eax,gs:[0x1000]//读取不成功,因为fs大小是FFF   //不要用DS 否则编译不过,这个不是真正0地址,而是关于基址的偏移
    mov dword ptr ds:[var],eax  

    //mov edx,dword ptr ds:[0x7FFDF000]
}

探测Limit存在

int var = 1;                    
__asm                   
{                   
    mov ax,fs               
    mov gs,ax               
    mov eax,gs:[0]  //不要用DS 否则编译不过去     
    mov dword ptr ds:[var],eax  

    //mov edx,dword ptr ds:[0x7FFDF000]
}

GDT(全局描述符表)LDT(局部描述符表)

当我们执行类似MOV DS,AX指令时,CPU会查表,根据AX的值来决定
查找GDT还是LDT,查找表的什么位置,查出多少数据.GDT是一张表,GDTR是个寄存器,存了GDT起始位置和有多少个元素。

段描述符与段选择子

段描述符

段描述符

段选择子

段选择子是一个16位的段描述符,该描述符指向了定义该段的段描述符.
段选择子

RPL:请求特权级别

TI:
TI=0 查GDT表
TI=1 查LDT表

Index:
处理器将索引值乘以8
在加上GDT或者LDT的
基地址,就是要加载的
段描述符

加载段描述符至段寄存器

除了MOV指令,我们还可以使用LES、LSS、LDS、LFS、LGS指令修改寄存器.
CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CS与EIP一起改,后面会讲.

char buffer[6];                 
__asm                           
{           
    les ecx,fword ptr ds:[buffer] //高2个字节给es,低四个字节给ecx  
}

注意:RPL<=DPL(在数值上)

段描述符属性

P位

P = 1 段描述符有效
P = 0 段描述符无效
3、段描述符与段寄存器的对应关系

WORD Selector;//16位
WORD Atrribute;//16位
DWORD Base;//32位
DWORD Limit;//32位 FFFFFFFF

S位

S = 1 代码段或者数据段描述符
S = 0 系统段描述符

Tpye域

因为P,DPL,S位,Type要么是1001(访问本段权限0),要么是1111(访问本段权限3),所以GDT表里第五5要是9或者F才是代码段或者数据段。又因为Type与第一位为1是代码段,所以第六位要是大于8就是代码段。
E 向上就是LImit里有效,扩展方向向下是Limit取反才是有效的。

系统段描述符

系统段Type域

DB位

情况一:对CS段的影响
D = 1 采用32位寻址方式
D = 0 采用16位寻址方式
前缀67 改变寻址方式

情况二:对SS段的影响
D = 1 隐式堆栈访问指令(如:PUSH POP CALL)使用32位堆栈指针寄存器ESP
D = 0 隐式堆栈访问指令(如:PUSH POP CALL) 使用16位堆栈指针寄存器SP

情况三:向下拓展的数据段

D = 1 段上线为4GB
D = 0 段上线为64KB
向上与向下扩展

段权限检查

位置用途
CPL存放在CS和SS段选择子的最后两位更其他无关当前程序特权级 ,CPU处于哪一环
DPL存放在段描述符中要访问该段需要什么权限才能访问
RPL存放在段选择子的最后两位

如何查看程序处于几环?

CPL(Current Privilege Level) :当前特权级
CS和SS中存储的段选择子后2位,要一致,另外跟这两个有关,跟其他的东西无关
DPL(Descriptor Privilege Level) 描述符特权级别

DPL存储在段描述符中,规定了访问该段所需要的特权级别是什么.
通俗的理解:
如果你想访问我,那么你应该具备什么特权.
举例说明:
mov DS,AX 如果AX指向的段DPL = 0 但当前程序的CPL = 3 这行指令是不会成功的!
RPL(Request Privilege Level) 请求特权级别
RPL是针对段选择子而言的,每个段的选择子都有自己的RPL

举例说明:

Mov ax,0008 与 Mov ax,000B//段选择子
Mov ds,ax Mov ds,ax//将段描述
指向的是同一个段描述符,但RPL是不一样的.

数据段的权限检查

参考如下代码:
比如当前程序处于0环,也就是说CPL=0
Mov ax,000B //1011 RPL = 3
Mov ds,ax //ax指向的段描述符的DPL = 0
数据段的权限检查:
CPL <= DPL 并且 RPL <= DPL (数值上的比较)
注意:
代码段和系统段描述符中的检查方式并不一样,具体参加后面课程.

总结:

CPL CPU当前的权限级别
DPL 如果你想访问我,你应该具备什么样的权限
RPL 用什么权限去访问一个段

为啥要有RPL?

我们本可以用“读写”的权限去打开一个文件,但为了避免出错,有些时候我们使用“只读”的权限去打开。

跨段跳转

代码间的跳转(段间跳转 非调用门之类的)

段间跳转,有2种情况,即要跳转的段是一致代码段还是非一致代码段
同时修改CS与EIP的指令
JMP FAR / CALL FAR / RETF / INT /IRETED
注意:
只改变EIP的指令
JMP / CALL / JCC / RET

JMP 0x20:0x004183D7 CPU如何执行这行代码?

(1) 段选择子拆分

0x20 对应二进制形式 0000 0000 0010 0000

RPL = 00   
TI = 0
Index = 4

(2) 查表得到段描述符

TI = 0 所以查GDT表

Index = 4 找到对应的段描述符

四种情况可以跳转:代码段、调用门、TSS任务段、任务门

(3) 权限检查

如果是非一致代码段,要求:CPL == DPL 并且 RPL <= DPL

如果是一致代码段,要求:CPL >= DPL

(4) 加载段描述符

通过上面的权限检查后,CPU会将段描述符加载到CS段寄存器中.

(5) 代码执行

    CPU将 CS.Base + Offset 的值写入EIP 然后执行CS:EIP处的代码,段间跳转结束.

总结:

  1. 对于一致代码段:也就是共享的段

    1. 特权级高的程序不允许访问特权级低的数据:核心态不允许访问用户态的数据
    2. 特权级低的程序可以访问到特权级高的数据,但特权级不会改变:用户态还是用户态
  2. 对于普通代码段:也就是非一致代码段
    1. 只允许同级访问
    2. 绝对禁止不同级别的访问:核心态不是用户态,用户态也不是核心态.

直接对代码段进行JMP 或者CALL的操作,无论目标是一致代码段还是非一致代码段,CPL都不会发生改变.如果要提升CPL的权限,只能通过调用门.

长调用与短调用

短调用
指令格式:CALL 立即数/寄存器/内存

三种调用图

调用门

调用门执行流程

指令格式:CALL CS:EIP(EIP是废弃的)
执行步骤:
1. 根据CS的值 查GDT表,找到对应的段描述符,这个描述符是一个调用门.
2. 在调用门描述符中存储另一个代码段段的选择子.
3. 这个调用门的选择子指向的段 段.Base +这个调用门的 偏移地址,就是真正要执行的地址.

门描述符

构造一个调用门(无参,提权,不提权8的位置是1B)

0040EC00 000810D0放入gdt里没用到的位置。

构造一个带参数的调用门

调用门描述符:0040EC03 00081030

eq 8003f048 0040EC03`00081030

调用门总结:

1) 当通过门,权限不变的时候,只会PUSH两个值:CS 返回地址
新的CS的值由调用门决定

2) 当通过门,权限改变的时候,会PUSH四个值:SS ESP CS 返回地址 新的CS的值由调用门决定 新的SS和ESP由TSS提供

3) 通过门调用时,要执行哪行代码有调用门决定,但使用RETF返回时,由堆栈中压人的值决定,这就是说,进门时只能按指定路线走,出门时可以翻墙(只要改变堆栈里面的值就可以想去哪去哪)
4) 可不可以再建个门出去呢?也就是用Call 当然可以了 前门进 后门出

中断门

Windows没有使用调用门,但是使用了中断门:

<1> 系统调用

<2> 调试

IDT

IDT即中断描述符表,同GDT一样,IDT也是由一系列描述符组成的,每个描述符占8个字节。但要注意的是,IDT表中的第一个元素不是NULL。在windbg中查看IDT表的基址和长度:

老的CPU用的中断门,新的用的快速调用

IDT表都是系统段描述符,都不是空(调用门就有可能是代码数据段,和有空了)

中断门Type是1110,陷阱门是1111

陷阱门与中断门的区别

中断门执行时,将IF位清零,但陷阱门不会。

在调用门、中断门与陷阱门中,一旦出现权限切换,那么就会有堆栈的切换。而且,由于CS的CPL发生改变,也导致了SS也必须要切换。

切换时,会有新的ESP和SS(CS是由中断门或者调用门指定)这2个值从哪里来的呢?

答案:TSS (Task-state segment ),任务状态段.

TSS的结构

TSS是一块内存,不是寄存器不在CPU中,内存结构如下图,大小104字节

TSS的结构

TSS的作用

Intel的设计思想

操作系统的设计思想

本质:

    不要把TSS与“任务切换”联系到一起

    TSS的意义就在于可以同时换掉”一堆”寄存器

CPU如何找到TSS呢? TR段寄存器

TR寄存器

CPU找TSS直接在TR寄存器,TR的Base指向TSS,Limit是Tss有多大,TSS是操作系统启动时从GDT加载的。TSS是系统段的一种。所以S为是0,Type是1001,说明没有加载到TR寄存器中,1011时是已经加载到TR寄存器。

TR寄存器读写

  1. 将TSS段描述符加载到TR寄存器
    指令:LTR
    说明:
    用LTR指令去装载的话 仅仅是改变TR寄存器的值(96位)
    并没有真正改变TSS
    LTR指令只能在系统层使用
    加载后TSS段描述符会状态位会发生改变就是上面9变B
  2. 读TR寄存器
    1. 指令:STR
    说明:如果用STR去读的话,只读了TR的16位 也就是选择子

注释

TSS是一块内存,大小104字节.通过TSS可以同时替换“一堆”寄存器,包括通用寄存器和段寄存器等.

CPU通过TR段寄存器来找到TSS.如果我们想用自己的TSS段来替换原来的寄存器,就要修改TR寄存器,TR寄存器的值又是来自TSS段描述符,那么我们接下来先构造一个段描述符。

修改TR寄存器

1) 在Ring0 我们可以通过LTR指令去修改TR寄存器

2) 在Ring3 我们可以通过CALL FAR 或者 JMP FAR指令来修改

用JMP去访问一个代码段的时候,改变的是CS和EIP :

JMP 0x48:0x123456  如果0x48是代码段  
执行后:CS-->0x48  EIP-->0x123456

用JMP去访问一个任务段的时候:

如果0x48是TSS段描述符,先修改TR寄存器,在用TR.Base指向
的TSS中的值修改当前的寄存器

任务门描述符

Type为0101即5.
任务门

这是TSS段的描述符(在GDT表),任务门本身在IDT表

任务门执行过程:

INT N

查IDT表,找到中断门描述符

通过中断门描述符,查GDT表,找到任务段描述符

使用TSS段中的值修改寄存器

IRETD返回

参考资料:

[1]:滴水视频

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值