11-中断门

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

1. 中断门的结构

|   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 |

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

2. 中断门安装在哪?

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

这里写图片描述

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

3. 中断门提权实验

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

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

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

int ghigh2G; // 在中断门中读取高2G内存保存进来。
int geflagsBefore; // 保存进入中断门前的 EFLAGS 寄存器。
int geflagsAfter; // 保存进入中断门里的 EFLAGS 寄存器。
int geax; // 待会在中断门里要用到 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
    }
}

3.2 构造中断门描述符

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

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

这里写图片描述

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

这里写图片描述

图2 安装中断门描述符后的样子

4. 中断门对堆栈的影响

4.1 提权

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

这里写图片描述

4.2 不提权

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

这里写图片描述

4.3 使用 INT 指令进入中断门

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

  • 切换成 0 环栈
  • 在 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;
}

5.执行结果

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

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

中断门会把 IF 位置 0

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

这里写图片描述

图 3 中断门提权读取高2G内存

6. 完整代码

#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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值