流沙团
【转载】中断门提权测试
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






中断门对堆栈的影响



提权



和调用门稍稍有点不一样的地方是,中断门提权会在堆栈中多压入一个值——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;
}
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容