#include <iostream>
#include <Windows.h>

//全局描述符地址是6字节,高32位是起始地址,低16位是大小
char GDT[7] = { 0 };
void _declspec(naked)GetRegister()
{
	_asm
	{
		//特权指令
		sgdt GDT;
		retf
	}
}

int main()
{
	printf("%08x\n", GetRegister);
	//call CS:EIP (EIP弃用)
	char buff[6] = {0};

	//EIP弃用
	*(DWORD*)&buff[0] = 0x12345678;

	//CS段选择子:
	//13位:段描述符[9]  1位:TI=0  2位:RPL=3  
	//1001011‬->0x4b
	*(DWORD*)& buff[4] = 0x4b;

	//保存跳转后要执行函数的地址
	char szFunAddress[9] = { 0 };
	sprintf_s(szFunAddress,9,"%08x", GetRegister);

	//构建跳转门描述符:

	//高32位:
	//16位:偏移=0  1位:P=1  2位:DPL=3  1位0=0  4位:Type=12  3位0  5位:参数=0
	//0000 0000 0000 0000 1110 1100 0000 0000
	//0    0    0    0    E    C    0    0

	//低32位:
	//--------------------------------------------------------------------------------------------------------
	//跳转门执行的地址是:门描述符高32位中的16位偏移和低32位中的16位偏移合成一个(32位)4字节的地址偏移,
	//加上低32位中的段选择子索引到的GDT表中的项(段描述符)的段基址,得到Call的地址。因此,要想控制Call的地址,
	//就要把偏移修改成要执行的函数地址,把段选择子索引到一个段基址为0的字段,同时DPL=0,S=1,这时Call的函数就能
	//提权操作,而GDT[1]项正好符合。
	//--------------------------------------------------------------------------------------------------------
	//16位段选择子->13位索引:0  1位TI=0  2位RPL=3;    16位偏移=0
	//0000 0000 0000 1 0 00->0x0008                     0x0000

	//得到的是:0x0000 EC00 0008 0000
	//修改: 地址前4位      地址后4位
	char szCallDoor[18] = { 0 };
	//写入地址前4位
	memcpy_s(szCallDoor,18, szFunAddress,4);
	//写入段属性
	memcpy_s(szCallDoor+4, 14, "EC00 ", 5);
	//写入段选择子
	memcpy_s(szCallDoor + 9, 9, "0008", 4);
	//写入地址后4位
	memcpy_s(szCallDoor + 13, 5, szFunAddress+4, 4);

	//打印
	printf("复制这个值:[ %s ]到GDT[9]\n", szCallDoor);
	system("pause");
	_asm 
	{
	   call fword ptr ds :[buff]
	}

       printf("%s\n", GDT);
       system("pause");
       return 0;
}

image.png

image.png

不知道为啥,程序运行总是报错。拿到x64调试发现:调用门正常运行,访问到了GDT的地址和大小。

image.png