Windows异常学习笔记(五)—— 未处理异常

  • 要点回顾
  • 最后一道防线
    • 实验一:理解最后一道防线
    • 实验二:新线程的最后一道防线
    • 总结
  • UnhandledExceptionFilter
    • 实验三:理解UnhandledExceptionFilter
  • 未处理异常

要点回顾

windows异常处理流程

CPU检测到异常
↓
查IDT表执行中断处理函数
↓
CommonDispatchException     //存储异常相关信息
↓
KiDispatchException         //异常分发处理函数,查找由哪一个处理程序处理这个异常//判断0环异常还是3环异常//若为3环,修正EIP,指向KiUserExceptionDispatcher(ntdll.dll)
↓
KiUserExceptionDispatcher   //通过RtlDispatchException查找异常处理函数位置,VEH/SEH
↓
RtlDispatchException        //先查找VEH,若找不到,查找SEH(从FS:[0]开始遍历)
↓
VEH
↓
SEH

思考:会不会存在一种情况,SEH中也没有程序能处理当前异常
答案:一般来说,不存在

最后一道防线

描述:当我们程序刚开始执行时,编译器已经替我们注册了一个异常处理程序,因此叫做最后一道防线

实验一:理解最后一道防线

1)编译并运行以下代码(在return处设置断点)

#include <stdio.h>
#include <windows.h>int main()
{int x = 1;return 0;
}

2)查看堆栈调用情况

可以发现,程序并不是从main函数开始运行的,而是从kernel32.dll的某个位置开始运行的

3)查看kernel32中的代码

4)查看7181704b位置调用的函数的具体代码

7C81704B   call        7C8024D6      //在堆栈中注册了一个异常处理程序

7C8024D6   push        7C839AC0h
7C8024DB   mov         eax,fs:[00000000]    //读取了FS:[0]
7C8024E1   push        eax
7C8024E2   mov         eax,dword ptr [esp+10h]
7C8024E6   mov         dword ptr [esp+10h],ebp
7C8024EA   lea         ebp,[esp+10h]
7C8024EE   sub         esp,eax
7C8024F0   push        ebx
7C8024F1   push        esi
7C8024F2   push        edi
7C8024F3   mov         eax,dword ptr [ebp-8]
7C8024F6   mov         dword ptr [ebp-18h],esp
7C8024F9   push        eax
7C8024FA   mov         eax,dword ptr [ebp-4]
7C8024FD   mov         dword ptr [ebp-4],0FFFFFFFFh
7C802504   mov         dword ptr [ebp-8],eax
7C802507   lea         eax,[ebp-10h]
7C80250A   mov         fs:[00000000],eax    //修改了FS:[0]
7C802510   ret

5)使用IDA进行查看(BaseProcessStart)

6)总结:当我们程序刚开始执行时,编译器已经替我们注册了一个异常处理程序,因此在主线程中,找不到SEH的情况基本是不会发生的

思考:如果新起一个线程,会出现找不到SEH的情况吗?
答案:不会,参考实验二

实验二:新线程的最后一道防线

1)编译并运行以下代码(在新线程中设置断点)

#include <stdio.h>
#include <windows.h>DWORD WINAPI ThreadProc(LPVOID lpParam)
{int x = 1;return 0;
}int main()
{CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);getchar();return 0;
}

2)当中断在新线程中时,查看新线程堆栈调用情况

可以发现,线程也不是从我们提供的线程函数开始运行的,仍然是从kernel32.dll的某个位置开始运行的

3)查看kernel32中的代码

在地址7C80B6E3处也调用了地址7C8024D6的代码(__SEH_prolog),注册了异常处理函数

总结

  1. 无论是主线程,还是新创建的线程,都存在最后一道防线,即编译器为我们注册的异常处理程序的结构
  2. 这个异常处理程序相当于以下代码
    __try
    {}
    __except(UnhandledExceptionFilter(GetExceptionInformation())
    {//终止线程//终止进程
    }
    

UnhandledExceptionFilter

描述

  1. 最后一道防线调用的异常处理程序的过滤函数
  2. 只有当它的返回值为EXCEPTION_CONTINUE_SEARCH(0)时,当前线程不存在对应的SEH
  3. 只有在当前程序处于调试状态时,才会发生上述情况

执行流程

  1. 通过NtQueryInformationProcess查询当前进程是否正在被调试,如果是,返回EXCEPTION_CONTINUE_SEARCH,此时会进入第二轮分发
  2. 如果没有被调试:
    1)查询是否通过SetUnhandledExceptionFilter注册处理函数 如果就调用
    2)如果没有通过SetUnhandledExceptionFilter注册处理函数,弹出窗口,让用户选择终止程序还是启动即时调试器
    3)如果用户没有启用即时调试器,那么该函数返回EXCEPTION_EXECUTE_HANDLER,此时会执行except中的代码

实验三:理解UnhandledExceptionFilter

1)编译并运行以下代码

#include <stdio.h>
#include <windows.h>long __stdcall callback(_EXCEPTION_POINTERS *excp)
{excp->ContextRecord->Ecx = 1;return EXCEPTION_CONTINUE_EXECUTION;
}int main()
{SetUnhandledExceptionFilter(callback);__asm{xor edx, edxxor ecx, ecxmov eax, 0x10idiv ecx}printf("程序正常执行\n");getchar();return 0;
}

2)执行结果

