前两日碰到了用异常处理来做加密的re题目 所以系统学习一下windows内核相关
内核层:R0 零环 核心态工作区域 大多数驱动程序
应用层:R3 用户态工作区域 只能使用win32 api与系统交互
当用户调用一个有关I/O的API时
该API封装在一个用户层的DLL文件中(如kernel32.dll或user32.dll)
此dll函数的更底层函数被包含在ntdll.dll中
即用户调用的系统API在ntdll.dll均有对应
(ntdll.dll中的函数均为成对出现 且为Nt与Zw开头 他们本质一样)
当调用到ntdll.dll中的函数时,检查完参数后,会通过int 2Eh或SysEnter进入R0内核层
调用内核ntoskrnl.exe中的SSDT(系统服务处理函数)他们与ntdll.dll的函数一一对应
_从用户态调用 Nt 与Zw_ 函数时**
完全一致 均为设置系统服务表与栈中参数后由SYSENTER跳入内核态
再由KiSystemService跳入对应系统例程
代码会严格检验传入参数
_从内核态调用 Nt 与Zw_ 函数时**
Nt* API:直接调用对应函数代码
Zw* API:由KiSystemService跳入对应函数代码
用户模式Nt*api会将Previous Mode改为用户态
内核模式Nt*api会将Previous Mode改为内核态
Zw*api只有用户态 但一调用就会将Previous Mode改为内核态
而Zw*不会严格检查传入参数 进而提高效率
Zw和Nt在表面功能一模一样!
也可使用RaiseException()函数来主动抛出一个异常
window正常启动后运行在保护模式下,当中断或异常发生时,CPU会通过IDT(中断描述符表 Interrupt Descriptor Table)来寻找相对应的处理函数
IDT存在于物理内存中 共有256项 32位每项8字节 64位中每项64字节
每个cpu中都有一份IDT拷贝 下面主要考虑32位
会根据异常号来寻找异常处理函数 如中断号03(即int 3)对应断点异常 由nt!KiTrap03 函数处理
该函数封装异常信息(包括异常产生原因与异常时线程状态(如寄存器值与其他特殊变量))
然后下发给内核的nt!KiDispatchException来处理
该函数会根据是否存在内核调试器,用户态调试器,以及调试器对异常的干预结果来进行不同的处理
当在被内核态调试器调试时,将异常处理控制权移交,表明FirstChance,
内核调试器根据设置来判断处理,若无法决定则发生中断将控制权移交给用户
若正确处理则继续执行程序
若无内核态调试器则跳过该步
若无内核调试器或内核调试器选择不处理该异常,将会调用内核的nt!RtlDispatchException函数,根据SEH来处理
若nt!RtlDispatchException函数未能处理该异常,系统将异常处理控制权移交内核调试器(SecondChance)
若不存在内核态调试器或在第二次机会时仍不处理,则系统调用KeBugCheckEx的BSOD 引发蓝屏
在以上任何一步异常被处理 整个异常处理流程就会被终结
完全可以交由内核调试器来处理 但一般内核调试器对用户异常不关心
所以将会分发给用户调试器
当在被用户态调试器调试时,将异常处理控制权移交,表明FirstChance,
若无用户态调试器则跳过该步
若无用户态调试器或用户态调试器未处理该异常,
将在栈上放置EXCEPTION_RECORD和CONTEXT两个结构
然后调用ntdll.dll的nt!RtlDispatchException函数
SEH:在有调试器时进入下一步,否则调用API函数SetUnhandledExceptionFilter的顶级异常处理,即显示以下对话框
若无调试器附加或调试器无法处理异常则ExitProcess函数来终结程序
若nt!RtlDispatchException函数未能处理该异常,系统将异常处理控制权移交用户调试器(SecondChance)
若无调试器则直接结束进程
若第二次机会调试器仍不处理则
TIB(线程信息块)位于TEB(线程环境块)头部
而TIB的首项指向异常处理链表
32位下FS寄存器指向TEB 即指向TIB 即指向异常处理链表
64位下是gs寄存器指向TEB
而TIB[0]对应的
因为TEB是线程环境块,所属于当前线程,所以SEH机制仅限于当前线程
若想新增或删除 则直接在链表头部编写一个新的_EXCEPTION_REGISTRATION_RECORD
可以说SEH是基于栈帧的异常处理机制
因此SEH是从0开始往后面找的异常处理
VEH与SEH大抵类似 同样是链表 但调用顺序为调试器>VEH>SEH
VEH对整个进程都有效 而SEH对单个线程有效
对我目前碰到的re题来说 与异常相关的只有主动抛出异常(如idiv rax)在循环加密过程中rax可能为0 从而进入另外的加密函数
若想动态跟踪该类函数 可以调试OD x64dbg 或IDA 的debug调试 将异常交由被调试者处理
(别没事瞎F5 伪代码还真不显示SEH 汇编
只总结了一点点 实际上加密与解密中关于内核和异常处理还有很多更深的内容 (但我还是选择先看17章(
手机扫一扫
移动阅读更方便
你可能感兴趣的文章