调用门描述符

门描述符跟段描述符的结构不同

调用门描述符 1. 它指定要访问的代码段(选择子)。
2. 它定义了在指定代码段例程的入口点(偏移)。
3. 它指定了调用者尝试去访问例程所需要的特权级(DPL 这里是访问者所要具备的特权级)。
4. 如果发生了栈切换,它指定了拷贝到新栈的可选参数的数量(5位最多32个)。
也就是说,在跳到R0执行相应例程的时候,特权级便了,使用的栈也会改变,所以会有传参这个说法。
5. 它定义了push到目标栈的值的大小:16位门强制16位push,32位门强制32位push。
6. 它指定了调用门描述符是否有效(p位)。

我们自己动手尝试一下:首先安装我们的调用门,我这里安装在xp上的,要安装我们自己例程,需要首先看下我们的安装函数的地址是多少,可以在我下面的示例代码里将int 3那里注释掉 或者调用win32api手动加一个断点 然后用windbg捕捉。xp_sp3上是0x401030,并且在8003f048处在xp_sp3必定是全0的一串的,我这里没捕捉(太懒了),我们把我们的例程安装到这个地址上。
用命令 eq 8003f048 0040ec00`00081030 进行安装。下图是我安装好之后的内存样子。
添加自定义的调用门

为什么是 0040ec00`00081030是因为根据调用门的结构来看,高16位和最低的16位拼接为偏移 也就是调用门中指定的例程位置。其他按照结构来就好 参数记得填0.我们选择08作为选择子是因为这个地方的DPL为0,我们进入这里,我们的CPL也会变为0,并且这个段基址与我们的程序的段基址是相同的,跳到这里的话不用再计算相对偏移就会非常方便。

调用门代码如下:
ps:这里最好用vc6的编译器编译,用vs2013发现不能运行,可能是缺少相应的依赖库,但是并未尝试,vc6的话能很好的兼容。

#include <Windows.h>
#include <stdio.h>

USHORT g_cs;
USHORT g_ds;
USHORT g_es;
USHORT g_ss;
USHORT g_fs;
USHORT g_gs;
USHORT kernelHiDword = 0;
USHORT kernelLowDword = 0;
_declspec(naked) void GetR0Data()//call时调用的例程
{
	//_asm {
	//int 3
	//}
	_asm {
		push eax
		//获得cs
		mov ax, cs
		mov g_cs, ax
		//获得ds
		mov ax, ds
		mov g_ds, ax
		//获得es
		mov ax, es
		mov g_es, ax
		//获得ss
		mov ax, ss
		mov g_ss, ax
		//获得fs
		mov ax, fs
		mov g_fs, ax
		//获得gs
		mov ax, gs
		mov g_gs, ax
		//获得两个dword
		mov eax, 0x8003f048
		mov kernelLowDword, [eax]
		mov eax,0x8003f04c
		mov kernelHiDword, [eax]
		pop eax
		retf
	}
}

 void GetR3Data()
{

	_asm {
		//获得cs
		mov ax, cs
		mov g_cs, ax
		//获得ds
		mov ax, ds
		mov g_ds, ax
		//获得es
		mov ax, es
		mov g_es, ax
		//获得ss
		mov ax, ss
		mov g_ss, ax
		//获得fs
		mov ax, fs
		mov g_fs, ax
		//获得gs
		mov ax, gs
		mov g_gs, ax
	}
}

void PrintData()
{
	printf("CS: %04x,\nDS: %04x\nES: %04x\nSS:  %04x\nFS:  %04x\nGS: %04x\n", g_cs, g_ds, g_es, g_ss, g_fs, g_gs);
	if (kernelLowDword != 0 && kernelHiDword != 0) {
		printf("kernelLowDword : %04x \nkernelHiDword : %04x\n", kernelLowDword, kernelHiDword);
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	unsigned char jmp_addr[6] = { 0, 0, 0, 0, 0x48, 0 };//跳到0x48的地方 段选择子恰好索引为9
	GetR3Data();
	PrintData();

	_asm {//转到 调用门
		call fword ptr [jmp_addr]
	}

	//GetR0Data(); 我这里一开始没发现 写了上去 发现 不对... 其实根本就不需要 因为调用了Gate指定的函数了 所以直接打印就好
	PrintData();
	
	//测试下返回后的情况
	GetR3Data();
	PrintData();
	getchar();
	getchar();
	return 0;
}


可以看出我们只是在调用过程中暂时到了R0的地方,但实际上调用完例程后我们就离开了R0,恢复了原来的一些寄存器。
打印情况

参考了以下博客:
https://bbs.pediy.com/thread-217562.htm
https://blog.csdn.net/q1007729991/article/details/52537782

带参数的调用门示例讲解可以参考下面的博客:
https://blog.csdn.net/q1007729991/article/details/52542296

猜你喜欢

转载自blog.csdn.net/qq_40890756/article/details/89457909