12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
三国霸业 战车-珍宝-觉醒-攻城掠地SF-全新玩法 免费玩新区 攻城掠地私服 进入游戏
手游私服盒子 各类免费游戏 0.1折送海量资源 各类手游私服 进入游戏
皇家MU2 《奇迹 2:传奇》韩国网禅公司《奇迹》正统续作。 3D锁视角Mmrpg 暗黑3+传奇+流放之路+奇迹 进入游戏
查看: 350|回复: 0

Windows异常处理原理

[复制链接]

49

主题

49

帖子

108

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
108
发表于 2020-7-7 20:22:22 | 显示全部楼层 |阅读模式
Windows运行过程中,不可避免会产生各种异常(由内核或应用程序),系统提供了一套强大的异常处理机制,灵活的使用它,可以让我们的应用程序变的更健壮。
了解涉及异常处理的数据结构
IDT 系统中断表
​
有异常产生时,处理器根据IDT的中断号,找到对应的处理函数 KiTrapxx,异常处理函数会将异常封装到一个数据结构。
[C++] 纯文本查看 复制代码typedef struct _EXCEPTION_RECORD32 {        DWORD    ExceptionCode; //异常代码        DWORD ExceptionFlags;  //异常标志        DWORD ExceptionRecord;  //EXCEPTION_RECORD32指针        DWORD ExceptionAddress; //产生异常时的地址        DWORD NumberParameters; //异常附加信息数量        DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //异常附加信息,指针指向一个数组} EXCEPTION_RECORD32, *PEXCEPTION_RECORD32;
CONTEXT异常上下文 ,不太严谨,姑且这样认为吧,它包含了异常产生时,执行环境的详细信息
[C++] 纯文本查看 复制代码typedef struct _CONTEXT {         //标识哪些成员是有效的        DWORD ContextFlags;          //调试寄存器         DWORD   Dr0;        DWORD   Dr1;        DWORD   Dr2;        DWORD   Dr3;        DWORD   Dr6;        DWORD   Dr7;          FLOATING_SAVE_AREA FloatSave;         //        // This section is specified/returned if the        // ContextFlags word contians the flag CONTEXT_SEGMENTS.        //         DWORD   SegGs;        DWORD   SegFs;        DWORD   SegEs;        DWORD   SegDs;         //        // This section is specified/returned if the        // ContextFlags word contians the flag CONTEXT_INTEGER.        //         DWORD   Edi;        DWORD   Esi;        DWORD   Ebx;        DWORD   Edx;        DWORD   Ecx;        DWORD   Eax;         //        // This section is specified/returned if the        // ContextFlags word contians the flag CONTEXT_CONTROL.        //         DWORD   Ebp;        DWORD   Eip;        DWORD   SegCs;              // MUST BE SANITIZED        DWORD   EFlags;             // MUST BE SANITIZED        DWORD   Esp;        DWORD   SegSs;         //        // This section is specified/returned if the ContextFlags word        // contains the flag CONTEXT_EXTENDED_REGISTERS.        // The format and contexts are processor specific        //         BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT;


当KiTrapxxx处理完成并将异常信息封装好后,转而调用系统内核nt!KiDispatchException函数。
KiDispatchException 该函数是异常处理的核心函数,该函数代码比较长,这里贴出函数头的部分代码。
​
该函数会根据异常类型及是否存在调试器,来进行不同的处理。
    内核态

    1.如果存在内核调试器,并设置处理进度为第一次处理,内核调试器如果不能处理异常则会中断,重新把控制权交给用户,如果调试器处理了异常,那么会从发生异常的地方继续执行指令。
    2.不存在内核调试器,或者内核调试器不能处理异常,内核就会调用RtlDispatchException函数,依次调用注册的异常处理函数。(SEH)
    3.如果RtlDispatchException也没有能够处理异常,内核会重新将异常交给调试器。
    4.如果调试器仍然处理不了,此时就可以见到久违的蓝屏错误了。(0x0000008e)
    用户态

1.如果存在内核调试器,将异常交给调试器处理。
2.如果存在用户态调试器,则交给调试器处理,调试器未处理该异常或者不存在调试器,KiDispatchException会压入2个数据结构,exception_record、context,让后将控制权返回给用户态函数,位于ntdll!RtlDispatchException。
3.如果RtlDispatchException未能处理异常,此时会再次将异常返回给内核KiDispatchException,它会将异常重新转给用户态调试器,如果仍然无法处理异常,就结束进程。
4.结束进程前,系统会再次调用注册的异常处理函数,然后会结束异常进程。

SEH (结构化异常处理)
前面讲到的是内核异常处理流程,下面说下用户态的异常处理。
SEH是一种异常处理机制,发生异常时,系统通过它找到处理函数,处理该异常。
涉及到的数据结构
TEB  线程环境块(翻译过来是这样的)
[C++] 纯文本查看 复制代码typedef struct _TEB {    PVOID Reserved1[12];    PPEB ProcessEnvironmentBlock;    PVOID Reserved2[399];    BYTE Reserved3[1952];    PVOID TlsSlots[64];    BYTE Reserved4[8];    PVOID Reserved5[26];    PVOID ReservedForOle;  // Windows 2000 only    PVOID Reserved6[4];    PVOID TlsExpansionSlots;} TEB, *PTEB;
TIB 线程信息块
typedef struct _NT_TIB32 {    DWORD ExceptionList;    DWORD StackBase;    DWORD StackLimit;    DWORD SubSystemTib;#if defined(_MSC_EXTENSIONS)    union {        DWORD FiberData;        DWORD Version;    };#else    DWORD FiberData;#endif    DWORD ArbitraryUserPointer;    DWORD Self;} NT_TIB32, *PNT_TIB32;
异常处理结构( ExceptionList成员)
[C++] 纯文本查看 复制代码typedef struct _NT_TIB32 {    DWORD ExceptionList;    DWORD StackBase;    DWORD StackLimit;    DWORD SubSystemTib; #if defined(_MSC_EXTENSIONS)    union {        DWORD FiberData;        DWORD Version;    };#else    DWORD FiberData;#endif     DWORD ArbitraryUserPointer;    DWORD Self;} NT_TIB32, *PNT_TIB32;

异常处理结构( ExceptionList成员)[C++] 纯文本查看 复制代码typedef struct _EXCEPTION_REGISTRATION_RECORD {    struct _EXCEPTION_REGISTRATION_RECORD *Next; //下一个处理    PEXCEPTION_ROUTINE Handler; //异常处理函数} EXCEPTION_REGISTRATION_RECORD;异常处理函数声明
typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
    _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord, //异常信息
    _In_ PVOID EstablisherFrame, //陷阱帧
    _Inout_ struct _CONTEXT *ContextRecord, //异常上下文
    _In_ PVOID DispatcherContext // 未知
    );

typedef EXCEPTION_ROUTINE *PEXCEPTION_ROUTINE;
从XP到现在的win10,这些数据结构都没有大的变动。
从以上流程知道,要处理异常,需要注册SEH处理函数,SEH位于TIB头,是一个单项链表,通过next指向下一个处理函数。
SEH异常处理代码(此处代码来自网上,做了部分调整)
[C++] 纯文本查看 复制代码#include #include #include #include   //异常处理函数DWORD dwTest;EXCEPTION_DISPOSITION NTAPI ExceptHandler(        _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,        _In_ PVOID EstablisherFrame,        _Inout_ struct _CONTEXT *ContextRecord,        _In_ PVOID DispatcherContext) {        printf("进入异常处理\n");        printf("异常地址:%X\n", ExceptionRecord->ExceptionAddress,                ExceptionRecord->ExceptionCode);        ContextRecord->Eax = (DWORD)(&dwTest);        return ExceptionContinueExecution;}   int main(){        PTEB teb=NULL;         _asm        {                mov eax,fs:[0]                mov teb,eax        }         printf("TEB %X \n", teb);          printf("注册SEH\n");        __asm {                lea eax, ExceptHandler  //将异常处理函数地址装入eax                push eax //异常处理函数入栈                push fs : [0] //构造EXCEPTION_REGISTRATION_RECORD                mov dword ptr fs : [0], esp //将构造好的处理函数加入SEH链表头部        }         __asm {                xor eax, eax                mov dword ptr[eax], 1234h  //内存访问无效异常        }        printf("删除SEH\n");         //卸载异常处理函数并恢复堆栈        __asm {                pop dword ptr fs : [0]                add esp, 4        }         printf("dwTest=%X\n", dwTest);         system("pause");}

​
下面在调试器中看下系统的异常处理流程,首先在内核的KiDispatchException函数下断,运行exe。
bp 0x83ef7dc1虚拟机有问题,触发异常后关闭,就在用户模式下调试了。
注册SEH前的异常处理列表0:000> dd fs:[0]
0053:00000000  0019ff60 001a0000 0019d000 00000000
0053:00000010  00001e00 00000000 00260000 00000000
0053:00000020  00004d5c 00000308 00000000 0026002c
0053:00000030  0025d000 00000000 00000000 00000000
0053:00000040  00000000 00000000 00000000 00000000
0053:00000050  00000000 00000000 00000000 00000000
0053:00000060  00000000 00000000 00000000 00000000
0053:00000070  00000000 00000000 00000000 00000000
0x19ff60 ExceptionList地址,继续查看 dd 0x19ff600:000> dd 0x19ff60
0019ff60  0019ffcc 00434bb0 af181fd2 00000000
0019ff70  0019ff80 74b50419 0025d000 74b50400
0019ff80  0019ffdc 7750662d 0025d000 6bd86c90
0019ff90  00000000 00000000 0025d000 00000000
0019ffa0  00000000 00000000 00000000 00000000
0019ffb0  00000000 00000000 00000000 00000000
0019ffc0  00000000 0019ff8c 00000000 0019ffe4
0019ffd0  775186d0 1c9b01fc 00000000 0019ffec
19ffcc 指向下一个异常处理函数,434bb0地址为当前异常处理函数地址。
我们看下,下一个异常处理函数0:000> dd 0x19ffcc
0019ffcc  0019ffe4 775186d0 1c9b01fc 00000000
0019ffdc  0019ffec 775065fd ffffffff 775251d2
0019ffec  00000000 00000000 0042c6e5 0025d000
0019fffc  00000000 78746341 00000020 00000001
001a000c  00003318 000000dc 00000000 00000020
001a001c  00000000 00000014 00000001 00000007
001a002c  00000034 0000017c 00000001 00000000
001a003c  00000000 00000000 00000000 00000000
0x775186d0处函数 ,这是MSVC编译器对系统SEH异常的增强,后面会陆续讲到。
​

接着安装我们的SEH异常处理函数。
004325e6 8d05cfc34200   lea     eax, [targetexe!ILT+970(?ExceptHandlerYG?AW4_EXCEPTION_DISPOSITIONPAU_EXCEPTION_RECORDPAXPAU_CONTEXT (0042c3cf)]004325ec 50             push    eax004325ed 64ff3500000000 push    dword ptr fs:[0]004325f4 64892500000000 mov     dword ptr fs:[0], esp fs:0053:00000000=0019ff60  //安装异常处理函数004325fb 33c0           xor     eax, eax004325fd c70034120000   mov     dword ptr [eax], 1234h //产生内存写入异常
查看安装SEH后的异常处理列表0:000> dd 19fe44
0019fe44  0019ff60 0042c3cf 00646110 00499d2c
0019fe54  0025d000 cccccccc cccccccc cccccccc
0019fe64  cccccccc cccccccc cccccccc cccccccc
0019fe74  cccccccc cccccccc cccccccc cccccccc
0019fe84  cccccccc cccccccc cccccccc cccccccc
0019fe94  cccccccc cccccccc cccccccc cccccccc
0019fea4  cccccccc cccccccc cccccccc cccccccc
0019feb4  cccccccc cccccccc cccccccc cccccccc
0x42c3cf 这里是我们注册的SEH处理函数地址。
​
内核会将异常转到用户态,然后调用我们注册的处理函数,这就是windows异常处理的整体流程,后面会陆续分享涉及SEH、VEH等处理细节。
文章地址 https://blog.csdn.net/vs2008ASPNET/article/details/101752690
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-11-25 12:51 , Processed in 0.093750 second(s), 30 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表