cpu 内部产生的异常( 软中断) 可分为2 种:
1. 陷阱, 由代码执行int xx 指令, 指令执行成功后陷入内核, 根据idt 表中对应的段描述符, 执行对应的中断处理函数, 因为是int xx 执行成功后才陷入的内核, 中断处理函数中得到的异常触发地址为int xx 的下一条指令的地址
2. 故障, 一条指令可以分解为多条微指令, 如在执行div ecx 时, 该指令被分为多条微指令, 执行过程中, 微指令会先判断操作数的值是否符合要求, 如果此时ecx 的值为0 不符合要求, 会产生除0 故障( 类似int 0 指令的效果), 陷入内核后, 对于cpu 来说, 该故障已经被处理,
对于操作系统来说异常处理才刚刚开始, 这里我们不谈操作系统的异常处理, 此时该指令还没执行完毕, 应该说执行失败, 中断处理函数中得到的异常触发地址为该指令的地址
例子:触发一个故障
#include <stdio.h>
int main ( int argc , char * argv [] )
{
_asm int 0x20;
return 0;
}
在执行int 0x20 过程中, 微指令判断对应的门描述符是否有效, 要陷入的函数权限是否符合要求, 任意不满足则触发故障, 指令执行失败。
0x20 号中断在idt 中的描述符为0000000000080000, 描述符p 位为0, 描述符无效, 该指令执行失败触发一个故障, 故障产生于应用程序, 对于操作系统来说是一个3 环异常, 所以产生如下效果
例子:触发一个陷阱一个故障
#include <stdio.h>
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 <stdio.h>
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 地址执行代码之后流程和之前情况相同蓝屏
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)