2-段的初探

段寄存器

根据下图了解相关段寄存器
image.png
为了减少地址转换的时间和代码复杂度,处理器提供了 6 个段寄存器来保存段选择符
代码段寄存器(CS),数据段寄存器(DS)和堆栈段寄存器(SS)赋予有效的段选择符
,3 个数据段寄存器(ES,FS 和 GS)供进程使用。

段基址,段限长和访问权限
image.png
:::tips
DS:数据段
CS:代码段
ES:扩展段
SS:堆栈段
FS:暂时不管
:::

DS数据段

要想操作eax的值,使用纯C语言是不行,我们在虚拟机VS2008中,在微软X86编译器语法中使用__asm 内嵌的方式去操作。
微软支持以下该写法

#include <Windows.h>
int g_value = 10;
int main()
{
	__asm
    {
    	mov eax,g_value;
    }

	return 0;
}

但标准写法应该是这样的

#include <Windows.h>
int g_value = 10;
int main()
{
	__asm
    {
    	mov eax,dword ptr:ds [g_value];
    }
	return 0;
}

我们将ds删掉

#include <Windows.h>
int g_value = 10;
int main()
{
	__asm
    {
    	mov eax,dword ptr [g_value];
    }
	return 0;
}

在我们调式该代码,反汇编的时候发现编译器自动加上了ds段这个标识符
image.png

这个段的值的术语叫做段选择子

SS堆栈段

我们将全局变量 移到局部变量里,就会发现使用的是堆栈段ss

#include <Windows.h>

int main()
{
    int g_value = 10;
	__asm
    {
    	mov eax,dword ptr [g_value];
    }
	return 0;
}

image.png

CS代码段


//全局变量可读可写
int g_value = 10;
/*

*/
int main()
{
    g_value = 100;
	__asm
    {
        //当我们将只有可读可执行的属性的cs时,
		//在后续代码中使用写入修改就会发生段错误
		mov ax,cs;
		mov ds,ax;
		mov eax,100;
		mov dword ptr ds:[g_value],eax;
		mov ax,es;
		mov ds,ax;
	}
	system("pause");
	return 0;
}

写入修改失败
image.png
可得出 段是有属性的,内存是跟段属性息息相关的

windbug实验

:::tips
r gdtr
#d:查看内存 db:一字节 dw:两字节 dd:四字节 dq:八字节
dq 80b97000
dq 80b97000 L40 #查看多少项
eq #修改
:::
r 只能查看通用寄存器 段寄存器 flg 标志
image.png

r eax
image.png
r gdtr
image.png
d 是看内存
byte word dword qword
image.png

段选择符

