媒介
所有保护模式索引链接:保护模式条记一 保护模式介绍
先前提到了保护模式下的两大特点:段的机制和页的机制
先从段的机制开始学习,而要学习段的机制,首先要了解的便是段寄存器
段寄存器
什么是段寄存器
在先前的逆向根本条记五 标志寄存器中,有提到过段寄存器的概念
当使用汇编来操作一个内存地点时,就会涉及到段寄存器,只不过先前并没有太过在意
如:
mov dword ptr ds:[0x123456],eax留意汇编语句中的 ds,它就是一个段寄存器
实际上真正读取的内存地点为:ds.base+0x123456
段寄存器有哪些
段寄存器共8个: CS DS ES SS FS GS LDTR TR
CS
代码段寄存器,用于存放代码段的段基址
DS
数据段寄存器,用于存放数据段的段基址
ES
附加段寄存器,用于存放附加段的段基址
SS
堆栈段寄存器,用于存放堆栈段的段基址,指示堆栈段区域的位置
FS
附加段寄存器,F为上一个附加段寄存器字母E后的字母,没有对应的名称
指向一种被称为线程信息块(TIB)的结构,这种结构是由内核在创建线程时创建的,用于支持操作系统相干功能、服务和API
GS
附加段寄存器,G为上一个附加段寄存器字母F后的字母,没有对应的名称
在32位Windows上GS保留供将来使用
在x64模式下,FS和GS段寄存器已交换
Win64使用GS的缘故原由是该FS寄存器用于32位兼容性层(称为Wow64)
32位应用程序永世不会导致GS更改,而64位应用程序永世不会导致FS更改
留意,在Win64和Wow64中GS是非零的,这可以用来检测一个32位应用程序是否在64位Windows中运行,在一个“真正“的32位Windows中GS总是零
IDTR
中断形貌符表寄存器,用于存放中断形貌符表IDT的32位线性基地点和16位表长度值
TR
任务寄存器,用于存放当前任务TSS段的16位段选择符、32位基地点、16位段长度和形貌符属性值
段寄存器的结构
组成BaseLimitAttributeSelector数据宽度32位32位16位16位是否可见不可见不可见不可见可见形貌基地点(当前段的起始地点)大小限制(当前段的整个长度)属性(当前段是否可读可写可执行)段选择子struct Segment{ WORD Selector; WORD Attribute; DWORD Base; DWORD Limit;}段寄存器的属性
拿OD随便载入一个程序,观察寄存器窗口:
得到了当前的盘算机的段寄存器信息(不同盘算机段寄存器信息不肯定相同)
段寄存器SelectorAttributeBaseLimitES0023可读、可写00xFFFFFFFFCS001B可读、可执行00xFFFFFFFFSS0023可读、可写00xFFFFFFFFDS0023可读、可写00xFFFFFFFFFS003B可读、可写0x7FFDE0000xFFFGS----段寄存器的读写
对于段寄存器可以使用MOV指令进行读写(LDTR和TR除外)
读段寄存器
#include #include int main(){ WORD selector=0; _asm{ mov selector, es } printf("%x\n",selector); return 0;}对段寄存器的读操作只能读取段寄存器的16位Selector部分(可见部分)
运行结果
能够正确地读出es段寄存器的selector
写段寄存器
#include #include WORD data=0x0610;WORD readData=0;__declspec(naked) void fuction(){ __asm{ //保留调用前堆栈 push ebp //提升堆栈 mov ebp,esp sub esp,0x40 //保护现场 push ebx push esi push edi //初始化提升的堆栈,填充缓冲区 mov eax,0xCCCCCCCC mov ecx,0x10 lea edi,dword ptr ds:[ebp-0x40] rep stosd //函数核心功能 push ds //保存ds段寄存器 mov ax,cs //将cs段寄存器的段选择子赋值给ax mov ds,ax //使用cs段寄存器覆盖ds段寄存器 mov ax,word ptr ds:[data] //使用修改后的段寄存器ds读取,这里相称于mov ax,word ptr cs:[data] pop ds //还原ds段寄存器 mov readData,ax //将读出来的数据赋值给变量 //恢复现场 pop edi pop esi pop ebx //低落堆栈 mov esp,ebp pop ebp //返回 ret } }int main(){ fuction(); printf("%X\n",readData); return 0;}运行结果
可以看到代码是能够正常执行,而且输出对应的data
说明
上述代码使用了裸函数,避免了编译器的干扰;关于裸函数可以回首:逆向根本条记九 C语言内联汇编和调用协定
截取出关键代码:
push ds //保存ds段寄存器mov ax,cs //将cs段寄存器的段选择子赋值给axmov ds,ax //使用cs段寄存器覆盖ds段寄存器mov ax,word ptr ds:[data] //使用修改后的段寄存器ds读取,这里相称于mov ax,word ptr cs:[data]pop ds //还原ds段寄存器mov readData,ax //将读出来的数据赋值给变量代码表明如上,就是个简单的覆盖段寄存器的操作
为什么明明替换了段寄存器,仍然能够正常运行呢?
首先要留意到,替换和被替换的段寄存器分别是:cs和ds;它们的base是相同的都为0,因此所访问的内存自然也是相同的
再来看权限题目:无论是cs还是ds,它们都具有可读的权限;这里也只对数据进行了读操作,于是可以正常运行
如果这里将读取data的代码修改为写data的代码,则会报错:
mov ax,word ptr ds:[data] //使用cs段寄存器覆盖过的ds段寄存器,读取data//将上面的代码修改为:mov word ptr ds:[data],ax //使用cs段寄存器覆盖过的ds段寄存器,修改data为什么会报错?由于此时的ds段寄存器已经被覆盖为了cs段寄存器,而cs段寄存器的权限为可读、可执行,没有可写的权限,所以会报错
报错截图:
可以看到,此时的data的地点明明是有用的,先前也验证了可以正确读取,但是在这里就会报错:Acccess Violation(非法访问)
就这里就是由于段寄存器权限不足导致的,也是为什么先前都是使用ds段寄存器来赋值,而不是用cs段寄存器
mov word ptr ds:[address],data //使用ds段寄存器修改数据,可以正常修改mov word ptr cs:[address],data //使用cs段寄存器修改数据,会报错和前面对段寄存器的读操作不同,写寄存器是对整个96位的段寄存器进行修改
但是这里明明只给出了16位的段选择子Selector,剩下的80位呢?
这个就段形貌符有关了,这里暂且不谈,留作之后自会知晓,先记着写寄存器是对整个段寄存器进行修改即可
验证Limit
在前面的读写中,或多或少都验证了段寄存器的几个属性:Base、Selector、Attribute
现在最后验证一下Limit
#include #include int main(){ unsigned char base; _asm{ mov al,fs:[0x1000] //超过limit:0xfff,无法正常运行 mov base,al } printf("%x\n",base); return 0;}
#include #include int main(){ unsigned char base; _asm{ mov al,fs:[0xfff] //在临界点可以正常运行 mov base,al } printf("%x\n",base); return 0;}
总结
- 段寄存器共96位,其中16位为可见部分,后80位为不可见部分
- 不同盘算机段寄存器信息不肯定相同
- FS和GS两个段寄存器分别在32位程序和64位程序发挥作用
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |