【火哥学习笔记】调用门

调用门实际上还是一个描述符的格式,储存于GDTorLDT表中,以下是他的结构

以下是跳转时候,使用门的汇编代码

1
2
3
4
5
char door[] = {0,0,0,0,0x48,0};
__asm
{
call fword ptr ds:[door];
}

这里使用的call 通过一个远指针(door)跳转,jmp也可以跳转但是不能提权。此时,描述符就不会被识别为段描述符而是门描述符

其中的TYPE位置为c(h) = 1100(c) 决定了这个描述符的的类型是 调用门

首尾组合成的段中偏移值就是我们要跳转到的地方,即汇编指令中跳转的地址是没有作用的,(上代码中为0)

使用调用门之后会自动压入当前代码段的CS和返回地址,所以我们不能使用ret而使用retf

此外用调用门的时候还可以携带参数,并且是先push参数,再push CS,再push返回地址,而门描述符中有5bit存放参数个数,意思是最多可以有2^5-1=31个参数,调用代码如下 (此时需要对应修改门描述符中参数个数为2)

1
2
3
4
5
6
7
char door[] = {0,0,0,0,0x48,0};
__asm
{
push 1;
push 2;
call fword ptr ds:[door];
}

目标函数中只需要使用retf即可把参数也pop出去,这就是retf的特殊之处了

使用调用门的时候需要满足以下的权限检查:

​ [数值上] ① CPL<=调用门描述符DPL,②调用门RPL<=调用门描述符DPL,③当前CPL>=目标代码段描述符DPL

​ 因为检查③的存在 我们可以由当前的R3(CPL=3)跳转到目标的R0(目标代码段描述符DPL=0)去

以下是实验代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*

gdtr=8003f000
8003f000: 00000000`00000000 00cf9b00`0000ffff
8003f010: 00cf9300`0000ffff 00cffb00`0000ffff

首先做以下修改
kd> eq 8003f048 0040ec00`000b1005
这里段选择子用的是0x000b,也就是调用门RPL=3,使用gdt表中的第二项为目标段描述符
目标段是r0的一个段,DPL=0 满足 ③[数值上] 当前CPL>=目标代码段描述符DPL
调用门描述符的DPL=3 满足① [数值上] CPL<=调用门描述符DPL
易知我们的跳转函数地址为0x00401005


*/

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
int nt_value = 0;
__declspec(naked) void test()
{
__asm
{
mov ebx,0x8003f00c; // R0的地址 (实际上就是gdt表的第二项前32位)
mov eax,[ebx];
mov nt_value,eax;
retf;
}
}
int main()
{
char door[] = {0,0,0,0,0x48,0};
// 因为 ②[数值上] 调用门RPL<=调用门描述符DPL 所以这里用0x4b也是能过去的
printf("%X\n",test);
system("pause");
__asm
{
call fword ptr ds:[door];
}
printf("%X\n",nt_value);
system("pause");
return 0;
}