保护模式篇——段寄存器

发布时间:2022-07-05 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了保护模式篇——段寄存器脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

  看此教程之前,问几个问题,基础知识储备好了吗?搭建好环境了吗?没有的话就不要继续了。


🔒 华丽的分割线 🔒


什么是段寄存器

  当我们用汇编读写某一个地址时,比如用下面的代码:

mov dword ptr ds:[0x123456], eax
@H_777_37@

  其实我们真正读写的地址是:ds.base + 0x123456。并不是0x123456不过正好的是ds段寄存器的基址是0而已。

一些段寄存器

  段寄存器有这几个:ES、CS、SS、DS、FS、GS、LDTR、TR,它们各有自己特殊的用途

段寄存器的结构

  段寄存器的结构可用下图表示:

保护模式篇——段寄存器

  段寄存器具有96位,但我们可见的只有16位。我们可以用OD随意加载一个程序,如下图所示:

保护模式篇——段寄存器

段寄存器的读写

  既然是寄存器了,那就可以进行读写操作,如下将介绍读写段寄存器的操作:

  • Mov指令:MOV AX,ES,但只能读16位的可见部分;MOV DS,AX写段寄存器,写的是96位。
  • 读写LDTR的指令为:SLDT/LLDT
  • 读写TR的指令为:STR/LTR

段寄存器属性探测

  我介绍过段寄存器有96位,但我们只能看见16位,那如果证明AttributeBaseLimIT的存在呢?我们将在下面进行初步探测。

段寄存器成员简介

  既然讨论段寄存器属性,首先要知道它们存着啥,下面表格的内容是我从虚拟机里查询到的值,可能和我的不一样,但无所谓。它们的属性我已查询并把它们的权限写到表格中,之所以为什么我之后将会介绍。

Windows操作系统并不会使用GS寄存器,故用-表示。

@H_601_126@ 段寄存器 Selector Attribute Base Limit ES 0023 可读、可写 0 0xFFFFFFFF CS 001B 可读、可执行 0 0xFFFFFFFF SS 0023 可读、可写 0 0xFFFFFFFF DS 0023 可读、可写 0 0xFFFFFFFF FS 003B 可读、可写 0x7FFDE000 0xFFF GS - - - -

探测Attribute

  如果你没有使用Visual C++6.0的话,我先将如何建立工程并写代码简单介绍一下,但只介绍一遍。打开Visual C++6.0,通过File->New打开新建项目,选择Win32 Console Application,输入你的PRoject name,如下图所示:

保护模式篇——段寄存器

  选中带有 Hello World 的工程,这样基本上IDE就帮我们建好要做实验的工程了。

保护模式篇——段寄存器

  然后IDE会展示帮我们新建的内容信息,如下图所示,直接点确定即可,工程新建完毕。

保护模式篇——段寄存器

  按照下图指示打开代码文件,删掉用不到的printf("Hello World!n");。就能开始做实验了。

保护模式篇——段寄存器

  怎么建工程我已详细说明,那就正式进入正题,我们将用以下代码进行验证Attribute

#include "stdafx.h"

int a=0;
int main(int argc, char* argv[])
{
    _asm
    {
        mov ax,cs;
        mov dword ptr ds:[a],10;
        mov ds,ax;
        mov dword ptr ds:[a],20;
    }
    return 0;
}

  然后在main函数的第一句代码下断点,然后单步运行,运行过第一句给变量a赋值的汇编代码时,成功通过,如下图所示:

保护模式篇——段寄存器

  运行到第二个给变量a赋值的汇编代码时,弹出一个错误信息框,如下图所示

保护模式篇——段寄存器

  翻译过来就是地址访问冲突,这是什么原因呢?这就是由于cs段寄存器是可读的,而不是可写的。原来的ds是可读可写的,但将cs通过ax赋值给ds时候,ds不再是原来的ds,而是cs,故会引发此错误。

探测Base

  老生常谈程序的零地址无法访问。但零地址一定是无法访问吗?我们将用以下代码进行验证Base

#include "stdafx.h"

int a=0;
int main(int argc, char* argv[])
{
    _asm
    {
        mov ax,fs;
        mov es,ax;
        mov eax,es:[0];        //不要用DS,否则编译不过去
        mov dword ptr ds:[a],eax;
    }
    return 0;
}

  编译运行通过,变量a正常赋值,如下图所示:

保护模式篇——段寄存器

探测Limit

  我们将用以下代码进行验证Limit

#include "stdafx.h"

int a=0;
int main(int argc, char* argv[])
{
    _asm
    {
        mov eax,fs:[0X1000];
        mov dword ptr ds:[a],eax;
    }
    return 0;
}

  执行第一句汇编就发生内存访问冲突错误,就是因为0x1000超出了它的Limit的值0xFFF,如下图所示:

保护模式篇——段寄存器

踩坑问题

  里面有一些坑我还没让你踩,你踩一踩看看。

  1. 在验证属性的时候,用下面的代码,结果运行mov dword ptr ds:[a],10正常通过,放开程序跑后内存访问冲突。
#include "stdafx.h"

int main(int argc, char* argv[])
{
    int a=0;
    _asm
    {
        mov ax,cs;
        mov ds,ax;
        mov dword ptr ds:[a],10;
    }
    return 0;
}

@H_276_360@ 🎉 踩坑解决方案 🎉


  为什么会出现这个问题呢?我明明代码很正常但就不行呢,难道只是因为局部变量的问题呢。但全局变量和局部变量在内存上根本没有区别。全局变量只是一个死地址,局部变量是该变量所在函数临时的地址,但访问上根本没有区别。我们看一看编译器到底把咱们的内联汇编到底翻译成了什么?为什么会出现这个问题呢?我明明代码很正常但就不行呢,难道只是因为局部变量的问题呢。但全局变量和局部变量在内存上根本没有区别。全局变量只是一个死地址,局部变量是该变量所在函数临时的地址,但访问上根本没有区别。我们看一看编译器到底把咱们的内联汇编到底翻译成了什么?

保护模式篇——段寄存器

  前面的内敛汇编编译器给我一五一十的直接翻译了,但这个mov dword ptr ds:[a],10;内联汇编给我翻译成了啥,过分了!结果压根没有用ds的权限来访问,而是默认的ss来访问,这和预期结果一样才怪。


2.在探测Base属性的时候,使用gs作为试验寄存器,单步执行到mov eax,gs:[0],出现内存访问冲突错误。

#include "stdafx.h"

int a=0;
int main(int argc, char* argv[])
{
    _asm
    {
        mov ax,fs;
        mov gs,ax;
        mov eax,gs:[0];
        mov dword ptr ds:[a],eax;
    }
    return 0;
}
🎉 踩坑解决方案 🎉


  是因为每次单步调试,就会触发单步调试异常,进入内核,内核会把gs清零了,故导致实验无法成功。


下一篇

  保护模式篇——段描述符与段选择子

脚本宝典总结

以上是脚本宝典为你收集整理的保护模式篇——段寄存器全部内容,希望文章能够帮你解决保护模式篇——段寄存器所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。