windows内核研究(保护模式 5-调用门/中断门/陷阱门)

保护模式


调用门

什么是调用门?

调用门是系统段描述符的一种特殊类型,它允许低特权级代码(如用户态程序)以受控方式调用高特权级代码(如内核服务)。调用门描述符存储在全局描述符表(GDT)或局部描述符表(LDT)中

执行步骤

  1. 根据CS的值查GDT表,找到对应的段描述符,这个描述符是一个调用门
  2. 在调用门描述符中存储另一个代码段的选择子
  3. 选择子指向的段(段.Base + 偏移地址)就是真正在执行的地址

在这里插入图片描述

位置:Volume 3 - Chapter 6 Protection - 6.8.3 Call Gates

当S位为0(系统段)Type为1100时,就表示该描述符是一个门描述符,Segment Selector里面存储着真正要调用的代码存在的段低4字节第0位到第15位和高4字节第16位和第31位一共组成了一个4字节的偏移


构造调用门

0000(Offset in Segment)E(P/DPL/S)C(Type)00 高4字节
0008(3环/0008/0环)0000(Offset in Segment) 低4字节

在这里插入图片描述
向805d6048这个位置写入我们刚刚构建好的调用门

0000EC00 00080000加上偏移后:00C5EC00 00081790

在这里插入图片描述
我们写一段代码来测试一下

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


void __declspec(naked) GetRegister() {
	_asm {
		// 由于我们在构造调用门时,构造的是内核权限,那么执行到这里时,就已经是0环的权限了不再是3环的权限了
		int 3 // 地址:00C51790 (一但执行到这里,会中断到0环调试器当中)
		retf	
	}
}

int main() {

	char buffer[6];

	*(DWORD*)buffer[0] = 0x12345678;// EIP寄存器
	*(WORD*)buffer[4] = 0x48; // CS段描述符
	_asm {
		call fword ptr[buffer]
	}
	getchar();

	return 0;
}

在执行之前先看一下我们段寄存器中CS:0008SS:0010ESP:8b417700的值

在这里插入图片描述

调用门(有参)

调用门的参数传递

在上图的调用门结构图中,高4字节的ParamCount就表示要携带的参数个数,重新构造一个调用门

0000(Offset in Segment)E(P/DPL/S)C(Type)03 (ParamCount) 高4字节
0008(3环/0008/0环)0000(Offset in Segment) 低4字节

修改我们之前设置的段描述符

在这里插入图片描述

编写测试代码

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

DWORD x,y,z;

void __declspec(naked) CateProc(){
	_asm{
		pushad
		pushfd
		mov eax,[esp+24+8+8]
		mov dword ptr ds:[x],eax
		mov eax,[esp+24+8+4]
		mov dword ptr ds:[y],eax
		mov eax,[esp+24+8+0]
		mov dword ptr ds:[z],eax
		popfd
		popad
		retf 0xc
	}
}



void PrintRegister(){
	printf("%x %x %x\n",x,y,z);
}


int main(){
	char buffer[6];
	*(DWORD*)&buffer[0] = 0x12345678;
	*(WORD*)&buffer[4] = 0x48; // 段选择子
	_asm{
		// 参数需要要自己手动压入
		push 1
		push 2
		push 3
		call fword ptr[buffer]
	}
	PrintRegister();
	getchar();
	return 0;
}

调用门总结:

  • 当通过门,权限不变的时候,只会PUSH两个值:CS和返回地址,新的CS的值由调用门决定
  • 当通过门,权限改变的时候,会PUSH四个值:SS ESP CS 返回地址,新的CS的值由调用门决定,新的SS和ESP由TSS提供
  • 通过门调用时,要执行哪行代码由调用门决定,但使用RETF返回时,由堆栈中压入的值决定,这就是说,进门时只能按指定路线走,出门时可以翻墙(只要改变堆栈里面的值就可以想去哪就去哪)
  • 也可以再建个门出去

中断门

在windows中并没有使用调用门,但是使用了中断门

Windows中使用中断门的地方

  1. 系统调用(早期系统)
  2. 调试

IDT

在这里插入图片描述

inter手册位置:Volume 3 - Chapter 7 - 7.11(IDT Descriptors)

IDT(中断描述符表),同GDT一样,IDT也是由一系列描述符组成的,每个描述符占8个字节,需要注意的是,IDT表中第一个元素不是NULL(IDT表中存储的都是系统段描述符

winddbg查看IDT表命令:

r idtr		// 查看IDT表的起始地址
r idtl		// 查看IDT表的大小(长度)

IDT表包含3种门描述符:

  1. 任务门描述符
  2. 中断门描述符
  3. 陷阱门描述符

上图中Interrupt Gate就是我们中断门的段描述符图

和调用门不同的是,高4字节地址,第0到第7位都为0,调用门可以传参数,但中断门不可以

代码演示


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

DWORD dwH2GValue;

void __declspec(naked) GetH2GValue(){
	_asm{
		pushad
		pushfd
		mov eax,[0x8003f00c]
		mov ebx,[eax]
		mov dwH2GValue,ebx
		popfd
		popad
		iretd
	}
}

void PrintH2GValue(){
	printf("%x \n",dwH2GValue);
}

int main(){
	_asm{
		INT 0x20
	}
	PrintH2GValue();
	getchar();
	return 0;
}

陷阱门

陷阱门(Trap Gate)是 x86/x86-64 架构 中的一种 中断描述符(Interrupt Descriptor),用于定义 异常或软件中断的处理方式。它和 中断门(Interrupt Gate) 类似,但有一个关键区别:陷阱门在执行中断处理程序时不会自动关闭 CPU 中断(IF 标志保持不变)

陷阱门和中断门唯一不同的是在高4字节中,第8到第12位,中断门是1110(0xD),陷阱门是1111(0xF)

代码演示:

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

DWORD dwH2GValue;

void __declspec(naked) GetH2GValue(){
	_asm{
		pushad
		pushfd
		mov eax,[0x8003f00c]
		mov ebx,[eax]
		mov dwH2GValue,ebx
		popfd
		popad
		iretd
	}
}

void PrintH2GValue(){
	printf("%x \n",dwH2GValue);
}

int main(){
	_asm{
		INT 0x20
	}
	PrintH2GValue();
	getchar();
	return 0;
}

中断门和陷阱门的区别:中断门执行时,将IF位清零,但陷阱门不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值