内核学习-异常处理
本文为看雪论坛优秀文章
看雪论坛作者ID:pyikaaaa
一
CPU异常记录
1、CPU异常的处理流程
① CPU指令检测到异常(例:除零)
2、分析中断处理函数 _KiTrap00
② 调用CommonDispatchException函数
(2)这样设计异常的目的是为了程序员有机会对异常进行处理。
3、CommonDispatchException函数分析
type struct _EXCEPTION_RECORD
{
DWORD ExceptionCode; //异常代码
DWORD ExceptionFlags; //异常状态
struct _EXCEPTION_RECORD* ExceptionRecord; //下一个异常
PVOID ExceptionAddress; //异常发生地址
DWORD NumberParameters; //附加参数个数
ULONG_PTR ExceptionInformation
[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针
}
4、总结
(1)CPU指令检测到异常
(2)查IDT表,执行中断处理函数
(3)调用CommonDispatchException(构建EXCEPTION_RECORD结构体)
(4)KiDispatchException(分发异常:目的是为了找到异常处理函数)
二
模拟异常记录
RaiseException分析
RaiseException函数分析
(1)把EXCEPTION_RECORD结构体的ExceptionCode最高位清零,用于区分CPU异常。
三
内核层用户处理流程
VOID KiDispatchException(ExceptionRecord, ExceptionFrame,TrapFrame,PreviousMode, FirstChance)
(1)KiDispatchException函数分析
KiDispatchException函数执行流程总结:
(2)RtlDispatchException函数分析
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
四
用户层异常处理流程
(1)上述内核层异常处理中,异常处理函数也在0环,不用切换堆栈,用户层异常发生在三环,异常处理函数也在3环,所以要切换堆栈(因为KiDispatchException在内核,从0环返到三环)回到3环执行异常处理函数。
(2)切换堆栈的处理方式与用户APC的执行过程几乎是一样的,惟一的区别就是执行用户APC时返回3环后执行的函数是KiUserApcDispatcher,而异常处理时返回3环后执行的函数是KiUserExceptionDispatcher。
(3)理解用户APC的执行过程是理解3环异常处理的关键。
分析用户层异常发生时的 KiDispatchException
(1)_KeContextFromKframes将Trap_frame被分到context为返回3环做准备
五
VEH(向量化异常处理)
KiUserExceptionDispatcher函数分析
_RtlDispatchException函数分析
作用:
代码:自定义VEH
typedef PVOID(NTAPI *FnAddVectoredExceptionHandler)(ULONG, _EXCEPTION_POINTERS*);
FnAddVectoredExceptionHandler MyAddVectoredExceptionHandler;
// VEH异常处理只能返回2个值
// EXCEPTION_CONTINUE_EXECUTION 已处理
// EXCEPTION_CONTINUE_SEARCH 未处理
//定义VEH的异常处理函数VectExcepHandler,这个函数只能有两个返回值
LONG NTAPI VectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
MessageBox(NULL,L"VEH异常处理函数执行了...",L"VEH异常",MB_OK);
if (pExcepInfo->ExceptionRecord->ExceptionCode == 0xC0000094)//异常被触发,判断是否是除0异常
{
//1.修改发生异常的代码的Eip idiv ecx长度2字节 从下一行开始执行
pExcepInfo->ContextRecord->Eip = pExcepInfo->ContextRecord->Eip + 2;
//2.将除数修改为1
//pExcepInfo->ContextRecord->Ecx = 1;
return EXCEPTION_CONTINUE_EXECUTION;//已处理
}
return EXCEPTION_CONTINUE_SEARCH;//未处理
}
int main()
{
//动态获取AddVectoredExceptionHandler函数地址
//AddVectoredExceptionHandler:将异常处理函数插入到VEH全局链表中
HMODULE hModule = GetModuleHandle(L"Kernel32.dll");
MyAddVectoredExceptionHandler = (FnAddVectoredExceptionHandler)::GetProcAddress(hModule,"AddVectoredExceptionHandler");
//参数1表示插入VEH链的头部, 0插入到VEH链的尾部
MyAddVectoredExceptionHandler(0, (_EXCEPTION_POINTERS *)&VectExcepHandler);
//构造除0异常
int val = 0;
_asm
{
xor edx, edx
xor ecx, ecx
mov eax, 100
idiv ecx //edx = eax / ecx
//异常触发,执行写入的异常处理函数
mov val, edx
}
printf("val = %d\n",val);
getchar();
}
typedef struct _EXCEPTION_POINTERS
{
PEXCEPTION_RECORD ExceptionRecord;//异常发生时的信息
PCONTEXT ContextRecord;//异常发生时的上下文环境
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
VEH异常的处理流程
① CPU捕获异常
六
SEH(结构化异常处理)
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD* Next; //下一个节点,-1就是没有下一个节点了
PEXCEPTION_ROUTINE Handler; //指向下一个SEH异常处理函数
} EXCEPTION_REGISTRATION_RECORD;
RtlDispatchException函数分析
/*SEH处理函数是提供给RtlDispatchException调用的,所以需要遵循一定的格式。
EXCEPTION_DISPOSITION _cdecl MyEexception_handler
(
struct _EXCEPTION_RECORD *ExceptionRecord, //异常结构体
PVOID EstablisherFrame, //SEH结构体地址
struct _CONTEXT *ContextRecord, //存储异常发生时的各种寄存器的值 栈位置等
PVOID DispatcherContext
)
*/
#include <stdio.h>
#include <windows.h>
/*
//0环异常处理时讲过这个结构体
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPtiON_ROUTINE Handler;
}
*/
struct MyException
{
struct MyException *prev;
DWORD handler;
};
/*SEH处理函数是提供给RtlDispatchException调用的,所以需要遵循一定的格式。
EXCEPTION_DISPOSITION _cdecl MyEexception_handler
(
struct _EXCEPTION_RECORD *ExceptionRecord, //异常结构体
PVOID EstablisherFrame, //SEH结构体地址
struct _CONTEXT *ContextRecord, //存储异常发生时的各种寄存器的值 栈位置等
PVOID DispatcherContext
)
*/
EXCEPTION_DISPOSITION __cdecl MyExceptionHandler(
struct _EXCEPTION_RECORD *ExceptionRecord, //ExceptionRecord存储异常信息:什么类型、异常产生位置
void * EstablisherFrame, //MyException结构体地址
struct _CONTEXT *ContextRecord, //Context结构体,存储异常发生时各种寄存器的值,堆栈位置等
void * Dispatchercontext)
{
MessageBox(NULL,L"SEH异常处理函数执行了...",L"SEH异常",NULL);
if (ExceptionRecord->ExceptionCode == 0xC0000094)
{
ContextRecord->Eip = ContextRecord->Eip + 2;
ContextRecord->Ecx = 100;
return ExceptionContinueExecution;
}
return ExceptionContinueSearch;
}
void TestException()
{
DWORD temp;
//插入异常,必须在当前线程的堆栈当中
//若定义成全局变量则无效
MyException myException;
__asm
{
mov eax, FS:[0]
mov temp, eax
lea ecx, myException
mov FS:[0], ecx
}
//链表插入操作,将原来的也挂入到插入的SEH后面
myException.prev = (MyException*)temp;
myException.handler = (DWORD)&MyExceptionHandler;
//构造除0异常
__asm
{
xor edx, edx
xor ecx, ecx
mov eax, 0x10
idiv ecx //EDX:EAX 除以 ECX
}
//处理完成,摘掉异常
__asm
{
mov eax, temp
mov FS:[0], eax
}
printf("函数执行完毕\n");
}
int main()
{
TestException();
return 0;
}
SEH异常的处理流程
七
编译器扩展的SEH
_try // 挂入链表
{
}
_except(过滤表达式) // 异常过滤
{
异常处理程序
}
except里的过滤表达式用于异常过滤,只能有以下三个值:
EXCEPTION_EXECUTE_HANDLER(1) 异常已经被识别,控制流将进入到 _except模块中运行异常处理代码。
EXCEPTION_CONTINUE_SEARCH(0) 异常不被识别,也即当前的这个 _except模块不是这个异常错误所对应的正确的异常处理模块。系统将继续到上 _try except域中继续查找一个恰当的 _except模块。
EXCEPTION_CONTINUE_EXECUTION(-1) 异常被忽略,控制流将在异常出现的点之后,继续恢复运行。
过滤表达式只能有三种写法:
_asm
{
mov eax,FS:[0]
mov temp,eax
lea ecx,myException
mov FS:[0],ecx
}
看雪ID:pyikaaaa
https://bbs.pediy.com/user-home-921642.htm
# 往期推荐
3.frida内存检索svc指令查找sendto和recvfrom进行hook抓包
球分享
球点赞
球在看
点击“阅读原文”,了解更多!