吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 17121|回复: 56
收起左侧

[系统底层] Windows异常处理原理

[复制链接]
SuperProgram 发表于 2019-9-30 11:20
本帖最后由 SuperProgram 于 2019-9-30 11:25 编辑

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 <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <winternl.h>
 
 
//异常处理函数
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<异常代码:%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");
}


&#8203;
下面在调试器中看下系统的异常处理流程,首先在内核的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异常的增强,后面会陆续讲到。
&#8203;

接着安装我们的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处理函数地址。
&#8203;
内核会将异常转到用户态,然后调用我们注册的处理函数,这就是windows异常处理的整体流程,后面会陆续分享涉及SEH、VEH等处理细节。
文章地址 https://blog.csdn.net/vs2008ASPNET/article/details/101752690

免费评分

参与人数 14吾爱币 +10 热心值 +13 收起 理由
nanjue、 + 1 扩展知识,过来学习
1900130512 + 1 + 1 用心讨论,共获提升!
mc.fpeng17 + 1 我很赞同!
该用户不想起名 + 1 + 1 我很赞同!
heng330917092 + 1 我很赞同!
刘样andholiday + 1 + 1 用心讨论,共获提升!
xu206363 + 1 用心讨论,共获提升!
Alluretoo + 1 + 1 热心回复!
世间寂寞 + 1 + 1 谢谢@Thanks!
一只皮皮虾 + 1 热心回复!
xuexiyixiaxia + 1 + 1 热心回复!
huangmo7111 + 1 + 1 谢谢@Thanks!
fengbolee + 1 + 1 谢谢@Thanks!
Lucifer_BW + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

zhoujinjian 发表于 2019-10-30 10:40
感觉window还是牛逼   只是微软体量太大 拖累了windows系统    说实话感觉window其实还是很厉害的  但是就是为了兼容  为了各种windwos问题导致它太笨重了  但是里面的设计是真的牛逼
bachelor66 发表于 2019-9-30 11:29
深奥,小白看的一头雾水啊。                                   
lengxiaoxi 发表于 2019-9-30 11:36
410791007 发表于 2019-9-30 11:54
完全不懂
zwqlon1978 发表于 2019-9-30 12:11
老蓝屏,怎么处理?
shelly1314 发表于 2019-9-30 12:16
感谢楼主分享
bester 发表于 2019-9-30 12:54
等大佬的VEH源码
韬. 发表于 2019-9-30 13:10
小白看的一头雾水
miaor123 发表于 2019-9-30 17:48
精品啊!  先收藏了再看
ssskys 发表于 2019-9-30 21:58
难度高呢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-22 00:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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