G(粒度)标志
在64位操作系统中 base段基址和limit限长以及G位就会被取消掉了
因为在32位系统中整体的段描述的范围大小是4GB,而64位的系统,只有32位的段是装不下的,所以就被废掉了.
DPL是权限位
limit限长是 0xFFFFF 2^20 = 1048576 / 1024 = 1024K = 1M,那么段限长最大为1M。
但是线长不是按照字节来算的,是与G标志位有关。
当G标志位为1时, 是按页来算的,1页有4kB,就是4096个字节
G =1时 : 0xFFFFF *4096(0x1000) = 0xFFFFF000 还是不满足4GB(0xFFFFFFFF)
但由于内存地址的特性,是从0开始计数的,所以 0~0xFFFFF,(0xFFFFF+1)*0x1000 = 10 0000 0000 -1 = 0xFFFFFFFF= 4GB
G位实验
测试修改G位的数值,检验当G=1时,数据是否存的下,当G=0,数据是否会发生异常.
将数据段的段描述符赋值到48的位置
eq 80b99000+48 00cff300
0000ffff`
测试代码
int g_value = 0x100;
int g_value1 = 0;
int main()
{
__asm
{
mov ax,0x48;
mov ds,ax;
mov eax,dword ptr ds:[g_value];
mov g_value1,eax;
mov ax,es;
mov ds,ax;
}
printf("g_value1 = %x\r\n",g_value1);
system("pause");
return 0;
}
G标志位为1,以页为单位,限长大小为4GB,能够将该地址的值写入eax
将G位值0,那么00cff3000000ffff就改为004ff300
0000ffff
eq 80b99000+48 004ff300
0000ffff`
执行代码后发生c5异常
总结:
- G标志位被称为粒度标志.
- 线长不是按照字节来算的,是与G标志位有关.
- G=0:段限长以字节为单位.,最大就是1M.
- G=1:段限长以页4KB为单位.最大就是4GB.
D/B(默认操作数大小/默认栈指针大小和/或上限)标志
根据这个段描述符所指的是一个可执行代码段,一个向下扩展的数据段还是一个堆栈
段,这个标志完成不同的功能。
D表示为默认:default 主要与Type有关.
对32位的代码和数据段,这个标志总是被置为1,而16位的代码和数据段,这个标志总是被置为0.
在32位系统中push eax
是4个字节,16位系统中,占2个字节.
这是如何区分的呢?
主要由D/B位来决定,如果D/B位为0,那么就认为你的操作数是16位系统,如果D/B位为1,那么你的操作数就是32位系统. 因此它决定你的堆栈寻址空间是16还是32位.
64位系统抛弃了base和limit以及G位
当D/B位的段描述符表示的是代码段时,则改变的是堆栈寻址的默认操作.
- 可执行代码段 这个标志被称为D标志,它指明该段中的指令所涉及的有效地址值
的缺省位位数和操作符的缺省位位数。如果该标志为1,缺省为32位的地址,32位
或者8位的操作符;若为0,缺省为16位的地址,16位或者8位的操作符。
- 堆栈段(由SS寄存器所指向的数据段) 这个标志被称为B(big)标志,它为隐含
的栈操作(如push,pop和call)确定栈指针值的位位数。如果该标志为1,则使用
的是32位的栈指针,该指针放在32位的ESP寄存器中;若该标志为0,则使用的是放
在16位 SP寄存器中的16位的栈指针。
实验:
目的:修改代码段CS,检验D/B位,置1或置0时,push eax ,查看堆栈寻址空间的变化
当D/B位为1时
CS的段选择子为001B,段描述符为00cffb00'0000ffff
c : 1100
此时给eax赋值0x12345678,再将eax push压入堆栈中
此时栈顶为0031FDBC
操作完后,栈顶的数值变为了0031FDB8
,0031FDBC-0031FDB8
= 4字节
当D/B位为0时
CS的段选择子为001B,段描述符为00cffb00'0000ffff
c : 1000
修改CS段为0048,那么使用jmp跳到0x48:009A13E0,再使用push eax 压入堆栈
此时ESP为0012FD5C
此时ESP为0012FD5A, 0012FD5C - 0012FD5A
= 2字节
当D/B位的段描述符表示的是代码段时,则改变的是堆栈寻址的默认操作.
以上在软件上的修改只是对该进程有效,当我们进入到内核中时,无论你是系统调用还是发生异常,系统第一步会检查各个段,并修复回原值,所以以上修改并不会导致系统崩溃.
当Type为普通数据段时
堆栈段
当我们的SS段发生改变,那么寄存器就会变成16位了,比如ESP就会变成SP了
最小的内存0-0xFFFF是不能被访问的.
所以当我们的SS段发生改变的时候,ESP寄存器变成了16位,此时压栈是会导致程序崩溃.
我们将008ffb00
0000ffff`段描述符改为数据段,该怎么改?
当Type的数值07之间就是数据段,815是代码段.
所以将008ff200
0000ffff`,此时该段权限是可读可写但没有访问的权限.
实验操作
eq 80b99000+48 008ff200
0000ffff`
执行完后发现
刚才的Type改为了2,执行完后却变成了3
原因是当我们的数据段被载入一次后,access这个标志位是由CPU自动置位的,说明这个段被用过.
这样可以区别该段是否被用过.
当我们使用堆栈段的时候就会发生C05异常
向下扩展的数据段
如果该堆栈段为一个向下扩展的数据段,B标志还确定了该堆栈段的地址上界。
向下扩展的数据段 这个标志称为B标志,它确定了该段的地址上界。
如果该标志为1,段地址上界为FFFFFFFF(4GB);若该标志为0,段地址上界为FFFF(64KB)。
测试向下拓展Type为6
eq 80b99000+48 008ff600
0000ffff`
程序发生异常
测试限长为0的情况 ,DB为1时
eq 80b99000+48 00c0f600
00000000`
程序没有发生异常
D位描述的是代码段,改变默认的寻址操作数
B位把整个代码段或者整个数据段变成16位的寄存器以及地址,限制了limit
非一致 、一致的代码段
使用一致代码段会有页的限制,除非我们运行在整个段里面。
保护模式分为两种
段页模式
纯段模式(没有页的保护)
总结:
1.Type为代码段时,这个标志被称为D标志
D/B为1:表示默认为32位的地址
D/B为0:表示默认为16位的地址
2.Type为普通数据段时,这个标志被称为B标志;分为堆栈段和向下扩展的数据段两种情况
堆栈段
D/B为1:使用的是32位的栈指针(ESP)
D/B为0:使用的是16位的栈指针(SP)
向下扩展的数据段
D/B为1:段地址上限为FFFFFFFF
D/B为0:段地址上限为FFFFF