image.png
:::tips
DS:数据段 0023
CS:代码段 001B
ES:扩展段 0023
SS:堆栈段 0023
FS:暂时不管
:::
段选择符是一个16位的段标识符,它并不直接指向该段,而是指向定义该段的段描述符。
LDT: Local descriptor table (局部描述符表)
GDT:Global descriptor table(全局描述符表)
Index (位3~15),选中GDT或LDT中8192个描述符中的某个描述符。处理器将索引值乘以8(段描述符的字节数),然后加上GDT或LDT的基地址(基地址在GDTR或者LDTR寄存器中)。
TI (table indicator) 标记 ( 位 2 )。确定使用哪一个描述符表:将这个标记置0,表示用GDT。将这个标记置1,表示用LDT。
请求的特权级(RPL)(位 0 和 1)。确定该选择符的特权级。特权级从0-3, 0为最高特权级。
image.png
GDT 中的第一项是不用的。指向 GDT 中第一项的段选择符(即选择符中的索引为 0,TI标记置为 0)被视为空(null)段选择符。当段寄存器(CS 或 DS)被赋值为空选择符时,处理器并不产生一个异常。然而当使用值为空选择符的寄存器来访问内存时,处理器会产生一个异常。空选择符可以用来初始化未使用的段寄存器。对 CS 或者 SS 赋予一个空选择符会导致处理器产生一个通用保护异常 (#GP)。对应用程序而言,段选择符作为指针变量的一部分,是可见的。但是其值由连接程序赋予或者更改,而不是应用程序。

DS段:0023

:::tips
0023 拆分二进制 0000000000100 0 11
RPL:11 ->请求特权级别为3
Ti:0 ->查找GDT表
index:0000000000100 ->索引为4,查找GDT表索引为4的段描述符
:::
意思就是我要查第四项的GDT表的值
image.png

段描述符

段描述符是GDT或LDT中的一个数据结构,它为处理器提供诸如段基址,段大小,访问权限
及状态等信息,段描述符主要是由编译器,连接器,装载器或者操作系统构造的,而不是
由应用程序产生的。
image.png
段描述符:00cff300`0000ffff
二进制:
0000 0000 1100 1111 1111 0011 0000 0000
0000 0000 0000 0000 1111 1111 1111 1111
image.png
:::tips
基地址域 base:00 00 0000 基址 上图有三个base
段限长字段 limt:f ffff
Type:3
S(描述符类型)标志 s:1
DPL(描述符特权级)域 DPL:3
P(段存在)标志 P:1
AVL:0
D/B( 默认操作数大小/默认栈指针大小和/或上限)标志 d/b:1
G(粒度)标志 g:1
:::

P(段存在)标志

段的第一位 代表这个段是否有效,
标志指出该段当前是否在内存中(1表示在内存中,0表示不在)。
:::tips
P = 1: 有效
P= 0: 无效
:::

S(描述符类型)标志

确定段描述符是系统描述符(S 标记为0)或者代码,数据段描述符(S 标记为1)。
:::tips
S = 0 表示系统段 调用门、中断们、任务段
S = 1 表示代码段或者数据段
:::

Type “是否可写”标志

代码和数据段描述符类型域的解码描述
image.png
描述符的类型域的低3位(位8,9,10)被解释为访问控制(A),是否可写(W),扩展方向(E)。

再举个例子:
image.png

CS段:001B

在前面讲述中 我们发现 RPL、TI占3位

RPL:11 ->请求特权级别为3
Ti:0 ->查找GDT表
index:0000 0000 0010 0

所以我们可以直接用与运算,快速的求出index
我们只关心前13位,& 上 1 ,就会得出13位是多少
001B & 0xFFF8 = 0000 0000 0001 1011 & 1111 1111 1111 1000
= 0000 0000 0001 1000 = 0x18
index = gdtr得出的地址+0x18
index = 0000000000011 = 3
或者001B直接 >>3就可以了
image.png
段描述符:00cffb00`0000ffff
二进制:
0000 0000 1100 1111 1111 1011 0000 0000
0000 0000 0000 0000 1111 1111 1111 1111
:::tips
base:00 00 0000 基址
limt:f ffff
Type:B
s:1
DPL:3
P:1
AVL:0
d/b:1
g:1
:::
image.png
所以根据上表CS段不支持写入修改的操作。

实验:Base段基址

修改段属性

将虚拟机设置为单核
将虚拟机设置 处理器内核数改为1
image.png

int g_value = 0x100;
int g_value1 = 0;
int main() 
{
	__asm
    {
		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;
}

以上代码测试结果为:
image.png
修改代码为

int g_value = 0x100;
int g_value1 = 0;
int main() 
{
	__asm
    {
        mov ax,0x4b;
		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;
}

测试结果为:
image.png
image.png

我们修改段属性,将DS段的属性复制到0x48的位置
image.png
image.png
使用eq指令修改该地址
eq 80b99000+48 00cff3000000ffff`
image.png
就会使得0x48的位置可以有写入权限
image.png
image.png

手动加点佐料-修改base段基址的偏移

使用eq指令修改该地址
eq 80b99000+48 00cff3000001ffff`

image.png
再次运行程序,调试发现eax的值变了
image.png
但是执行结果仍是0x100
image.png
很诡异???
image.png
逻辑地址:010F7014h
根据前面的操作,我们修改0x48地址的值为DS段的段描述符 00cff3000000ffff,根据前面我们的分析DS段的base段基址 ,这个逻辑地址要与我们的base相加,没修改之前base的值应是 0000 0000 + 010F7014h ,base的值为0,那么该地址就是线性地址,而eq 80b99000+48 00cff3000001ffff ,人为修改后的值应是 0000 0001 + 010F7014h = 0000 0001 + 010F7015h 。所以在mov eax,dword ptr ds:[g_value];取的地址的值是错误的。

可以通过这种方式hook.
例子:
base +idt.offset =0x48;
0x12345678
0x12345678 - idt.offset = value
gdt.48.base = value

X64系统没有了base和 limit

:::tips
int g_value = 0x100;
int g_value1 = 0;
int main()
{
__asm
{
mov ax,0x4b;
//修改了ds段的地址,原本ds段的值为23,现在改为了4b,
mov ds,ax;
//原本我们手动修改的ds的段描述符原封不动的赋值给了4b的地址,但是我们做了一个手脚
//00cff3000001ffff` 将base段基地址加了个1,所以在寻址ds段的地址时,会往前寻址一位
mov eax,dword ptr ds:[g_value];
//安装正常的流程,寻址到的值是0x100,赋值给eax
image.png
//但是我们base段基地址加了个1,所以寻址的地址的值是 FE 00 00 01
image.png
mov g_value1,eax;
mov ax,es;
//es段存的值是0x23
image.png
//最后ds的值恢复原来的0x23
mov ds,ax;
}
image.png
//所以在调用输出这个函数的时候使用的是ds段的值0x23的地址,而这个地址存放的值是0x100,赋值给eax,在push eax,输出的值是0x100
printf(“g_value1 = %x\r\n”,g_value1);
system(“pause”);
return 0;
}

:::

实验:P(段存在)标志

将p设置为0,说明该段无效
image.png
修改指令eq 80b99000+48 00cf73000000ffff`
image.png
0x48这个位置是无效段,写入失败
image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑桃鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值