Window system segment-Call Gate test


编程利用调用门读取GDT[2]并打印。

因为在Windows中没有使用调用门,因此,在GDT表中是没有调用门描述符的.
想要试验一个调用门,就需要自己使用windbg开启双击调试,并自行在GDT中设置一个.

定义结构体并赋值

定义这种系统相关的结构体时,要注意粒度对齐。

#pragma pack(1)
typedef struct _CALLGATEDESCRIPT{
    unsigned int functionAddrLow : 16; // 被调用函数地址的低16位
    unsigned int SegmentSelector : 16; // 想要切换的段选择子
    unsigned int paramCount : 5; // 参数个数
    unsigned int none : 3; // 无
    unsigned int Type : 4; // 系统段类型, 必须为12(1100b: 32位调用门)
    unsigned int S : 1; // S位,必须为0
    unsigned int DPL : 2; // 描述符特权级别
    unsigned int P : 1; // 描述符有效位
    unsigned int functionAddrHig : 16; //被调用函数地址的高16位
}CALLGATEDESCRIPT;

定义一个初始化函数:

unsigned long long createCallGateDescript(unsigned short selector, unsigned int functionAddr, int functionParamSize)
{
	CALLGATEDESCRIPT cgd = { 0 };
	cgd.P = 1; // 描述符有效位, 必须设置为1
	cgd.S = 0; // 系统段描述符, 必须设置0
	cgd.Type = 12; // 调用门描述符,必须设置为12
	cgd.DPL = 3; // 设置为3,表示此描述符可被3环所使用.
	cgd.SegmentSelector = selector;// 要切换的段选择子
	cgd.functionAddrHig = (functionAddr & 0xFFFF0000) >> 16;
	cgd.functionAddrLow = functionAddr & 0x0000FFFF;
	cgd.paramCount = functionParamSize / 4; // 参数个数,如果函数没有参数,填0,如果有参数,则填参数占用栈的字节数 / 4
	return *(unsigned long long*) & cgd;
}

GDT表

使用调用门的之前,需要将调用门的描述符写进系统的GDT表中。

2: kd> rgdtr
gdtr=8ff1dc20

读取gdtr寄存器,得到GDT的地址。然后查看GDT表中哪些是空闲的,值为0的都是空闲的。

2: kd> dq 8ff1dc20
8ff1dc20  00000000`00000000 00cf9b00`0000ffff
8ff1dc30  00cf9300`0000ffff 00cffb00`0000ffff
8ff1dc40  00cff300`0000ffff 8f008bf1`875020ab
8ff1dc50  8f4093f1`50003748 0040f300`00008000
8ff1dc60  0000f200`0400ffff 00000000`00000000
8ff1dc70  8f0089f1`aac00068 8f0089f1`ab300068
8ff1dc80  00000000`00000000 00000000`00000000
8ff1dc90  800092b9`500003ff 00000000`00000000

gdt[9]是空闲的,下面构造段选择子:

9 | 0 | 3
1001 0 11
0x4B

//低4字节是eip,高2字节是cs
char buff[] = { 0,0,0,0,0x4b,0};
_asm call fword ptr ds:[buff];

代码

#include <iostream>
#include <iomanip>
#pragma pack(1)
typedef struct _CALLGATEDESCRIPT {
	unsigned int functionAddrLow : 16; // 被调用函数地址的低16位
	unsigned int SegmentSelector : 16; // 想要切换的段选择子
	unsigned int paramCount : 5; // 参数个数
	unsigned int none : 3; // 无
	unsigned int Type : 4; // 系统段类型, 必须为12(1100b: 32位调用门)
	unsigned int S : 1; // S位,必须为0
	unsigned int DPL : 2; // 描述符特权级别
	unsigned int P : 1; // 描述符有效位

	unsigned int functionAddrHig : 16; //被调用函数地址的高16位
}CALLGATEDESCRIPT;
struct SELECTOR {
	unsigned short index : 13;
	unsigned short T1 : 1;
	unsigned short RPL : 2;
};

unsigned long long createCallGateDescript(unsigned short selector, unsigned int functionAddr, int functionParamSize)
{
	CALLGATEDESCRIPT cgd = { 0 };
	cgd.P = 1; // 描述符有效位, 必须设置为1
	cgd.S = 0; // 系统段描述符, 必须设置0
	cgd.Type = 12; // 调用门描述符,必须设置为12
	cgd.DPL = 3; // 设置为3,表示此描述符可被3环所使用.
	cgd.SegmentSelector = selector;// 要切换的段选择子
	cgd.functionAddrHig = (functionAddr & 0xFFFF0000) >> 16;
	cgd.functionAddrLow = functionAddr & 0x0000FFFF;
	cgd.paramCount = functionParamSize / 4; // 参数个数,如果函数没有参数,填0,如果有参数,则填参数占用栈的字节数 / 4
	return *(unsigned long long*) & cgd;
}
int g_num;
short g_ss;
int g_esp;

//通过调用门调用的函数
void _declspec(naked) GateFun()
{	
    g_num = 100;
	_asm mov g_esp, esp;
	_asm mov ax, ss;
	_asm mov word ptr g_ss, ax
	_asm retf;
}
int main()
{
	printf("调用门函数地址:%08X\n", GateFun);
	printf("切换的段选择子:%04X\n", 8);/*8是内核中的代码段选择子*/

	unsigned long long descript =
		createCallGateDescript(8/*8是内核中的代码段选择子*/, (unsigned int)GateFun, 0);


	printf("请将这个段描述符写入到GDT[9]中: ");
	std::cout << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << descript << '\n';
	system("pause");
	// 获取当前寄存器的值.
	_asm mov g_esp, esp;
	_asm mov ax, ss;
	_asm mov word ptr g_ss, ax

	printf("调用前  esp=%08X, ss=%04X\n", g_esp, g_ss);

	// 前4字节是EIP,后2字节是CS(0x004b)
	char buff[] = { 0,0,0,0,0x4b,00 };

	// 执行流程:
	// 1. 从buff这块内存中取出段选择子:0x4b
	//
	//        解释                        SEL  T RPL
	//      十进制                          9  0 3
	 // 2. 将段选择子分解,100 1011 ==> 1001 0 11, 得到GDT表中的下标:9
	// 3. 取出GDT表中第9项描述符, 是一个调用门描述符.
	// 4. 将调用门描述符中的段选择子加载到CS段寄存器
	// 5. 将调用门描述符中的函数地址设置到EIP寄存器.
	_asm call fword ptr ds : [buff] ;
	printf("调用后  esp=%08X, ss=%04X\n", g_esp, g_ss);
	printf("g_num=%d\n", g_num);

	system("pause");
	return 0;
}

运行:

调用门函数地址:00D71080
切换的段选择子:0008
请将这个段描述符写入到GDT[9]中: D7EC0000081080

双机调试

运行程序后,windbg break。

修改

kd> eq 80b95048 00D7EC00`00081080
kd> dq 80b95000
80b95000  00000000`00000000 00cf9b00`0000ffff
80b95010  00cf9300`0000ffff 00cffb00`0000ffff
80b95020  00cff300`0000ffff 80008b1e`500020ab
80b95030  84409317`1c003748 0040f300`00000fff
80b95040  0000f200`0400ffff 00d7ec00`00081080
kd> g
调用前  esp=0019F7E0, ss=0023
调用后  esp=AA50FD9E, ss=0010
g_num=100

虚拟机配置,1个cpu,1个核心。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值