【转载】中断门提权测试

2018-3-19 流沙 保护模式学习总结

原文地址: http://blog.csdn.net/q1007729991/article/details/52644720


除了使用调用门进行提权,本篇的中断门显的更加重要。因为在 Windows 中,大量使用了中断门。

中断门的结构

| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |  字节
|76543210|76543210|7 65 4 3210|76543210|76543210|76543210|76543210|76543210|  比特
|-----------------|1|--|0|1110|--------|--------|--------|--------|--------|  占位 |offset in segment|P|D |S|TYPE| |segment selector |offset in segment| 含义
| 31-16 |P |               |                 | 15-0 |
                    |L | 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通常中断门是以 ----ee00 0008----这种形式出现的,当然了,第 5 个十六进制数不一定就是 e,如果 DPL = 0 的话,那这个数就是 8. 后面的 0008 是段选择子,而左右两侧的占位符是要跳转的地址。

中断门安装在哪?

和前面的调用门一样,中断门也可以实现提权。但是我们已经知道,调用门是安装在 GDT 表中的,但是中断门并不是,它是安装在一个被称作 IDT(中断描述符表)中的,它同 GDT 一样,每个元素占 8 个字节。可以在 WinDbg 中通过 r idtr来查看 IDT 表在内存中的位置。 


这里写图片描述 

从上图中我们除了可以看见中断门的描述符外,还看见了其它我们暂时还不知道的描述符,刚刚已经知道,中断门的五六位通常是ee或者8e,但是除此之外,我们还看见了85这种类型(任务门),其实在 IDT 表中,除了有中断门外,还有任务门,陷阱门(通常是8f),后面会陆续讲到。

中断门提权实验

为了能够感受一下中断门是如何从3环进入0环的,这里使用 WinDbg + xp 系统做一个简单的中断门实验。

编写我们自己的 0 环入口函数

在虚拟机中,打开 VC6.0,编写入口函数。

int g_high2G; // 在中断门中读取高2G内存保存进来。 int g_eflagsBefore; // 保存进入中断门前的 EFLAGS 寄存器。 int g_eflagsAfter; // 保存进入中断门里的 EFLAGS 寄存器。 int g_eax; // 待会在中断门里要用到 eax ,先把旧的保存到这里。 __declspec(naked) void func() {
    __asm { /*
          此时栈结构(当你执行 int 指令的时候,CPU 会自动在0环栈中压入这些值):
            | eip3 | <- esp0
            | cs3  |
            |eflags|
            | esp3 |
            | ss3  |
         */ mov g_eax, eax // 保存当前 eflags pushfd
        pop g_eflagsAfter // 保存原始 eflags mov eax, [esp+0x08]
        mov g_eflagsBefore, eax // 读取 8003f500 处的值 mov eax, ds:[0x8003f500]
        mov g_high2G, eax // 恢复 eax mov eax, g_eax // 中断门返回 iretd
    }
} 
  • 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

构造中断门描述符

通过在 VC6.0 中,可以查看到 func 函数的起始地址为 0x00401020. 根据中断门描述符构造规则,可以设计如下描述符。

 | 函数的偏移地址 | .__.           .__. |  |           |  |  0040-ee00-0008-1020 |<----->| ^ | '----这地方较为固定。ee00是中断门的特征,0008表示要通过中断门跨入0008描述的代码段中。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 执行指令
eq 8003f500 0040ee00`00081020 
  • 1
  • 具体过程 

    这里写图片描述 
    图1 发现这个地方的描述符无效 


    这里写图片描述 
    图2 安装中断门描述符后的样子 

中断门对堆栈的影响

提权

和调用门稍稍有点不一样的地方是,中断门提权会在堆栈中多压入一个值——EFLAGS.


这里写图片描述 

不提权

如果不提权,意味着不会切换栈,所以也没有必要在栈中压入栈段选择子和栈顶指针。 


这里写图片描述 

使用 INT 指令进入中断门

IDT表中各种类型的门,都可以通过 int [index] 汇编指令进入。有一点需要说明的是,使用 int 指令进入中断门,会影响栈。如果从 3 环进入 0 环,有两件事情要做。

  1. 切换成 0 环栈
  2. 在 0 环栈压入 ss3, esp3, eflags3, cs3, eip3

上面的 ss3 等后面的 3 表示的是 3 环下的栈,栈顶指针,eflags, 3环代码段选择子和返回地址。这些值都是被 CPU 自动保存起来的,和操作系统没有任何关系,这是 CPU 本身固有的特性。

很少有使用中断门从 3 环进入 3 环的,这种设计感觉有点傻。但是我们也要知道,如果真的是这样,那么 CPU 做的事情和上面就完全不一样了,既不会切换栈,也不会压力入ss3, esp3. IF 位也没什么变化。

  • 主函数测试
int main(int argc, char* argv[])
{
    __asm { // 构造的中断门描述符安装在 IDT[20] 这个位置。 int 0x20;
    } printf("0x8003f500: %08x\n", g_high2G); printf("进入中断门前的 EFLAGS = %08x\n", g_eflagsBefore); printf("进入中断门后的 EFLAGS = %08x\n", g_eflagsAfter); return 0;
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

执行结果

从结果中可以看到,已经成功读取了我们在 8003f500 位置处安装的描述符,和我们期望的值一样。另外这里还读取了 EFLAGS 寄存器,它的值是有变化的。变化的那一个比特位实际上是第 9 位,叫 IF(Interrupt Flag)位。它由原来的 1 变成了 0. 意思是说,我(CPU)已经进入中断门了,如果还有可屏蔽中断信号到来,我将不理睬。

这里出现了很多新概念,暂时不用理会。我们只记住这样的事实:

中断门会把 IF 位置 0

等到学习的深入,再深究它。 


这里写图片描述
图 3 中断门提权读取高2G内存 

完整代码

#include <stdio.h> int g_high2G; // 在中断门中读取高2G内存保存进来。 int g_eflagsBefore; // 保存进入中断门前的 EFLAGS 寄存器。 int g_eflagsAfter; // 保存进入中断门里的 EFLAGS 寄存器。 int g_eax; // 待会在中断门里要用到 eax ,先把旧的保存到这里。 __declspec(naked) void func() {
    __asm { /*
          此时栈结构:
            | eip3 | <- esp0
            | cs3  |
            |eflags|
            | esp3 |
            | ss3  |
         */ mov g_eax, eax // 保存当前 eflags pushfd
        pop g_eflagsAfter // 保存原始 eflags mov eax, [esp+0x08]
        mov g_eflagsBefore, eax // 读取 8003f500 处的值 mov eax, ds:[0x8003f500]
        mov g_high2G, eax // 恢复 eax mov eax, g_eax // 中断门返回 iretd
    }
} int main(int argc, char* argv[])
{
    __asm { // 构造的中断门描述符安装在 IDT[20] 这个位置。 int 0x20;
    } printf("0x8003f500: %08x\n", g_high2G); printf("进入中断门前的 EFLAGS = %08x\n", g_eflagsBefore); printf("进入中断门后的 EFLAGS = %08x\n", g_eflagsAfter); return 0;
}

发表评论:

Powered by 流沙团

备案号:鄂ICP备15017378号-1