逻辑地址到线性地址的转换
段基址
如果开启了分页选项,那么线性地址就是物理地址,无需再进行转换.
段基地址(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.门描述符
存储描述符的格式
附件 84245
描述符长8个字节
用位段定位该数据结构
附件 84246
为什么段界限会分开?
为了兼容以前的CPU型号
全局(GDT)和局部描述符表(LDT)
任务
任务:可以理解成线程,每个线程需要一个描述符来描述
描述符表
描述符表:由描述符组成的线性表,描述符表包括:GDT,LDT,IDT.
单核的系统中,GDT和IDT只有一张,LDT每个线程都可以有一张.
多核的系统中,GDT和IDT也可能不止一张.
存储描述符表的特殊数据段最多可包含8K(8192)个描述符.
一个任务可使用的整个虚拟地址空间分为相等的两半,一半空间的描述符在全局描述符表中,另一半空间的描述符在局部描述符表中.
由于全局和局部描述符表都可以包含多达8192个描述符,而每个描述符所描述的段的最大值可达4G字节,因此最大的虚拟地址空间可为:
4GB*8192*2=64MMB=64TB
段选择子
段选择子是神马?
段选择子替代了实模式下面的段值.也就是段描述符在段描述符表中的下标.
长度:16位
OD里面查看选择子,也就是各段寄存器里面的值
附件 84247
windows的段描述符表
windows没有采用Intel的LDT,只采用GDT.
可以使用windbg来查看GDT
附件 84248
试验:显示选择子0到100的段描述符
附件 84249
全局描述符表寄存器GDTR
GDTR中的低16位表示界限,GDTR中的高32位表示基地址,前面说了描述符表都存放在特殊的数据段中.
用windbg查看GDTR的值:
附件 84250
使用特权指令获取GDTR的值
附件 84251
部分查询类特权指令3环也可以使用,但是修改类特权指令3环一般是不能用的.
试验:VC来打印GDTR
附件 84252
效果:
附件 84253
我的机器是双核的,说明多核的情况下GDT表可以不止一张.
再谈段选择子
实模式下: 逻辑地址 由 段地址 + 段内偏移地址 组成
附件 84254
保护模式下: 虚拟地址(逻辑地址) 由 段选择子 + 段内偏移 组成.
附件 84255
很明显,段选择子取代了段地址的地位,那么说明段选择子可以确定哪一个段,以及段的起始地址.
因为段描述符都存放在段描述表中的,段描述表相当于一个存放段描述符的数组,那么要确定哪一个段描述符,想想缺少什么条件?对,就是数组下标,段选择子,就是用来确定段描述符在段描述符表中的下标的.
附件 84256
TI位: 取值0:从GDT中读取描述符 取值1:从LDT中读取描述符
由于windows值采用了GDT,所以相应的TI位总是为0.
解析windows某一时刻的段选择子
Ring0
附件 84257
ds,es两个选择子为啥显得怪异?
明明当前是ring0,为什么请求特权级(RRL)对应项是ring3,这是由于内核态只需要用到cs(代码),ss:(堆栈),fs(内核相关数据结构),而不需要用到ds,es,所以ds,es两个选择子并没有进行切换,所以显示的还是原来ring3的选择子的值,所以看起来有的怪异.
对照windbg的GDT表
附件 84258
和以上的手工分析是一致的.
Ring3
如何让选择子切换到3环呢?
我们知道API实现内部都会调用ntdll模块中的快速系统调用这个函数
附件 84259
所以只需要在ntdll!KiFastSystemCall下断点即可.
附件 84260
运行起来,就会断下KiFastSystemCall的入口处,这个时候的选择子就是3环的
附件 84261
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表
附件 84262
和手工分析同样是一致的.
windows间接屏蔽掉了VA(虚拟地址)到线性地址的转换
根据上面的各个段的起始地址都是从0开始的,所以表示的范围从0到4GB,这样就造成了虚拟机即线程地址.
例如:
ss:00401000 ==> 线性地址 <==> 0 + 00401000 = 00401000
所以,windows里面的 虚拟地址 等价于 线性地址
更多保护模式内容请参看附件,这里一并奉上.
附件 84263
附件 84264
附件 84265
附件 84266
附件 84267
附件 84268
附件 84269
附件 84270
附件 84271
附件 84272
附件 84273
附件 84274
附件 84275
附件 84276
附件 84277
段基址
如果开启了分页选项,那么线性地址就是物理地址,无需再进行转换.
段基地址(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.门描述符
存储描述符的格式
附件 84245
描述符长8个字节
用位段定位该数据结构
附件 84246
为什么段界限会分开?
为了兼容以前的CPU型号
全局(GDT)和局部描述符表(LDT)
任务
任务:可以理解成线程,每个线程需要一个描述符来描述
描述符表
描述符表:由描述符组成的线性表,描述符表包括:GDT,LDT,IDT.
单核的系统中,GDT和IDT只有一张,LDT每个线程都可以有一张.
多核的系统中,GDT和IDT也可能不止一张.
存储描述符表的特殊数据段最多可包含8K(8192)个描述符.
一个任务可使用的整个虚拟地址空间分为相等的两半,一半空间的描述符在全局描述符表中,另一半空间的描述符在局部描述符表中.
由于全局和局部描述符表都可以包含多达8192个描述符,而每个描述符所描述的段的最大值可达4G字节,因此最大的虚拟地址空间可为:
4GB*8192*2=64MMB=64TB
段选择子
段选择子是神马?
段选择子替代了实模式下面的段值.也就是段描述符在段描述符表中的下标.
长度:16位
OD里面查看选择子,也就是各段寄存器里面的值
附件 84247
windows的段描述符表
windows没有采用Intel的LDT,只采用GDT.
可以使用windbg来查看GDT
附件 84248
试验:显示选择子0到100的段描述符
附件 84249
全局描述符表寄存器GDTR
GDTR中的低16位表示界限,GDTR中的高32位表示基地址,前面说了描述符表都存放在特殊的数据段中.
用windbg查看GDTR的值:
附件 84250
使用特权指令获取GDTR的值
附件 84251
部分查询类特权指令3环也可以使用,但是修改类特权指令3环一般是不能用的.
试验:VC来打印GDTR
附件 84252
效果:
附件 84253
我的机器是双核的,说明多核的情况下GDT表可以不止一张.
再谈段选择子
实模式下: 逻辑地址 由 段地址 + 段内偏移地址 组成
附件 84254
保护模式下: 虚拟地址(逻辑地址) 由 段选择子 + 段内偏移 组成.
附件 84255
很明显,段选择子取代了段地址的地位,那么说明段选择子可以确定哪一个段,以及段的起始地址.
因为段描述符都存放在段描述表中的,段描述表相当于一个存放段描述符的数组,那么要确定哪一个段描述符,想想缺少什么条件?对,就是数组下标,段选择子,就是用来确定段描述符在段描述符表中的下标的.
附件 84256
TI位: 取值0:从GDT中读取描述符 取值1:从LDT中读取描述符
由于windows值采用了GDT,所以相应的TI位总是为0.
解析windows某一时刻的段选择子
Ring0
附件 84257
ds,es两个选择子为啥显得怪异?
明明当前是ring0,为什么请求特权级(RRL)对应项是ring3,这是由于内核态只需要用到cs(代码),ss:(堆栈),fs(内核相关数据结构),而不需要用到ds,es,所以ds,es两个选择子并没有进行切换,所以显示的还是原来ring3的选择子的值,所以看起来有的怪异.
对照windbg的GDT表
附件 84258
和以上的手工分析是一致的.
Ring3
如何让选择子切换到3环呢?
我们知道API实现内部都会调用ntdll模块中的快速系统调用这个函数
附件 84259
所以只需要在ntdll!KiFastSystemCall下断点即可.
附件 84260
运行起来,就会断下KiFastSystemCall的入口处,这个时候的选择子就是3环的
附件 84261
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表
附件 84262
和手工分析同样是一致的.
windows间接屏蔽掉了VA(虚拟地址)到线性地址的转换
根据上面的各个段的起始地址都是从0开始的,所以表示的范围从0到4GB,这样就造成了虚拟机即线程地址.
例如:
ss:00401000 ==> 线性地址 <==> 0 + 00401000 = 00401000
所以,windows里面的 虚拟地址 等价于 线性地址
更多保护模式内容请参看附件,这里一并奉上.
附件 84263
附件 84264
附件 84265
附件 84266
附件 84267
附件 84268
附件 84269
附件 84270
附件 84271
附件 84272
附件 84273
附件 84274
附件 84275
附件 84276
附件 84277