[windows内核]调用门

Windows系统并没有使用调用门,但是没用并不等于没有,所以我们这次主要是靠自己实现,来完成这次的探究
关于调用门的说明在手册第三卷的5.83中
在这里插入图片描述
下面是调用门的段描述符格式
在这里插入图片描述
可以看到有些值是固定的,这是调用门特有的,我们来逐个看一下段描述符格式里的各个意思

The segment selector field in a call gate specifies the code segment to be accessed. The offset field specifies the
entry point in the code segment. This entry point is generally to the first instruction of a specific procedure. The
DPL field indicates the privilege level of the call gate, which in turn is the privilege level required to access the selected procedure through the gate. The P flag indicates whether the call-gate descriptor is valid. (The presence
of the code segment to which the gate points is indicated by the P flag in the code segment’s descriptor.) The
parameter count field indicates the number of parameters to copy from the calling procedures stack to the new
stack if a stack switch occurs (see Section 5.8.5, “Stack Switching”). The parameter count specifies the number of
words for 16-bit call gates and doublewords for 32-bit call gates.
Note that the P flag in a gate descriptor is normally always set to 1. If it is set to 0, a not present (#NP) exception
is generated when a program attempts to access the descriptor. The operating system can use the P flag for special
purposes. For example, it could be used to track the number of times the gate is used. Here, the P flag is initially
set to 0 causing a trap to the not-present exception handler. The exception handler then increments a counter and
sets the P flag to 1, so that on returning from the handler, the gate descriptor will be valid.

在这里插入图片描述
段选择子确定了将要访问的代码段
偏移量确定了在该代码段中的入口点(该入口点通常指向特定例程的第一条指令)
DPL指明了该调用门的特权级,也就是通过该调用门访问该例程所
必须具备的特权级
P标志指明该调用门描述符是否有效
参数计数域的作用是,当发生了栈切换时,需要从调用进程的栈拷贝到新
栈的参数个数

调用门的大概执行流程

CALL CS:EIP//(EIP是废弃的)

1.根据CS的值查GDT表找到对应的门描述符,这个描述符是一个调用门描述符
2.根据门调用门描述符里的代码段段选择子查GDT表找到代码段的段描述符
3.根据段选择子指向的段描述符中的Base+调用门中的偏移量 就是真正要执行的地址
当然这里没有说权限检查的问题,一是前面说过,二如果想看更详细的权限问题可以去翻下手册中第三卷5.8.4

下面我们来手动构造一个调用门(无参)
假设调用门的偏移是0x00401000 代码段选择子为08(0环代码段)

Offset in Segment 31:16   P   DPL  S  Type        Param Count
000000000100 00001110  1100  0000  0000
Segment Selector        Offset in Segment 15:000000 0000 0000 1000‬     ‭0001 0000 0000 0000

然后写入GDT表
在这里插入图片描述

然后编译以下代码

#include "pch.h"
#include <iostream>
#include <windows.h>

BYTE GDT[6] = { 0 };
DWORD GDT_ADDR = 0;
DWORD dwValue = 0;
//0401000h
void __declspec(naked)GetRegister()
{
	__asm
	{

		//int 3;

		pushad;
		pushfd;

		mov eax, 0x80b95048;
		mov ebx, [eax];
		mov dwValue, ebx;
		
		sgdt GDT;//特权指令

		popfd;
		popad;

		retf  //不是ret
	}

}





int main()
{
	
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		call fword ptr[buff]
	}

	GDT_ADDR = *(PDWORD)(&GDT[2]);
	printf("GDT:%x  HighAddress:%x\n", GDT_ADDR,dwValue);
	system("pause");
	GetRegister();
	return 0;
}

运行,查看输出

在这里插入图片描述
可以看到成功运行了调用门的代码,读取了高2g的内存,说明我们成功通过调用门提权,读取高2g的的内存

下面我们再来做一下调用门有参的实验,如果需要传参那么我们只需要修改Param Count字段即可

Offset in Segment 31:16   P   DPL  S  Type        Param Count
000000000100 00001110  1100  0000  0011
Segment Selector        Offset in Segment 15:000000 0000 0000 1000‬     ‭0001 0000 0000 0000

写入GDT表
在这里插入图片描述
编译如下代码

#include "pch.h"
#include <iostream>
#include <windows.h>

DWORD A = 0;
DWORD B = 0;
DWORD C = 0;
//0401000h
void __declspec(naked)GetRegister()
{
	__asm
	{

		//int 3;

		pushad;
		pushfd;


		mov eax,[esp + 0x24 + 0x8 + 0x8];
		mov dword ptr ds : [A], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x4];
		mov dword ptr ds : [B], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x0];
		mov dword ptr ds : [C], eax;

		popfd;
		popad;

		retf 0xc //不是ret
	}

}





int main()
{
	
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		push 1;
		push 2;
		push 3;
		call fword ptr[buff]
	}

	printf("A:%x  B:%x  C:%x\n", A,B,C);
	system("pause");
	GetRegister();
	return 0;
}

运行
在这里插入图片描述
可以看到我们已经读出了我们传入的参数,证明了传参是有效的,这里需要我们手动压栈和平衡栈,那么我们在寻址参数时候为什么要这样写呢

		mov eax,[esp + 0x24 + 0x8 + 0x8];
		mov dword ptr ds : [A], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x4];
		mov dword ptr ds : [B], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x0];
		mov dword ptr ds : [C], eax;

首先+0x24是因为我们使用了pushad和pushfd
在这里插入图片描述
在这里插入图片描述
加起来压入的一共是0x24位,再+0x8可以参考我们以前发过的图

在这里插入图片描述
是代表返回的eip和cs,我们可以下断看看
在这里插入图片描述
这一段就是代表返回的eip和cs,后面的就是我们的参数

总结:
当通过门,权限不变的时候,只会PUSH两个值:CS 返回地址
新的CS的值由调用门决定

当通过门,权限改变的时候,会PUSH四个值:SS ESP CS 返回地址 新的CS的值由调用门决定 新的SS和ESP由TSS提供

通过门调用时,要执行哪行代码有调用门决定,但使用RETF返回时,由堆栈中压人的值决定,这就是说,进门时只能按指定路线走,出门时可以翻墙(只要改变堆栈里面的值就可以想去哪去哪)

可不可以再建个门出去呢?也就是用Call 当然可以了 前门进 后门出

扩展:进门的时候只能走大门进去,但是出去的时候,RETF返回的依据就是堆栈中的值,ESP+0x24,如果在堆栈里直接更改这个值为新的函数,就会直接返回到别的地方

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值