avatar

Catalog
调用门

前一篇中提到CALL FAR指令最终跳转的地址是调用门里,今天就要分析一下调用门。首先从调用门的执行流程开始

调用门执行流程

指令格式:CALL CS:EIP(EIP是废弃的)

执行步骤:

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

光看描述,的确很难懂,结合调用门描述符来分析,会好理解很多

调用门描述符

  • 高4字节8~15位:这是和普通段描述符完全一样的8位,其中第12位是判断该描述符是系统段还是数据段或代码段的位置,调用门描述符是系统段,所以此处值一定是0。接下来的Type域,这个根据段描述符那章中也能找到,32位的调用门描述符,Type域为1100,这也是确定的。
  • 高4字节的高16位+低4字节的低16位:这两块区域加起来刚好是32位,构成一个Offset,也就是调用门执行流程第三步里Base加上的Offset,那么Base哪里拿呢?
  • 低4字节的高16位:这16位是一个段选择子,有段选择子,就可以拆分,于是RPL,TI,Index都能解析出来,这时候就可以根据Index去GDT表里找到段描述符,而这个段,就是调用门跳转的段,因此要用这个段的Base+Offset便可获得真正要执行的地址。
  • 高4字节的低5位(第5~7位均为0):这5位的作用是描述调用门传进去的参数,调用门是可以传参的,而参数的个数,决定了这个位置的值

下面,通过代码来验证调用门的执行流程。

无参调用门

调用门分为无参和有参(示例默认都提权)两种情况,这里先用无参调用门进行实验:

构造调用门

因为Windows是不使用调用门的,所以需要自己构造一个调用门:0040EC00 00081010

为什么要这样构造呢?先看最熟悉的那8位,EC = 11101100,P=1,S=0,调用门=1100,DPL为啥选取3呢。首先,调用门的提权在于通过调用门后,新的段选择子会修改CS达到CPL的提权,但是访问调用门描述符还是需要保证CPL=DPL,因此,DPL需要设置为3。由于无参调用门,也就没参数,因此参数位为0,EC00也就解释清了。

接下来看0008,这个也很好理解,段选择子嘛,拆分一下,RPL=0,Index=1,我们去Windbg里看一下就好了

这下就清晰了,指向00cf9b00 0000ffff这个段描述符,拆分一下,Limit=FFFFFFFF,G=1,Base=00000000,是个非一致代码段。看来想要跳转成功,也得是0环的代码段才行。

由于Base为0,那么跳转到的地方就是0+401010,那这个401010是哪来的呢?这得看代码才能说清。

代码实现

c
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
#include "stdafx.h"

int saveEax = 0;
short oldCS = 0;
short newCS = 0;
short oldSS = 0;
short newSS = 0;
int oldESP = 0;
int newESP = 0;

__declspec(naked) void GetValue() {
__asm {
mov saveEax, eax
lea eax, [esp] //new esp
mov newESP, eax
mov eax, [esp+8] //old esp
mov oldESP, eax
mov eax, [esp+4] //old cs
mov oldCS, ax
mov ax, cs
mov newCS, ax
mov eax, [esp+0xc] //old ss
mov oldSS, ax
mov ax, ss
mov newSS, ax
mov eax, saveEax
retf
}
}

int main(int argc, char* argv[])
{
char buffer[6] = {0x0, 0x0, 0x0, 0x0, 0x4B, 0x0};
__asm {
call fword ptr [buffer]
}
printf("%x, %x, %x, %x, %x, %x", oldCS, newCS, oldSS, newSS, oldESP, newESP);
getchar();
return 0;
}

来看一下代码,从main函数开始看起,我们自己构造一个CS:EIP(EIP已废弃)的6字节char型数组,然后在汇编中执行CALL FAR调用我们构造的CS:EIP,接着打印部分内容。

可以看出执行调用门的语句嵌入了汇编里,根据上方在构造调用门时的分析可以得出,最终调用门跳转的地址会是401010,那么这个401010是怎么来的呢?其实就是GetValue函数的地址,我们知道通过调用门后会跳转到一个地址,但是如何才能检测成功跳转并提权呢?就得有一个函数来收集这些信息并将其打印出来,也就有了GetValue函数(GetValue地址通过VC下断点查看,然后写入构造的调用门描述符中)GetValue要声明成裸函数,这样堆栈只需自己平衡,可以避免元素访问的位置过远。

根据前一篇文章的内容,如果跨段并提权,堆栈内部大致情况如下

因此,这里采用通过全局变量,来依次读取堆栈不同位置的值,并打印查看结果。

由结果看,ESP,CS,SS均发生了切换,且CPL变为0,ESP的值也变为了一个高2G的数

为了验证结果的正确性,我们可以通过中断再看一下0环的堆栈结构,将裸函数中的汇编代码清除,只留下int 3和长返回语句如下:

Code
1
2
3
4
_asm {
int 3
retf
}

然后重新执行,会中断到Windbg(为什么会从虚拟机中断到Windbg,这个到后面中断部分会详细讲解)查看栈顶部分内存

注意:这里的栈顶esp的值和刚刚不一样是因为程序重新执行了,进入0环时,ESP和SS是TSS给的,而TSS内的值是当前线程给的,因为代码修改了,所以重新执行程序时,线程不一样了,所以0环的堆栈也就不一样了。但是3环的数据是没有变的(理论上也是会变的,这里没变是因为编译器的优化),对比刚刚手动读取的结果来看,3环的ESP,CS,SS完全一致,说明刚刚调用门的实验成功提权进入0环。

有参调用门

无参说完了,接下来是有参调用门,有参调用们和无参的区别在于仅仅是参数位的值会有所变动,栈里多了push进去的参数,其余和无参基本上相同。

构造调用门

这里构造的调用门描述符是:0040EC03 00081020,(地址变成了401020,是因为我重启了下虚拟机所以GetValue函数地址变了,并不影响),需要注意的是参数位设置成了3,因为这次计划传入3个参数进去。

代码实现

由于Push了参数进去,所以不确定参数在0环堆栈中位于什么位置,于是先用int 3的方法,在Windbg中查看一下堆栈的情况

图中可以发现,在压入参数的调用门进入0环后,参数位于3环ESP和3环CS中间的位置,堆栈表示大致如下:

这样,就可以来编写代码了,总体和无参的差距不大。

c
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
#include "stdafx.h"

int saveEax = 0;
int para1, para2, para3;

__declspec(naked) void GetValue() {
__asm {
mov saveEax, eax
mov eax, [esp+8]
mov para3, eax
mov eax, [esp+0xc]
mov para2, eax
mov eax, [esp+0x10]
mov para1, eax
mov eax, saveEax
retf 0xc
}
}

int main(int argc, char* argv[])
{
char buffer[6] = {0x0, 0x0, 0x0, 0x0, 0x4B, 0x0};
__asm {
push 1
push 2
push 3
call fword ptr [buffer]
}
printf("%x, %x, %x", para1, para2, para3);
getchar();
return 0;
}

需要注意一点,这里由于push了3个参数,所以长返回的时候要用RETF 0xc来平衡堆栈,否则直接中断到Windbg,要是不处理的话就蓝屏了。

代码执行效果如下,成功在0环堆栈取到了push进入的参数。

总结

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

参考教程:https://www.bilibili.com/video/av68700135?p=17

Author: cataLoc
Link: http://cata1oc.github.io/2020/03/14/%E8%B0%83%E7%94%A8%E9%97%A8/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