保护模式第一篇段寄存器,学习过8086汇编的同学应该知道段寄存器在8086种的重要程度,8086CPU的寄存器都是16位,8086CPU能够以16位的地址总线访问到1MB的内存地址,采用的就是段地址*16+偏移地址=物理地址的方式,以16位地址总线访问1MB的物理内存,8086时代访问的地址都是物理地址没有虚拟地址的概念,进程访问的都是物理地址,如果不小心改了系统数据这是非常的危险的,本人在保护模式下编写驱动都没少蓝过,实在不敢想象如果应用程序能直接访问操作系统,得让操作系统崩溃多少次,实在不安全,于是随着时代的发在实模式退出了舞台(当然计算机刚上电进入的是实模式然后跳转到保护模式的),来到了保护模式的时代,首先需要理解的是保护模式保护的是什么
刚接触编程的时候老师们就没少给我们灌输,计算机所有能操作的东西都必须加载到内存中才能够处理数据,可见内存有多么重要,理所当然保护模式就是保护的内存资源,从实模式到保护模式,由单任务->到多任务,有直接访问的物理地址->到抽象到虚拟地址(每个进程有4GB的虚拟地址,低2GB不同,高2GB是操作系统内核所有进程通用的),我们应用程序能有权限访问的只有低2GB,高2GB运行着操作系统的内核应用程序没有权限访问,保护模式保护的是内存,它是通过段机制,和页机制的双层保护机制(后面会讲到CPU是如果通过段机制和页机制实现对内存的保护)
通过上面的科普让大家了解了一点实模式和保护模式的知识,也来到今天的课题段寄存器32位windows系统下使用的寄存器都是32位,段寄存器有CS ES DS SS FS GS等,我们在使用dtbug等调试工具看到的这些寄存器都是16位的,而这16位只是段寄存器的可见部分(称为段选子),还有80位是不可见的但却实际存在的,总共分为四部分
struct Segment{
shrot Select; //16位的段选择子
shrot Atrribute; //16位的段属性 决定了这个段是可读,可写,还是可执行
int Base; //段的开始
int Limit; //段的限长 从开始到结束就是段的限长可以访问的地方
}
段CS ES DS SS FS GS
在3环可以看到WIndows Xp系统可以看到
cs的段选择子为0x1B Base=0x00000000 Limit=0xFFFFFFFF cs段的属性为可读,可执行(稍后证明)
es=ss=ds=0x23 Base=0x00000000 Limit=0xFFFFFFFF 这些数据段的属性为可读可写(稍后证明)
fs的段选择子为0x3B Base=0x7FFDF000 Limit=0xFFF 属性为可读可写
操作系统有两种表非常的重要GDT(全局描述符表)IDT(中断描述符表)
这些段寄存会根据段选择子,从GDT表中加载段描述符(顾名思义就描述加载段的属性 访问属性,Base,Limit)
加载的时机就是当段寄存的选择子发生(也就是可见部分)改变的时候比如:Mov ds,ax 执行这行代码后,ds段选择子发生赋值
就会从GDT表中加载段描述符(需要新的属性描述这个段了)
可以看到下面这张图片, mov ax,ss mov ds,ax 把ss的段选择值,给了ds 在执行为mov ds,ax后 CPU需要从GDT表中
根据段选择子加载对应的段描述符,用于描述ds段的属性,这里ds和ss的选择值本来就是相同的都是0x23 段的属性也都相同可读可写,段的开始和结束也都一样,得到的结构也完全没有问题
cs的选择子是0x1B Base=0,Limit=0xFFFFFFFF和ds相同的,但是cs段的属性是可读可执行,ds段的属性是可读可写,
可以看到执行了mov ds,ax ds=0x1B后 CPU会从GDT表中加载对应的段描述符(用于描述ds段的属性),加载完成后ds段访问权限和cs段一样了可读,可执行当执行mov ds:[taolaoda],1000就挂了 0xC0000005访问异常 没有写的权限
fs段寄存的选择子位0x3B Base=0x7FFDF000(以自己电脑的为准) Limit=0xFFF把0x3B赋值给es后,CPU从GDT表中根据段选择子加载段属性,Base,Limit 此时 es段的Base=0x7FFDF000,所有 mov edi,dword ptr es:[0]这行代码访问的是 es.Base+0这个内存地址,这才是本质,平常es=ds=ss的base=0所以 []里面的地址就是实际的虚拟地址 0+[]中的地址
es段寄存器赋值了fs的选择子,当CPU从GDT表中记载完成后,Base=0x7FFDF000,Limit=0xFFF所以es段的范围只有
Base到Base+Limit之间,这明显越界了,所以过不了段的检测
就是这么简单,有人肯定会有疑问2GB以上的内存空间访问会出问题是因为没道理?难道因为搞2GB是内核空间所有不能访问,
应用程序确实不能访问高2GB,但不是因为高2GB是内核,ds,es,ss的base=0,limit=0xFFFFFFFF,这说明内核也在他们访问范围之内,可以过段的检查,但是别忘了保护模式是有段和页机制一起实现,过来段的检查过不了页的检查啊,
GS寄存器Windows并没有使用到,我们可以自己使用,需要注意的是GS寄存器几乎进内核就会把清0(后面的帖子到逆向Windows的线程切换代码SwapContext中可以找到这条清0的反汇编代码),这是初学内核需要注意的地方,下断点调试的时候如果实现了GS单步的时候可能会挂,让程序直接运行就可以了。
我们上面写段寄存器的时候只写入了16位,剩下的80位填充什么,从哪里来的?答案是根据写入的段选择子,CPU从GDT表中对应位置的段描述符中加载而来的,本期见这里了,后面还会带来,CPU如果从GDT表中记载段描述到段寄存器,和各种保护模式的只是,页的只是,应用程序到系统调用如何从ring3进入ring0,进程线程的只是,windows线程切换的逆向,APC机制等等