逻辑地址到线性地址的转换
段基址
如果开启了分页选项,那么线性地址就是物理地址,无需再进行转换.
段基地址(Base Address): 32位
段界限(Limit): 描述段的大小,20位
段属性(Attributes): 属性中的最高位G(粒度位)表示字节单位还是4K字节为单位.
段大小计算公式
1.字节方式
段的上限 = 基地址 + 段界限
比如:基地址:00012345H 段界限:5678H
段的上限 = 00012345H + 5678H = 000179BDH
所以段的范围是:000179BDH -- 000179BDH
2.4K方式
段的上限 = 00012345H + 5678H * 4K + 0FFFH = 0568B344H
4K方式追加FFFH的缘由
上面的计算方式中,后面加了一个0FFFH,这是因为20位段界限表示最大值为0FFFFFH,那么
段上限 = 0FFFFFH * 4K = 0FFFFF000H,不能够完全表示4G的范围,所以后面追加一个0FFFH,这样段上限 = 0FFFFFH * 4K + 0FFFH = 0FFFFF000H + 0FFFH = 0FFFFFFFFH,这样
0 -- 0FFFFFFFFH刚好可以表示4GB.
存储段描述符
描述符
描述一个段可以有三个东西:1.
段基址 2.
段界限 3.
段属性
用于表示这三个东西的数据结构就是描述符,可以理解成一个结构体.
描述符分类:
1.存储段描述符(存放段:
存放程序可以直接访问的代码和数据的段)
2.系统段描述符
3.门描述符
存储描述符的格式
描述符长8个字节
用位段定位该数据结构
为什么段界限会分开?
为了兼容以前的CPU型号
全局(GDT)和局部描述符表(LDT)
任务
任务:可以理解成线程,每个线程需要一个描述符来描述
描述符表
描述符表:由描述符组成的线性表,描述符表包括:GDT,LDT,IDT.
单核的系统中,GDT和IDT只有一张,LDT每个线程都可以有一张.
多核的系统中,GDT和IDT也可能不止一张.
存储描述符表的特殊数据段最多可包含8K(8192)个描述符.
一个任务可使用的整个虚拟地址空间分为相等的两半,一半空间的描述符在全局描述符表中,另一半空间的描述符在局部描述符表中.
由于全局和局部描述符表都可以包含多达8192个描述符,而每个描述符所描述的段的最大值可达4G字节,因此最大的虚拟地址空间可为:
4GB*8192*2=64MMB=64TB
段选择子
段选择子是神马?
段选择子替代了实模式下面的段值.也就是段描述符在段描述符表中的下标.
长度:16位
OD里面查看选择子,也就是各段寄存器里面的值
windows的段描述符表
windows没有采用Intel的LDT,只采用GDT.
可以使用windbg来查看GDT
试验:显示选择子0到100的段描述符
全局描述符表寄存器GDTR
GDTR中的低16位表示界限,GDTR中的高32位表示基地址,前面说了描述符表都存放在特殊的数据段中.
用windbg查看GDTR的值:
使用特权指令获取GDTR的值
部分查询类特权指令3环也可以使用,但是修改类特权指令3环一般是不能用的.
试验:VC来打印GDTR
效果:
我的机器是双核的,说明多核的情况下GDT表可以不止一张.
再谈段选择子
实模式下: 逻辑地址 由 段地址 + 段内偏移地址 组成
保护模式下: 虚拟地址(逻辑地址) 由 段选择子 + 段内偏移 组成.
很明显,段选择子取代了段地址的地位,那么说明段选择子可以确定哪一个段,以及段的起始地址.
因为段描述符都存放在段描述表中的,段描述表相当于一个存放段描述符的数组,那么要确定哪一个段描述符,想想缺少什么条件?对,就是数组下标,段选择子,就是用来确定段描述符在段描述符表中的下标的.
TI位: 取值0:从GDT中读取描述符 取值1:从LDT中读取描述符
由于windows值采用了GDT,所以相应的TI位总是为0.
解析windows某一时刻的段选择子
Ring0
ds,es两个选择子为啥显得怪异?
明明当前是ring0,为什么请求特权级(RRL)对应项是ring3,这是由于内核态只需要用到cs(代码),ss:(堆栈),fs(内核相关数据结构),而不需要用到ds,es,所以ds,es两个选择子并没有进行切换,所以显示的还是原来ring3的选择子的值,所以看起来有的怪异.
对照windbg的GDT表
和以上的手工分析是一致的.
Ring3
如何让选择子切换到3环呢?
我们知道API实现内部都会调用ntdll模块中的快速系统调用这个函数
所以只需要在ntdll!KiFastSystemCall下断点即可.
运行起来,就会断下KiFastSystemCall的入口处,这个时候的选择子就是3环的
GDT索引 TI RPL
cs:0x001b <=> 0000000000011(索引号3) 0(GDT) 11 (ring3)
ss:0x0023 <=> 0000000000100(索引号4) 0 (GDT) 11(ring3)
ds:0x0023 <=> 0000000000100(索引号4) 0 (GDT) 11(ring3)
es:0x0023 <=> 0000000000100(索引号4) 0 (GDT) 11(ring3)
fs:0x003b <=> 0000000000111(索引号7) 0(GDT) 11(ring3)
对照windbg的GDT表
和手工分析同样是一致的.
windows间接屏蔽掉了VA(虚拟地址)到线性地址的转换
根据上面的各个段的起始地址都是从0开始的,所以表示的范围从0到4GB,这样就造成了虚拟机即线程地址.
例如:
ss:00401000 ==> 线性地址 <==> 0 + 00401000 = 00401000
所以,
windows里面的 虚拟地址 等价于 线性地址
更多保护模式内容请参看附件,这里一并奉上.
驱动加载工具DIY.doc
windbg命令.doc
应用层和驱动层的通讯.doc
API Ring3切换Ring0流程.doc
通过华生医生抓取崩溃Dump文件.doc
保护模式-分段机制.doc
分段管理之段选择子.doc
分页管理机制.doc
启用PAE后虚拟地址到物理地址的转换.doc
解析VA(804e3a42) --》PA.doc
解析explore.exe中0x77EF1000.doc
在虚拟机中,对calc.exe进程做修改,让关于弹不出.doc
Ring3与Ring0的通讯.doc
模拟API-ReadProcessMemory.doc
ReadProcessMemory简单分析.doc
[课程]Android-CTF解题方法汇总!