3)双击exe执行,执行结果

4)总结

  1. 当程序处于调试状态时,UnhandledExceptionFilter返回EXCEPTION_CONTINUE_SEARCH,此时该异常没有SEH能够进行处理,程序无法向下运行
  2. 当程序不处于调试状态时,UnhandledExceptionFilter返回EXCEPTION_EXECUTE_HANDLER,异常由注册的callback函数进行处理

思考:遇到这种情况如何进行调试
答案:HOOK NtQueryInformationProcess

未处理异常

执行流程

KiUserExceptionDispatcher
↓
RtlDispatchException            //查找并执行异常处理函数//如果返回真,调用ZwContinue再次进入0环//但线程再次返回3环时,会从修正后的位置开始执行//如果返回假,调用ZwRaiseException进行第二轮异常分发//(参见KiUserExceptionDispatcher代码)

Windows异常学习笔记(五)—— 未处理异常相关推荐

  1. Windows异常学习笔记(四)—— 编译器扩展SEH

    Windows异常学习笔记(四)-- 编译器扩展SEH 要点回顾 编译器支持的SEH 过滤表达式 实验一:理解_try_except 实验二:_try_except 嵌套 拓展SEH结构体 scope ...

  2. Windows异常学习笔记(一)—— CPU异常记录模拟异常记录

    Windows异常学习笔记(一)-- CPU异常记录 基础知识 异常的分类 CPU异常 分析中断处理函数 _KiTrap00 分析 CommonDispatchException 总结 软件模拟异常 ...

  3. Windows异常学习笔记(二)—— 内核异常处理流程用户异常的分发

    Windows异常学习笔记(二)-- 内核异常处理流程&用户异常分发 用户层与内核层异常 内核异常 分析 KiDispatchException 分析 RtlDispatchException ...

  4. 软件调试学习笔记(四)—— 异常的处理流程

    软件调试学习笔记(四)-- 异常的处理流程 要点回顾 异常的处理流程 实验1:理解调试器与异常的关系 未处理异常:最后一道防线 实验2:理解UnhandledExceptionFilter执行流程 实 ...

  5. Windows APC学习笔记(二)—— 挂入过程执行过程

    Windows APC学习笔记(二)-- 挂入过程&执行过程 基础知识 挂入过程 KeInitializeApc ApcStateIndex KiInsertQueueApc Alertabl ...

  6. Windows系统调用学习笔记(三)—— 保存现场

    Windows系统调用学习笔记(三)-- 保存现场 要点回顾 基本概念 Trap Frame 结构 线程相关的结构体 ETHREAD KTHREAD CPU相关的结构体 KPCR _NT_TIB KP ...

  7. Windows APC学习笔记(一)—— APC的本质备用APC队列

    Windows APC学习笔记(一)-- APC的本质&备用APC队列 基础知识 APC的本质 APC队列 APC结构 分析 KiServiceExit 总结 备用APC队列 挂靠环境下Apc ...

  8. 【AngularJs学习笔记五】AngularJS从构建项目开始

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...

  9. Windows系统调用学习笔记(四)—— 系统服务表SSDT

    Windows系统调用学习笔记(四)-- 系统服务表&SSDT 要点回顾 系统服务表 实验:分析 KiSystemService 与 KiFastCallEntry 共同代码 SSDT 实验: ...

最新文章

  1. OSPF-网络类型(ip ospf network ?)
  2. pip3 install numpy
  3. 二.Python的基础语法知识
  4. java增加 删除 修改表格_Java实现单链表的创建、添加、修改、删除
  5. 【数据挖掘】基于密度的聚类方法 - OPTICS 方法 ( 核心距离 | 可达距离 | 族序 )
  6. python摄像头跟随人脸_opencv-python 学习笔记2:实现目光跟随(又叫人脸跟随)
  7. c++11 function
  8. 流媒体管理服务器显示不可用,部署国标流媒体服务器成功后无法播放视频问题步骤排查...
  9. 如何利用python监控主机存活并邮件、短信通知
  10. Redis 雪崩和击穿
  11. excel两列数据对比找不同_快速找出Excel表格中两列数据不同内容的3种方法!
  12. 研究调查脉搏血氧饱和度仪使用Masimo RRp(R)读取儿科患者呼吸率的精度
  13. html编辑器有哪些
  14. NOIP 2015 D1 T2 信息传递
  15. C++ 线程里面延时1秒的技巧
  16. LIMS实验室系统建设如何提高检验效率?
  17. threejs加载3D模型
  18. 小旋风360推送工具
  19. linux 2g内存 64系统怎么样,电脑2g内存能装64位系统吗|2g内存装64位系统好不好
  20. ubantu-20.04.3安装OTRS

热门文章

  1. AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛——09:55-10:40刘兵教授《Open-World AI and Continual Learning》
  2. Postman:Postman(HTTP的测试工具)使用方法详细攻略
  3. 成功解决Exception unhandled OSError cannot open resource File: F:\Program Files\Python\Python36\Lib\si
  4. Py之smtplib:smtplib(aiosmtplib)的简介、安装、使用方法之详细攻略
  5. Java:Java编程实现导出二维码
  6. sql 获取本周周一和周日
  7. ionic 完美仿微信摇一摇
  8. Silverlight中服务通信方式的选择(WCF、Data Service、Ria Service)
  9. 一些.net持久化框架的例子
  10. STM32F103之FLASH组织