Windows异常学习笔记(一)—— CPU异常记录&模拟异常记录

基础知识

一个异常产生后,首先是要记录异常信息(异常的类型、异常发生的位置等),然后要寻找异常的处理函数,称为异常的分发,最后找到异常处理函数并调用,称为异常处理

异常的分类

  1. CPU产生的异常
  2. 软件模拟产生的异常

例一:在C语言中使用除法时,若CPU检测到除数为0,便会抛出异常(CPU产生的异常
在这里插入图片描述
例二:使用代码抛出异常(软件模拟产生异常
在这里插入图片描述

CPU异常

处理流程

1.CPU指令检测到异常(例:除0),异常一定是先由CPU发现的
2.查IDT表,执行中断处理函数,不同的异常调用不同的中断处理函数
3.CommonDispatchException
4.KiDispatchExceeption

Windows异常代码(出自 张银奎《软件调试》):
在这里插入图片描述

分析中断处理函数 _KiTrap00

执行流程

  1. 保存现场(将寄存器堆栈位置等信息保存到 _Trap_Frame 中)
  2. 调用CommonDispatchException函数(分析参数EAX,EBX中的值)

查看IDT表反汇编(通过 ntoskrnl.exe 搜索字符串 _IDT 进行定位):
#### 第一步:定位中断表
在这里插入图片描述
在这里插入图片描述
[ebp+68h]:指向 _Trap_Frame 中的 Eip 的位置,当程序产生异常时,保存进入异常处理前的地址
0xC0000094h:除0异常的异常代码
loc_407399 调用了 CommonDispatchException
在这里插入图片描述
总结

  1. 异常处理函数中并没有直接对异常进行处理,而是调用了CommonDispatchException
  2. 这样设计异常的目的是为了程序员有机会对异常进行处理

分析 CommonDispatchException

CommonDispatchException在堆栈上构建了一个结构体,然后把异常的一些相关信息存储到一个结构体里
在这里插入图片描述
结构体:

type struct _EXCEPTION_RECORD		
{								
	DWORD ExceptionCode;				//异常代码,除0时为0x0C0000094
	DWORD ExceptionFlags;				//异常状态,0位CPU异常,1位软件模拟异常,堆栈异常为8等等
	struct _EXCEPTION_RECORD* ExceptionRecord;	//下一个异常,通常为空
	PVOID ExceptionAddress;				//异常发生地址
	DWORD NumberParameters;				//附加参数个数
	ULONG_PTR ExceptionInformation 
	[EXCEPTION_MAXIMUM_PARAMETERS];		//附加参数指针
}

总结

CPU异常执行的流程
1、CPU指令检测到异常
2、查IDT表,执行中断处理函数
3、调用CommonDispatchException(构建EXCEPTION_RECORD)
4、KiDispatchException(分发异常:目的是找到异常的处理函数)

软件模拟异常

模拟异常的产生

CxxThrowException

(KERNEL32.DLL)RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *lpArguments)

NTDLL.DLL!RtlRaiseException()

NT!NtRaiseException

NT!KiRaiseException

实验:分析模拟异常

第一步:编译并运行以下代码
#include <stdio.h>

int main()
{
	throw 1;

	getchar();
	return 0;
}
第二步:查看汇编代码

在这里插入图片描述
可以看到,当人为抛出一个异常时,实际上就是调用了 __CxxThrowException 这样一个函数

注意:本实验使用的编辑器为vc6.0,为C++环境,在不同的环境中,调用的函数可能是不同的,但是最终调用的系统函数都是相同的

第三步:分析 __CxxThrowException

在这里插入图片描述
__CxxThrowException 调用了 Kernel32.dll 中的函数 RaiseException

第四步:分析 RaiseException
  1. 填充ExceptionRecord结构体
type struct _EXCEPTION_RECORD		
{								
	DWORD ExceptionCode;				//异常代码
	DWORD ExceptionFlags;				//异常状态
	struct _EXCEPTION_RECORD* ExceptionRecord;	//下一个异常
	PVOID ExceptionAddress;				//异常发生地址
	DWORD NumberParameters;				//附加参数个数
	ULONG_PTR ExceptionInformation 
	[EXCEPTION_MAXIMUM_PARAMETERS];		//附加参数指针
}

在这里插入图片描述
2. 调用 Ntdll.dll!RtlRaiseException
在这里插入图片描述


注意一:当CPU产生异常时会记录一个ErrorCode,通过查表可以查到ErrorCode具体的含义,不同的异常对应不同的错误代码,但是软件抛出的ErrorCode是根据编译环境决定的,如下图的EDX中存储的值即为当前编译环境的ErrorCode
在这里插入图片描述
注意二CPU记录异常的地址是真正发生异常的地址,但软件抛出异常时记录的地址是__RaiseException函数的地址
在这里插入图片描述

第五步:分析 KiRaiseException

(IDA代码待补充)

  1. EXCEPTION_RECORD.ExceptionCode最高位清零 用于区分CPU异常。

  2. 调用 KiDispatchException 开始分发异常

总结

异常只有在记录的时候处理方式不同,最终在分发时走向同一个地方
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值