SEH的介绍及实战

SEH的介绍及实战根据我们上节的异常讲解中 我们说过了 SEH StructuredEx 是在无调试器接手的情况下 系统会遍历 SE 链 然后寻找相应的 SEH 函数来处理异常 首先稍微复习下我们的 TEBkd gt dt tebntdll TEB 0x000NtTib NT TIB 0x01cEnviron

根据我们上节的异常讲解中,我们说过了SEH(Structured Exception Handler)是在无调试器接手的情况下,系统会遍历SE链,然后寻找相应的SEH函数来处理异常。

首先稍微复习下我们的TEB

kd> dt _teb ntdll!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : Ptr32 Void +0x020 ClientId : _CLIENT_ID +0x028 ActiveRpcHandle : Ptr32 Void +0x02c ThreadLocalStoragePointer : Ptr32 Void .......... 

teb的第一项是一项_NT_TIB是纪录着线程信息块的结构,里面就有着我们今天的主角。
查看下tib的结构

ntdll!_NT_TIB +0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x004 StackBase : Ptr32 Void +0x008 StackLimit : Ptr32 Void +0x00c SubSystemTib : Ptr32 Void +0x010 FiberData : Ptr32 Void +0x010 Version : Uint4B +0x014 ArbitraryUserPointer : Ptr32 Void +0x018 Self : Ptr32 _NT_TIB -------------seperrator------------- typedef struct _EXCEPTION_REGISTRATION_RECORD { 
    struct _EXCEPTION_REGISTRATION_RECORD *Next; //指向下一个ERR PEXCEPTION_ROUTINE Handler; //异常处理函数 }EXCEPTION_REGISTRATION_RECORD -------------seperrator------------- typedef enum _EXCEPTION_DISPOSITION { 
    ExceptionContinueExecution = 0, ExceptionContinueSearch = 1, ExceptionNestedException = 2, ExceptionCollidedUnwind = 3 } EXCEPTION_DISPOSITION; 

看这个第一项,就是一个SE的链,查看一下它的结构,正是一条异常处理链。x86平台下,fs:[0]就是指向TEB的指针,同时里面的值也就是指向第一个SEH的指针。x64下是gs:[0]。每一个结点的结构都是一个ERR(异常处理记录)。并且这条链是一条单向链,所以决定了它插入和删除只能在头部,也就说新注册的异常处理函数只会在头部新增。并且TEB是每个线程所私有的,所以每个线程的SEH是不同的,也就是SEH只对本线程有效,并且系统会维护链表最后的next指向0xFFFFFFFF。
异常处理回调函数原型如下:

__cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext); 

同时,我们在上次讲了,KiDispatchException这个函数是在内核态的,而内核的栈与ring3的栈用的不是同一个,所以会进行相应的变换,相应的代码如下:

 // // If the SS segment is not 32 bit flat, there is no point // to dispatch exception to frame based exception handler. // if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) || TrapFrame->EFlags & EFLAGS_V86_MASK ) { 
    ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION; ExceptionRecord2.ExceptionFlags = 0; ExceptionRecord2.NumberParameters = 0; ExRaiseException(&ExceptionRecord2); } // // Compute length of context record and new aligned user stack // pointer. // UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - CONTEXT_ALIGNED_SIZE; // // Probe user stack area for writability and then transfer the // context record to the user stack. // ProbeForWrite((PCHAR)UserStack1, CONTEXT_ALIGNED_SIZE, CONTEXT_ALIGN); RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT)); // // Compute length of exception record and new aligned stack // address. // Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS - ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) & (~3); UserStack2 = UserStack1 - Length; // // Probe user stack area for writeability and then transfer the // context record to the user stack area. // N.B. The probing length is Length+8 because there are two // arguments need to be pushed to user stack later. // ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG)); RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length); // // Push address of exception record, context record to the // user stack. They are the two parameters required by // _KiUserExceptionDispatch. // *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1; *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2; // // Set new stack pointer to the trap frame. // KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA); KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2)); // // Force correct R3 selectors into TrapFrame. // TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode); TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode); TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode); TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode); TrapFrame->SegGs = 0; // // Set the address of the exception routine that will call the // exception dispatcher and then return to the trap handler. // The trap handler will restore the exception and trap frame // context and continue execution in the routine that will // call the exception dispatcher. // TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher; return; 
nt!_EXCEPTION_POINTERS +0x000 ExceptionRecord : Ptr32 _EXCEPTION_RECORD +0x004 ContextRecord : Ptr32 _CONTEXT 

我们再来看下源码的情况

