专栏名称: 看雪学苑
致力于移动与安全研究的开发者社区,看雪学院(kanxue.com)官方微信公众帐号。
目录
相关文章推荐
国机工程集团  ·  甲级资信!国机工程集团获工程咨询行业最高级别 ... ·  20 小时前  
国机工程集团  ·  甲级资信!国机工程集团获工程咨询行业最高级别 ... ·  20 小时前  
嘶吼专业版  ·  Google Play 上的 ... ·  2 天前  
安全研究GoSSIP  ·  G.O.S.S.I.P 阅读推荐 ... ·  3 天前  
安全研究GoSSIP  ·  G.O.S.S.I.P 阅读推荐 ... ·  3 天前  
Java基基  ·  拿自己的旧电脑搭建了个服务器! ·  4 天前  
Java基基  ·  拿自己的旧电脑搭建了个服务器! ·  4 天前  
51好读  ›  专栏  ›  看雪学苑

CPU 异常浅析

看雪学苑  · 公众号  · 互联网安全  · 2017-07-14 18:02

正文


CPU 内部产生的异常(软中断)可分为 3 种:

1. 陷阱

由代码执行int xx指令,指令执行成功后陷入内核,根据idt表中对应的段描述符,执行对应的中断处理函数,因为是int xx执行成功后才陷入的内核,中断处理函数中得到的异常触发地址为int xx的下一条指令的地址。

2. 故障

一条指令可以分解为多条微指令,如在执行div ecx时,该指令被分为多条微指令,执行过程中,微指令会先判断操作数的值是否符合要求。如果此时ecx的值为0不符合要求,会产生除0故障(类似int 0指令的效果),陷入内核后,对于cpu来说,该故障已经被处理。

对于操作系统来说异常处理才刚刚开始,这里我们不谈操作系统的异常处理,此时该指令还没执行完毕,应该说执行失败,中断处理函数中得到的异常触发地址为该指令的地址。

例子:触发一个故障

#include

int main(int argc, char *argv[])

{

    _asm int 0x20;

    return 0;

}

在执行int 0x20过程中,微指令判断对应的门描述符是否有效,要陷入的函数权限是否符合要求,任意不满足则触发故障,指令执行失败。

0x20 号中断在 idt 中的描述符为 0000000000080000,描述符 p 位为0,描述符无效,该指令执行失败触发一个故障,故障产生于应用程序,对于操作系统来说是一个3环异常,所以产生如下效果:

例子:触发一个陷阱一个故障

#include

int main(int argc, char *argv[])

{

    _asm int 0x20;

    return 0;

}

依旧使用int 0x20,此时我们使用windbg修改idt表将0x20号的门描述符修改为对于cpu来说,3环程序可用的门描述符。

0号门描述符的地址为0x80b95400

20 号门描述符的地址应为 0x80b95400 + 0x20*8 = 0x80b95500

修改为0000 ee 00 0008 0000,此时虽然中断处理函数的地址为00000000,但对于cpu来说是一个3环程序可用的门描述符。

int 0x20执行成功后程序陷入0环,并且试图去0地址执行代码,对于cpu来说陷阱已经处理完毕,在windows中0-0xffff地址为空指针区域,都不是一个合法区域,所以会产生一个访问冲突的0环异常,其对应一个故障,我们在程序的内核态中并没有注册过任何seh结构化异常处理函数,所以该异常将成为一个不可除去的异常,因为处于0环导致系统崩溃蓝屏。

例子:触发两个故障

#include


int main(int argc, char *argv[])

{

    _asm{

       xor ecx, ecx;

       div ecx

    }

    return 0;

}

该程序会产生一个除0的故障对应中断号为0。

门描述符为84038e0000088650这是一个0环才可用的门描述符,但是我们在3环下触发除0异常,可以使用到此门,这可能是cpu体供的一些特殊机制,对此我们不深究。

当跳转到该门的中断函数地址后,cpu视为该故障已被处理。正常情况下,将执行系统的异常处理机制,此时我们修改该门描述符为00008e0000080000,只修改了中断处理函数,第一个故障处理后cpu将处于0环模式并且试图在0地址执行代码之后流程和之前情况相同蓝屏。

3. 中止

一些文章中写到此类异常标志的最严重的错误,诸如硬件错误,此类异常总是无法精确地报告引起错误的指令的位置,一个比较典型的终止类异常是"双重故障"(中断号为8),当发生一次异常之后,处理器在转入该中断的处理程序时,又发生了另外的异常。

双重故障理解:

首先故障是在执行某条指令的过程中产生的,对于cpu来说,陷入内核跳转到该故障对应的中断处理函数地址,便认为该故障已经被处理,双重故障则是在此过程中又将产生新的故障,此时故障无法被处理,便产生一个双重故障,也就是说所有的故障全都因为一条指令的执行。

例子:触发一个双重故障

#include


int main(int argc, char *argv[])

{

    _asm{

       xor ecx, ecx;

       div ecx

    }

}

在执行div ecx过程中将产生一个除0故障,此时cpu对于此故障处理将执行一个int 0的操作,我们修改0 号中断的门符号,将其修改为一个无效的中断描述符此时将发生第二个故障,产生此故障的不是某条指令而是cpu本身的故障处理机制,此时便触发一个双重故障。

修改0号中断的门描述符为一个无效的门描述符, 0000000000080000

双重故障的中断号为8,同时修改8号门描述符,使其处理函数为KiTrap03,方便我们观察效果。

程序运行触发8号中断,并且异常已无法精确地报告引起错误的指令的位置。

此时我又做了一个有趣的小实验:

#include


int main(int argc, char *argv[])

{

    _asm{

       xor ecx, ecx;

       div ecx

    }

}

双重故障产生后,cpu依旧会处理这个故障,执行一个int 8的操作,此时我们让int 8也产生一个故障会怎么样呢?

按照原来的思路构建一个双重故障,修改0号终端的门描述符为无效。

修改8号终端的门描述符为无效

很遗憾虚拟机直接重启了。

实验大多是在我的推测基础上进行的,可能有些不严谨或细节的错误,希望大家发现可以给我指出,我对此非常感兴趣。


本文由 看雪论坛 布衣勇者 原创

转载请注明来自看雪论坛

如果你喜欢的话,不要忘记点个赞哦!


热门阅读文章:

更多优秀文章,长按下方二维码,“关注看雪学院公众号”查看!

看雪论坛:http://bbs.pediy.com/

微博:看雪安全

商务合作:[email protected]