SEH源码赏析之C篇

(笔记)逆向工程核心原理——SEH SEH异常机制学习记录 阅读详情
1.起因
    C++程序员对try,catch,throw都应该很熟悉,能知道VC怎么实现它的人就不多了,不过网络世界使很多人知道了它与SEH (structured exception      handling)有密切关系,我也不例外,也是在若干年前从网络知道了SEH,并且大致也知道 SEH的流程.但是和多数人一样在我的实践也很少直接使用SEH,对SEH也就仅限于网络上一些文章的介绍.曾经在用Windbg对某些软件作分析,我遇到了断点失效的情况,查找资料介绍是SEH中的Handler清除了调试寄存器,在分析SEH代码中由于VC没有SEH的源码,于是我产生了一种想法,详细完整地翻译VC的SEH的代码,一劳永逸的解决问题.C和C++的SEH有所不同,C++的要复杂些,我在此介绍的仅为C的SEH代码,也就是 __try,__finally,__except,__leave所产生的SEH代码,C++篇有时间的话我再作.我以前看过的资料大都以比较专业的语言介绍,在此我仅以我自己感觉比较通俗的语言介绍,希望能有更多的人能认识,认清SEH.
2.SEH术语:SEH中术语虽然不多,但由于没有SDK的明确定义有时很难区别,各家说的表述也不太统一,因此为此文特定义如下术语,这些术语可能与其它文献有点冲突或细微差别,有的也是我自己的定义:
  A.SEH(structured exception handling): 在C语言中关键字是__try(_try),__finally (_finally),_except(__except),而C++使用的关键字是try,catch.在以下的表述中所有的try均不再特别声明为C 关键字,一律默认为C关键字.
  B. EH3_List:_EH3_EXCEPTION_REGISTRATION链表,表头位于FS:[0],0xFFFFFFFF为链表结束标志.编译器在编译一个函数时只要检测到含有_try或__try则为此函数生成一个_EH3_EXCEPTION_REGISTRATION节点,并插入到表头.因为每个函数编译只生成一个节点,因此在一个函数中C和C++的SEH不能同时存在,如果代码中同时有catch和except则不能通过编译就是此原因.
  C. EH3_ScopeTable:是一个由编译器在data section生成的一张表(数组),实质可看作是二叉树结构(可能有多个二叉树顺序存放),节点为_SCOPETABLE_ENTRY类型,其中_SCOPETABLE_ENTRY.ParentLevel是父节点在数组中的位置, EH3_ScopeTable[0]是根节点,_SCOPETABLE_ENTRY.ParentLevel=0xFFFFFFFF.由此可见 ParentLevel很重要,是SEH判断try嵌套层次的唯一依据.编译器从函数入口点开始遍历try,每遇到一个try生成一个节点 _SCOPETABLE_ENTRY,并放在表最后,注意节点的先后与try的嵌套层次无关.
  D. filter handler:是异常发生后让用户决定是否认识此异常,通过修改异常语句的上下文环境(CONTEXT)可使应用程序能继续正常运行.其返回值有三.
  EXCEPTION_EXECUTE_HANDLER(1): 去执行exception handler,然后进程终止,且不显示出错提示框.
    EXCEPTION_CONTINUE_SEARCH(0): 不执行exception handler,显示出错提示框,进程终止或者进入调试器进行调试.
    EXCEPTION_CONTINUE_EXECUTION(-1): 不执行exception handler,系统用CONTEXT重新设置CPU环境,进程继续执行,如果修改了EIP则从新的EIP开始执行,否则从原异常点开始执行.

    E. exception handler:是异常发生后检测到该异常无法被处理,而进程终止前提醒应用程序执行的收尾工作
  F. termination handler:如果try语句被过早终止,不管是正常离开或者是非正常离开,包括goto,leave及异常时均执行此handler,具体执行过程可查MSDN.
  G. 展开(unwind):这个名词很让人费解,翻译也确实不好命名,我也沿用此名.展开的目的是执行finally对应的 termination handler,对照在下面的源代码中我们就能很容易理解MSDN关于finally的解释了.展开分为本地局部展开 (_local_unwind)和全局展开(_global_unwind);本地展开:展开此try所在函数try嵌套关系并分别执行其finally 对应的termination handler;全局展开:当exception handler不在本try函数时,执行 exception handler前需要先执行这之前的termination handler,全局展开就是查找这些 termination handler并执行它.需要说明的是全局展开不含本地展开.