;异常处理回调函数 myHandler proc C uses ebx esi edi pExcept,pFrame,pContext,pDispatch invoke MessageBox,0,addr messuc,addr szTit,MB_APPLMODAL or MB_OK invoke ExitProcess,0 myHandler endp ;程序入口点 _Start: assume fs:nothing push ebp mov ebp,esp push offset myHandler //压入我们的handler push fs:[0] //压入之前的ERR mov fs:[0],esp //fs:[0]更新成我们的处理地址 xor esi,esi mov eax, dword ptr [esi] invoke MessageBox,0,addr mesfail,addr szTit,MB_APPLMODAL or MB_OK mov esp,dword ptr fs:[0] //使esp指向我们的ERR,此时[esp+0]就是下一个ERR的地址 pop dword ptr fs:[0] //让fs:[0]恢复到未安装我们的handler的情况 mov esp,ebp pop ebp retn END _Start 

在这里插入图片描述
handler:0x00401060是MessageBox的调用,上面四个push是MessageBox的参数
在这里插入图片描述此处下断,观察堆栈和TIB的情况如何
堆栈,此时压入的依次是后一个ERR的地址,我们的handler:
在这里插入图片描述
TIB,可以看到此时的ExceptionList是我们的ERR的地址,正是0x0019ff78
在这里插入图片描述至此,我们的异常处理就结束了。












版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/213229.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午6:17
下一篇 2026年3月18日 下午6:18


相关推荐

  • 科大讯飞iFLYTEK翻译机4.0星火版限时特惠

    科大讯飞iFLYTEK翻译机4.0星火版限时特惠

    2026年3月14日
    2
  • Linux下安装mysql-8.0.21

    Linux下安装mysql-8.0.21转载原文https://www.jianshu.com/p/4587e9429702下面记录了我在Linux(Centos7)环境下安装Mysql的完整过程,实操记录,绝非水文,如有错误或遗漏,欢迎指正。安装过程中务必保证文件路径的前后统一,否则可能会导致不可预期的结果,推荐直接使用文中的命令进行操作。一安装前准备1、检查是否已经安装过mysql,执行命令[root@localhost/]#rpm-qa|grepmysql从执行结果,可以看出我们已经安装了

    2022年5月10日
    66
  • Matlab矩阵基本操作(定义,运算)

    一、矩阵的表示在MATLAB中创建矩阵有以下规则:a、矩阵元素必须在”[]”内;b、矩阵的同行元素之间用空格(或”,”)隔开;c、矩阵的行与行之间用”;”(或回车符)隔开;d、矩阵的元素可以是数值、变量、表达式或函数;e、矩阵的尺寸不必预先定义。二,矩阵的创建:1、直接输入法最简单的建立矩阵的方法是从键盘直接输入矩阵的元素,输入

    2022年4月16日
    454
  • 把一个数据表导入另一个数据库_把一个表里的数据导入另一个表

    把一个数据表导入另一个数据库_把一个表里的数据导入另一个表文章作者:姜南(Slyar)文章来源:SlyarHome(www.slyar.com)转载请注明,谢谢合作。之前发了《表达式变量批量替换器batchSQL》这篇文章,有童鞋说导入数据用phpMyAdmin提供的csv导入功能不是更好。的确,导入数据进入mysql用这个功能非常好,不过如果需要进行批量操作的是update或者其他操作呢,例如要从新的excel里批量更新某一部分的数据,总不能全…

    2025年11月28日
    8
  • CSDN各产品线月度NPS分析报告新鲜出炉【2021年7月】[通俗易懂]

    CSDN各产品线月度NPS分析报告新鲜出炉【2021年7月】[通俗易懂]不知道各位用户大大有没有注意到,最近,咱们CSDN网站很多页面增加了这个功能模块:你愿意向朋友推荐xxx吗?哈哈,聪明的小伙伴们可能已经知道了,它其实就是个简单的满意度调查,学名NPS。那到底什么是NPS呢?且听C菌我慢慢道来。一、什么是NPS?NPS是针对企业良性收益与真实增长所提出的用户忠诚度概念,它是目前最流行的顾客忠诚度分析指标。根据用户的推荐意愿,将用户分为三类:推荐者、被动者、贬损者,推荐者与贬损者是对企业实际的产品口碑有影响的用户,这两部分用户在用户总数中所占百分比之差,就是.

    2022年5月5日
    56
  • QPM(量化项目管理)

    QPM(量化项目管理)一 1 首先收集原始数据 2 对收集的数据进行处理 会做出一张全生命周期的主模型 包括需求开发 需求评审 设计 设计评审 编码 代码验证 测试等 收集的数据包括 LL AVG UL STDEV 标准差 3 对搜集得到的数据进行归一化处理 二 1 定义假设 绿色 将各个搜集到的数据填写进去 假设就定义好了 2 定义决策 黄色 填写下限 从 1 开始 与上线 几种选择方法

    2026年3月19日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号