保护模式,主要学习的就是段 与 页 的关系. 学习段的时候先学习段寄存器
所以先从段开始学.
先看一段汇编 代码如下
mov eax,fs:[0]
mov eax,fs:[eax + 0x30]
上面的代码学过 TEB PEB 结构的人应该知道.是在做什么. 而我么你这里所说不讲 PEB TEB
看汇编代码.我们操作了FS; FS就是段寄存器 我们这里操作的地址分别就是 FS:[0] 与 fs:[0x30]
而其实展开来说. 我们真正的操作的地址是 FS.base + 0 或者 FS.base + 0x30 这样来操作的.
出了FS寄存器.还有 ES CS SS DS GS LDTR TR等段寄存器
我们上面所说的 段.base 其实是段寄存器的一个成员. 可以理解为段寄存器就是一个结构体
PS: 在inter手册中也称为段寄存器为段描述符
在我们x86平台下.我们知道一个寄存器是 4个字节. 32位. 可以表达一个32位的数据. 但是我们的段
却很少有人关注 其实我们的段 是有96位的.是一个结构体.
结构如下:
struct Segment
{
WORD Selecter; //16位段选择子 可见部分. 使用OD 或者X64dbg看段寄存器只会显示16位的段选择子可见部分.
WORD Attribute; //16位表示的段属性, 表示了当前段寄存器是可读的可写的还是可执行的
DWORD Base; //32位表示的基址,表示段从哪里开始
DWORD limit; //32位表示,表示的是基址的长度. base + limit 可以确定一个段的大小
}
使用汇编可以对段寄存器进行读写.
读取
mov ax,ss 段寄存器的可见部分只有16位.所以读出来之后只能放到16位寄存器中
写入
mov ss,ax 读寄存器只是读了可见部分的16位.而写入寄存器则是写入了96位
inter手册对段寄存器的操作寄存器指令有以下
mov pop LDS LES LSS LGS LFS L是Load的意思
在x86下.我们可以看如下寄存器表示图.
寄存器名称 | 段选择子(Select) | 段属性(Attributes) | 段基址(Base) | 段长(Limit) |
---|---|---|---|---|
ES(附加扩展段) | 0x0023 | 可读,可写 | 0x0000000 | 0xFFFFFFFF |
CS(代码段) | 0x001B | 可读,可执行 | 0x00000000 | 0xFFFFFFFF |
SS(堆栈段) | 0x0023 | 可读,可写 | 0x00000000 | 0xFFFFFFFF |
DS(数据段) | 0x0023 | 可读,可写 | 0x00000000 | 0xFFFFFFFF |
FS(分段机制) | 0x003B | 可读,可写 | 0x7FFDF000 | 0xFFF |
GS | 未使用 | 未使用 | 未使用 | 未使用 |
我们可以巧妙的利用汇编.来探测一下段属性 是否存在的. 依靠段寄存器结构所知. 出了CS段 不能写之外.其余段都是可以写的.
那么看一下内敛汇编代码.
根据上面我们可以轻而一举的探测出 CS段 带有不可写的属性. 那么同样我们可以探测一下基地址.
在上图我们得知. 有基地址的只有FS. 看如下代码.
int main()
{
__asm
{
mov ax,fs
mov gs,ax
mov eax,gs:[0]
}
}
看汇编我们可以看到. 为啥访问 gs:[0]不会出错那. 有基础的都应该知道. 0地址是不可读的. 其实我们说过. 我们对任何一个地址的操作,操作的都是它的 段.base + 偏移的方式. 在实模式下.这个概念应该知道.到了保护模式下. 段base为0了. 所以偏移就是我们看到的虚拟地址.
上面的汇编是一样的. FS是有基地址的. 当其赋值给gs的时候. gs代表的就是fs. 所以用 gs去操作[0]地址是有效的. 等价于操作 fs:[0]
而FS:[0] 就是我们的TEB结构.
在段地址探测中,我们学习到了 访问有效地址 都等价于 段.base + 偏移地址 x86下.偏移地址就是我们所看到的虚拟地址. 也称为逻辑地址.
那么我们如何探测 段长? 根据段寄存器表格得知. FS的 limit 只有0xFFF大小. 我们可以写汇编代码验证一下
int main()
{
__asm
{
mov ax, fs
mov gs, ax
mov eax, gs: [0] //fs.base + 0 读取
mov eax,gs:[0x1000]//fs.base + 0x1000
mov eax, dword ptr ds : [eax + 0xFFF] ;
// mov eax,gs:[0x1000] //fs.base + 0x1000 读取
}
}
可能是我们win764上测试的. 读取长度.越界读取.都会导致程序崩溃.