3.SEH数据结构
  typedef struct  // (sizeof=0xC)
  {
    DWORD ParentLevel ;     // 当前Handler父层TRY在EH3_ScopeTable中的位置,根没有上一层,故值=-1
                 // 形成TRY层次的二叉树结构,与_EH3_EXCEPTION_REGISTRATION.TryLevel物理意义一样
    DWORD FilterFunc;      // 非NULL则HandlerFunc是exception handler,否则termination handler
    DWORD HandlerFunc;     // exception handler or termination handler
  } _SCOPETABLE_ENTRY;
  typedef struct  // (sizeof=0x10)
  {
    _EH3_EXCEPTION_REGISTRATION* pPrev;// 栈上一级EH3_List节点,=0xFFFFFFFF则为最后一个节点
    EXCE_HANDLER ExceptionHandler;     // VC7.1中统一指向_except_handler3
    _SCOPETABLE_ENTRY* pScopeTable;     // 指向一个_SCOPETABLE_ENTRY数组,函数有n个TRY,则数组有n个元素
                       // p[0]->Try0为根,p[1]->Try1,p[2]->Try2...
    DWORD TryLevel;     // 指示当前指令在Try的层次级别,-1未进入TRY,进第一个TRY为0,第二个为1,...
              // 但它与嵌套层次无关,在编译时确定,从函数代码开始处开始计数
  } _EH3_EXCEPTION_REGISTRATION;
  typedef struct  // (sizeof=0x10)还有待进一步分析其用处
  {
    DWORD unKnown;    // 未知:被编译器赋值
    DWORD HandlerFunc;  // _SCOPETABLE_ENTRY.HandlerFunc
    DWORD firstPara;    // Try所在函数第一个参数:crtMain!EBP+8
    DWORD TryEBP;    // Try所在函数EBP
  }_NLGDestination;
  // 以下在MSDN中均有定义,不再作解释
  typedef struct _EXCEPTION_RECORD
  {
    DWORD    ExceptionCode;
    DWORD ExceptionFlags;
    struct _EXCEPTION_RECORD *ExceptionRecord;
    PVOID ExceptionAddress;
    DWORD NumberParameters;
    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
  } EXCEPTION_RECORD,*PEXCEPTION_RECORD;
  typedef struct _CONTEXT
  {
    ...         // 依据CPU类型有不同定义,具体可见winnt.h
  } CONTEXT,*PCONTEXT;
  typedef struct _EXCEPTION_POINTERS
  {
    PEXCEPTION_RECORD ExceptionRecord;
    PCONTEXT ContextRecord;
  } EXCEPTION_POINTERS,*PEXCEPTION_POINTERS;
4.SEH汇编要点
  A. 函数中try的数据模型:VC将会为有try函数在栈上首先建立三个变量:
    EBP-18h:    SaveESP          // TRY前保存当时ESP,因此有:SaveESP<&Eh3Exception
    EBP-14h:    pExceInfo        // GetExceptionPointers(),在handler中调用filter前赋值
    EBP-10h:    Eh3Exception      // 一个_EH3_EXCEPTION_REGISTRATION,其大小刚好为10h哦
    EBP+00h:    ebp            // 上一frame的EBP
    EBP+04h:    EIP            // CALL返回地址
    在这里我们应该注意到这个公式是成立的:
          EBP=&Eh3Exception+10h
    而在_except_handler3中参数: pEh3Exce刚好为&Eh3Exception,所以每当在 _except_handler3中要回调filter,exception,termination handler时,在这之前汇编中均有一句:
          lea     ebp, [ebx+10h]    // ebx=_except_handler3!arg_pEh3Exception
    明白了这点就不难明白如何在
SEH不起作用的问题跟踪 关建词 _except_handler4、_except_handler3、_ValidateEH3RN 问题: 写了一个程序(shell,你懂的),发现SEH捕捉不到异常,网上搜了一下没找到相关内容,下面的代码是为了写文章简单写的例子代码 __try { _asm int 3; } __except(1) { printf("except\r\n"); } 阅读详情

相关推荐

中兴B860AV2.1-A刷机避坑指南:晶晨免拆神器+安卓7.1.2固件实测

