吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5017|回复: 5
收起左侧

[系统底层] 保护模式笔记三 段描述符和段选择子

  [复制链接]
lyl610abc 发表于 2021-4-12 13:53
本帖最后由 lyl610abc 于 2021-4-12 15:41 编辑

前言

所有保护模式索引链接:保护模式笔记一 保护模式介绍

先前了解了段寄存器,现在继续深入学习段寄存器


段描述符

引出问题

首先要解决的就是上个笔记遗留下来的问题:

mov bx,ds                //将段寄存器ds的Selector部分保存到bx(ecx的低16位)
mov ax,cs                //将段寄存器cs的Selector部分保存到ax(eax的低16位)
mov ds,ax                //将先前读出来的段寄存器去写ds这个段寄存器,也就是用cs段寄存器覆盖ds段寄存器

写寄存器是对整个96位的段寄存器进行修改,但是这里只给出了16位的段选择子Selector,剩下的80位呢


在回答问题之前,还需要了解两个结构:GDT(全局描述符表)和 LDT(局部描述符表)

为什么要了解这两张表?

因为当执行类似前面对段寄存器进行修改的指令:MOV DS,AX时,CPU会先查表,根据AX的值(段选择子)来决定查找GDT还是LDT

但在Windows中LDT并没有被使用,于是AX的值(段选择子)是用来决定查询表中的哪个位置


GDT

什么是GDT

GDT全称:Global Descriptor Table,为全局描述符表,表中存储的数据项为段描述符


GDT的数量

一个处理器对应一个GDT


定位GDT

大致了解了GDT是一张表,接下来则要定位到这张表,查看其内容

想要定位GDT表的位置,可以通过gdtr寄存器来定位

gdtr寄存器存储了GDT表的起始位置和GDT表的大小


通过windbg定位GDT

通过在windbg中输入下列指令查看有关GDT的信息:

r gdtr      //读取gdt表的起始位置
r gdtl      //读取gdt表的大小

image-20210411215712880


得到了:

GDT表的起始位置 GDT表的大小
0x8003f000 0x3ff
数据宽度 DWORD(4字节) WORD(2字节)

得到了GDT表的起始位置后,就可以查看GDT表的内容了:

dq 0x8003f000

image-20210411221254633


段描述符

知道了查询的表为GDT后,再说说GDT表存储的数据项:段描述符


什么是段描述符

段描述符顾名思义就是用来描述段的信息的,每个段对应一个段描述符


段描述符的数据宽度

每个段描述符的数据宽度为:64位=8字节(QWORD)


定位段描述符

通过段选择子可以定位到对应的段描述符

如何定位,则要先了解段选择子的结构


段选择子

什么是段选择子

段选择子顾名思义就是用来选择段的,通过段选择子可以定位到对应的段描述符


段选择子的结构

image-20210411222702417


Index TI RPL
含义 索引 表指示器 请求特权等级
全称 Index Table Indicator Requested Privilege Level
数据宽度 13位 1位 2位

Index

索引,真正用来索引段描述符的数据


TI

表指示器,用来确定选择GDT(全局描述符表)还是LDT(局部描述符表)

TI==0 TI==1
选择的表 GDT LDT

在Windows上并不使用LDT表,故TI恒等于0


RPL

请求的特权等级,会和请求的段描述符的特权等级进行比较,留作后续补充说明


根据段选择子定位段描述符

了解了段选择子的结构后,就可以通过段选择子来定位段描述符了

例子:以段选择子 = 0x001B为例

首先将段选择子转换为二进制 : 0000 0000 0001 1011

将其按段选择子的结构填入:

Index TI RPL
二进制值 0000 0000 0001 1 0 11
十进制值 3 0 3
含义 索引为3 查询GDT表 请求特权等级为3

得到的索引为3

拿到索引之后就可以定位对应的段描述符了

对应的段描述符地址 = GDT表首地址 + 索引× 段描述符长度 =  GDT表首地址 + 索引 × 8(注意这里的单位为字节,64位=8字节)

所以:对应的段描述符地址 =  0x8003f000 + 3×8= 0x8003f000 + 24 = 0x8003f000 + 0x18 = 0x8003f018

image-20210411225609994


加载段描述符至段寄存器

除了MOV指令,还可以使用LES、LSS、LDS、LFS、LGS指令修改寄存器.

CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CS与EIP一起改,在后续的笔记会提到


下面以lds为例子,观察指令执行前后寄存器的变化

#include <stdio.h>
#include <windows.h>

char buffer[6]={0x44,0x33,0x22,0x11,0x1B,0x00};

int main(){

    _asm{
        push ds
        lds eax,fword ptr ds:[buffer]   //fword为6字节
        pop ds

    }
        return 0;
}

下断点观察

image-20210412134301577


执行前

image-20210412132910807


执行后

image-20210412132938532


对比执行前后

EAX DS
执行前 0xCCCCCCCC 0x23
执行后 0x11223344 0x1B

得出指令功能

LDS指令格式为:LDS OPRD1,OPRD2

OPRD1用来接收OPRD2的低(OPRD-2)字节

OPRD2的高2字节为段选择子,通过段选择子修改DS

其它指令:LES、LSS、LFS、LGS也是一样的格式,只不过修改的段寄存器不同罢了


内存寻址关系一览图

下面给出内存寻址的流程中,GDT、段描述符、段选择子的关系图:

image-20210412153414326


以MOV EAX,DWORD PTR DS:[0x123456]为例

根据DS获得Segment Selector(段选择子):0x23(在 保护模式笔记二 段寄存器中获得的,不同机器可能不同)

根据地址获得Offset(偏移):0x123456

然后通过段选择子查询GDT(全局描述符表)得到对应的Segment Descriptor(段描述符)

通过段描述符可以得到Base(基地址)= 0 (DS段寄存器的Base为0)

最终要访问的内存地址为:Base+Offset = 0+0x123456=0x123456(期间也会根据段描述符进行一系列校验,这里暂且不提)


说明

该篇笔记主要介绍了如何通过段选择子定位到对应的段描述符并补充了段选择子的结构和修改段寄存器的指令

但关于段描述符的结构还没有深入介绍

前面引出的问题也尚未完全解决,通过前面的学习得知段寄存器剩下的80位是通过段描述符来填充的

但是段描述符的长度只有64位,如何填充80位?

这些都留作之后的笔记再作说明(づ ̄ 3 ̄)づ

免费评分

参与人数 5吾爱币 +4 热心值 +5 收起 理由
ICEY + 2 + 1 我很赞同!
niany + 1 我很赞同!
游叶子明 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Service123 + 1 + 1 热心回复!
爱你小吉君 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

yellowpeach 发表于 2021-4-15 11:18
我在52学代码
bestwars 发表于 2021-4-12 15:12
辉哥j 发表于 2021-4-13 00:17
c259179 发表于 2022-8-18 11:52

大佬威武!谢谢!
huchen 发表于 2024-3-8 14:28
请问,段选择子又是怎么确定的呢?如果是那些cs、ss等那些段寄存器来决定的话,咋看呀r cs吗?(在windbg中)
namesensen 发表于 2024-3-9 14:46
用心讨论,共获提升!感谢楼主无私奉献
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 14:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表