保护模式
调用门
什么是调用门?
调用门是系统段描述符的一种特殊类型,它允许低特权级代码(如用户态程序)以受控方式调用高特权级代码(如内核服务)。调用门描述符存储在全局描述符表(GDT)或局部描述符表(LDT)中
执行步骤
- 根据CS的值查GDT表,找到对应的段描述符,这个描述符是一个调用门
- 在调用门描述符中存储另一个代码段的选择子
- 选择子指向的段(段.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:0008,SS:0010,ESP: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中使用中断门的地方
- 系统调用(早期系统)
- 调试
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种门描述符:
- 任务门描述符
- 中断门描述符
- 陷阱门描述符
上图中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位清零,但陷阱门不会
1万+

被折叠的 条评论
为什么被折叠?