本文提供中兴B860AV2.1-A机顶盒的详细刷机教程,重点介绍使用晶晨免拆神器配合安卓7.1.2线刷固件包的实操步骤。内容涵盖刷机前的硬件确认、工具准备、软件设置、烧录全过程监控及常见问题解决,旨在帮助用户安全、高效地完成刷机,规避变砖风险,释放设备潜力。

pytorch8learner的博客 750

Dify视觉模型节点避坑指南:我用Qwen2.5-VL做OCR识别时踩过的3个坑(及解决方案)

本文分享了使用Dify平台和Qwen2.5-VL视觉模型进行OCR票据识别时遇到的三个常见问题及解决方案,包括文件变量传递、结构化输出和条件分支判断失效。通过实战经验,帮助开发者优化多模态模型在财务自动化中的应用,提升OCR识别精度和效率。

weixin_29284885的博客 407

告别单一查询!MultiQueryRetriever在LangChain中的高级用法解析

本文深入解析了LangChain中的MultiQueryRetriever高级用法,该工具通过大语言模型自动生成多样化查询,显著提升文档检索的召回率和准确性。文章详细介绍了其核心机制、高级配置调优策略以及在多语言和专业领域中的实际应用,为开发者提供了优化知识库问答系统的实用方案。

weixin_28730927的博客 272

2025年微信小程序获取手机号授权登录注册详细教程(含服务端)。不弹窗原因以及减少组件用量的方法

前端把获取到的code传给后端,后端获取access_token和前端传过来的code,请求服务器接口,即可获取到手机号。

coderYYY的博客 3万+

形状记忆合金热机械本构模型的ABAQUS子程序实现与Fortran\_ABAQUS编程实践

本文详细介绍了如何使用ABAQUS子程序实现形状记忆合金的热机械本构模型,并通过Fortran_ABAQUS进行编程实践。我们首先介绍了问题的背景和目标,然后讲解了如何在Fortran_ABAQUS子程序中实现热变形模型、应力应变关系和相变模型,最后进行了ABAQUS模拟,并分析了模拟结果。希望通过本文的讲解,能让大家更深入理解ABAQUS子程序以及Fortran的使用,也希望能对大家在进行相关学习或研究工作时提供一些帮助。

qq_38334677的博客 1328

核心组件大换血:Backbone与Neck魔改篇:YOLO26替换EfficientNetV2主干:神经架构搜索(NAS)的最优解嫁接

YOLO26引入EfficientNetV2作为Backbone的改进方案探索了计算机视觉模型优化的前沿方向。文章首先分析了YOLO26默认C3k2模块的局限性,指出其在通道利用效率和缩放策略上的不足。随后重点介绍了EfficientNetV2的核心优势,包括Fused-MBConv模块、训练感知的NAS搜索空间和渐进式学习策略,这些特性使其在精度和效率上保持领先。最后提出了将EfficientNetV2嫁接至YOLO26的具体方案,强调通道对齐这一关键挑战,并给出两种解决方案。这一改进思路展现了&quot

ZDQ58818的博客 309

高通GAIA V3命令参考手册的研读学习(15):自定制命令的详细工作描述

里的示例一样,手机APP发出这个GetManuFacturer的命令发0x0000,那么耳机回复:0x8000, 同时在回复的playload部分,用最多15个字节的ACSII码来表达制造商信息(因此最多15个字母)。那么这个APP在通过GAIA协议控制这个耳机时,不仅可以通过前面讲过的vendor ID来识别,也可以更进一步通过这个命令来识别这个耳机,到底是不是本公司的。通过这个可变长度的playload,真的是可以传达极多的事先约定的信息,相当的灵活,真是不错!这个命令用来得到设备的制造商信息。

qq_37191547的博客 399

在ubuntu20.04中安装MATLAB时常见问题及解决方法

   如果你刚装完ubuntu20.04,这时你想要安装MATLAB2021A,在输入命令./install进行安装时,可能会报以下错误:    terminate called after throwing an instance of ‘JailContainerException’ what(): Error: 88: No JRE version parsed from/media/jzx/MATHWORKS_R2021A/sys /java/jre/glnxa64/jre/release   

慕羽★的博客 1万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

上一篇: 一种注入进程,获得完全操控的方法
下一篇: PE结构是那一类!获取导入表和导出表的类
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值