9 异常

如果你希望在软件调试上有所突破,或者想了解如何通过异常进行反调试,或者想自己写一个调试器,那么就必须要深入了解异常,异常与调试是紧密相连的,异常是调试的基础。

异常产生后,首先是要记录异常信息(异常的类型、异常发生的位置等),然后要寻找异常的处理函数,我们称为异常的分发,最后找到异常处理函数并调用,我们称为异常处理。我们后续的学习,也是围绕异常的、分发、处理。

异常记录

异常可以简单分为2类,即CPU产生的异常软件模拟产生的异常,如下两张图所示,我们可以看见第一张图中进行了除法运算,CPU检测到除数为0,就产生了异常;第二张图中使用了throw关键词,通过软件模拟主动产生了异常。

images/download/attachments/2949166/image2023-5-25_20-56-53.png

images/download/attachments/2949166/image2023-5-25_20-57-1.png

CPU的异常记录

我们先了解一个结构体_EXCEPTION_RECORD,它的格式及每个成员的意义如下:

typedef struct _EXCEPTION_RECORD 
{
	DWORD ExceptionCode; // 异常状态码,在Windows中每一种状态(包括异常)都有一个状态码
	DWORD ExceptionFlags; // 异常状态,0表示CPU异常,1表示软件模拟异常,8表示堆栈异常
	struct _EXCEPTION_RECORD *ExceptionRecord; // 通常情况下该值为空,如果发生嵌套异常(即处理异常时又出现了异常)则指向下一个异常
	PVOID ExceptionAddress; // 异常发生地址,表示异常发生时的位置
	DWORD NumberParameters; // 附加参数个数
	ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; // 附加参数指针
} EXCEPTION_RECORD, *PEXCEPTION_RECORD;

Windows状态码及其对应含义,我们可以在在线文档中获取:[MS-ERREF]: NTSTATUS Values | Microsoft Learn,如下图所示我们可以看见,整数除0时的异常状态码为0xC0000094。

images/download/attachments/2949166/image2023-5-25_21-36-30.png

接着我们可以通过除0的例子来看一下CPU的异常记录过程,它的大致过程就是:CPU指令检测到异常→查IDT表执行中断处理函数→执行CommonDispatchException→执行KiDispatchException。我们可以通过IDA打开Ntoskrnl.exe先找到IDT表,通过ALT+T快捷键全局搜索_IDT,找到IDT表。

images/download/attachments/2949166/image2023-5-25_21-47-45.png

根据下图所示的中断描述符表我们可以知道除0异常对应的0号中断处理函数,因此我们就可以在IDA中进入对应的处理函数:

images/download/attachments/2949166/image2023-5-25_21-50-9.png

images/download/attachments/2949166/image2023-5-25_21-51-19.png

该函数的前面一部分代码和KiSystemService函数(系统调用API进0环)的代码一样,都是用来保存现场的:

images/download/attachments/2949166/image2023-5-25_21-54-40.png

接着向下可以看到,该中断处理函数直至执行结束都没有对异常进行处理(微软在设计时,希望程序员自己能够对异常进行处理,因此在中断处理函数中并没有对异常进行处理),反而是有多处的跳转,调用了另一个函数CommonDispatchException。

images/download/attachments/2949166/image2023-5-25_22-0-10.png

跟进CommonDispatchException函数,我们可以看见它开辟了一块大小为0x50的空间,用于存放_EXCEPTION_RECORD结构体,并且给结构体的每个成员赋值,最终执行KiDispatchException函数,该函数通常用来分发异常,目的是找到异常的处理函数。

images/download/attachments/2949166/image2023-5-25_22-8-38.png

其中异常状态码是来自上层的EAX,这点我们通过之前的流程就可以知道,接着我们来看异常发生地址(即ExceptionAddress)是来自上层的EBX,而EBX又来自[EBP+0x68],这里实际上指的是Trap_Frame结构体0x68偏移位Eip成员,它是用来记录中断发生地址的,这是因为在保存现场结束之后,ESP指向Trap_Frame的顶部,而EBP也与ESP一样

images/download/attachments/2949166/image2023-5-25_22-22-29.png

软件模拟的异常记录

我们接着来看一下throw关键词触发的软件模拟异常记录过程,在关键词处下断点,然后运行通过反汇编代码我们可以知道它调用的就是CxxThrowException函数:

images/download/attachments/2949166/image2023-5-26_12-30-18.png

跟进该函数,会发现它也是调用了另外一个函数RaiseException,并通过栈的方式压入了几个传参:

images/download/attachments/2949166/image2023-5-26_12-36-14.png

接着我们跟进RaiseException函数发现,正是这几个传参填充了_EXCEPTION_RECORD结构体,在这里我们会发现两个比较关键成员的值都与CPU异常记录时的赋值内容不一致,首先是ExceptionCode,它的值很明显是一个Windows异常状态码中没有的(即0xE06D7363),这是因为在软件模拟产生的异常场景下,ExceptionCode的值是根据不同的编译环境而生成的;其次是ExceptionAddress,如下图中我们可以看见,它的值是RasieException函数的首地址,而并不是真正产生异常的那段地址。

images/download/attachments/2949166/image2023-5-26_12-45-24.png

RaiseException函数之后的流程是这样的:RtlRaiseException→NtRaiseException→KiRaiseException,在最后执行到KiRaiseException函数时,会将ExceptionCode的最高位清0,便于区分CPU/软件模拟异常

images/download/attachments/2949166/image2023-5-26_12-53-55.png

虽然模拟异常与CPU异常有一定的差异,但是在最后,两者都会去调用KiDispatchException函数,用于异常分发

images/download/attachments/2949166/image2023-5-26_12-56-19.png

异常分发与处理

异常可以发生在用户空间,也可以发生在内核空间。无论是CPU异常还是模拟异常,无论是用户空间异常还是内核空间异常,最终都要通过KiDispatchException函数进行分发,理解这个函数是学好异常的关键,这个函数比较复杂,我们以内核、用户两个角度来分析学习。

内核异常

本章我们主要分析内核异常是如何分发,如何处理的。首先我们需要来了解一下KiDispatchException函数的格式,及每个参数的作用:

VOID KiDispatchException (
	PEXCEPTION_RECORD ExceptionRecord, // 异常记录结构体
	PKEXCEPTION_FRAME ExceptionFrame, // X86系统下,该值为NULL
	PKTRAP_FRAME TrapFrame, // 3环进0环保存现场所用的结构体
	KPROCESSOR_MODE PreviousMode, // 先前模式,表示调用来自什么模式,0表示内核模式,1表示用户模式
	BOOLEAN FirstChance // 判断是否是第一次分发这个异常,对于同一个异常,Windows最多分发两次
	// 该值为1表示第一次分发,为0表示第二次分发
)

通过IDA直接打开Ntoskrnl.exe模块,找

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950823.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过可穿戴外骨骼,以更灵活的方式操作你的机器人。

今天,我们将介绍一款专为控制 Mercury X1 和 Mercury B1 机械臂而设计的创新外骨骼。这种外骨骼以人类手臂的结构为蓝本,可实现直观和精确的控制。 开发这种外骨骼的动机源于人们对深度学习和机器学习等领域日益增长的兴趣。这些技术使机器人能够自主学习…

esp32开发笔记之一:esp32开发环境搭建vscode+ubuntu

最近想用esp32做一个物联网项目,踩坑N个终于有点心得,写下来避免和我一样的小白踩无谓的坑。 写在前面: 第一,大家一定要用linux系统作为编译工具,速度上是windows无法比的,不要因为不熟悉linux而选择win…

58. Three.js案例-创建一个带有红蓝配置的半球光源的场景

58. Three.js案例-创建一个带有红蓝配置的半球光源的场景 实现效果 本案例展示了如何使用Three.js创建一个带有红蓝配置的半球光源的场景,并在其中添加一个旋转的球体。通过设置不同的光照参数,可以观察到球体表面材质的变化。 知识点 WebGLRenderer …

【OpenCV】使用Python和OpenCV实现火焰检测

1、 项目源码和结构(转) https://github.com/mushfiq1998/fire-detection-python-opencv 2、 运行环境 # 安装playsound:用于播放报警声音 pip install playsound # 安装opencv-python:cv2用于图像和视频处理,特别是…

STM32-笔记39-SPI-W25Q128

一、什么是SPI? SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且 在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为…

LabVIEW四旋翼飞行器姿态监测系统

四旋翼飞行器姿态监测系统是一个集成了高度、速度、俯仰角与滚转角数据采集与分析的系统,提高飞行器在复杂环境中的操作精确度与安全性。系统利用LabVIEW平台与硬件传感器相结合,实现实时数据处理与显示,有效地提升了四旋翼飞行器的监测与控制…

arcgisPro加载CGCS2000天地图后,如何转成米单位

1、导入加载的天地图影像服务,一开始是经纬度显示的。 2、右键地图,选择需要调整的投影坐标,这里选择坐标如下: 3、点击确定后,就可以调整成米单位的了。 4、切换后结果如下: 如有需要,可调整成…

新时期下k8s 网络插件calico 安装

1、k8s master节点初始化完毕以后一直处于notreadey状态,一直怀疑是安装有问题或者是初始化有问题(当然,如果真有问题要先解决这些问题),经过不断探索才发现是网络插件没有安装导致的,根据建议安装calico插…

《分布式光纤测温:解锁楼宇安全的 “高精度密码”》

在楼宇建筑中,因其内部空间庞大,各类电器设施众多,如何以一种既高效又稳定,兼具低成本与高覆盖特性的方式,为那些关键线路节点开展温度监测,是目前在安全监测领域一项重点研究项目,而无锡布里渊…

基于vue框架的的校园社团管理平台9ck4e(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能:社团,社团加入,学生,社团活动,活动报名,社团成员,物资信息,物资申请 开题报告内容 基于Vue框架的校园社团管理平台开题报告 一、研究背景与意义 随着信息化技术的快速发展,校园管理正逐步向数字化、智能化转型。校园社团作…

网络安全 基础入门-概念名词

域名相关 域名 域名和IP地址相互映射,这样不用去记住能够被机器直接读取的IP地址数串 域名系统(DNS) 它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用UDP端口53。 1. 如果是自动获取dns,就向上查询&#xff…

CSS Grid 布局全攻略:从基础到进阶

文章目录 一.Grid 是什么二.示例代码1. 基础使用 - 固定宽高2.百分百宽高3.重复设置-repeat4.单位-fr5.自适应6.间距定义其他 一.Grid 是什么 CSS 中 Grid 是一种强大的布局方式,它可以同时处理行和列 Grid 和Flex有一些类似,都是由父元素包裹子元素使用…

自动驾驶控制算法-油门刹车标定

本文是学习自动驾驶控制算法第十讲 油门刹车标定表的制作以及后续一节的学习笔记。 车辆油门刹车标定的目的是获取不同车速下不同的油门踏板或刹车踏板行程下车辆的加速度,标定的结果作为纵向控制模型的输入,用于计算输出合理的油门或刹车。 1 Apollo中…

2024年1月4日蜻蜓hr人才招聘系统v1.1.7更新-正式版发布-客户端源代码开源发布供学习-本产品完成上线正式版-修复多个bug-优雅草果果|小无

2024年1月4日蜻蜓hr人才招聘系统v1.1.7更新-正式版发布-客户端源代码开源发布供学习-本产品完成上线正式版-修复多个bug-优雅草果果|小无 前端代码开源库 关于开源说明:企业服务-招聘信息管理系统-前端uniapp-系统前端开放源代码仅供学习-优雅草科技-目前优雅草科…

Java-JVM详解

Java-JVM ①JVM概述 ❶基本介绍 JVM:全称 Java Virtual Machine,一个虚拟计算机,Java 程序的运行环境(Java二进制字节码的运行环境) 特点: Java 虚拟机基于二进制字节码执行,由一套字节码指…

PyTorch 框架实现线性回归:从数据预处理到模型训练全流程

系列文章目录 01-PyTorch新手必看:张量是什么?5 分钟教你快速创建张量! 02-张量运算真简单!PyTorch 数值计算操作完全指南 03-Numpy 还是 PyTorch?张量与 Numpy 的神奇转换技巧 04-揭秘数据处理神器:PyTor…

升级 Spring Boot 3 配置讲解 —— 支持断点传输的文件上传和下载功能

学会这款 🔥全新设计的 Java 脚手架 ,从此面试不再怕! 在现代 Web 应用中,文件上传和下载是非常常见的需求。然而,当文件较大时,传统的上传下载方式可能会遇到网络不稳定或传输中断的问题。为了解决这些问题…

R语言安装教程与常见问题

生物信息基础入门笔记 R语言安装教程与常见问题 今天和大家聊一个非常基础但是很重要的技术问题——如何在不同操作系统上安装R语言?作为生物信息学数据分析的神兵利器,R语言的安装可谓是入门第一步,学术打工人的必备技能。今天分享在Windows…

UI自动化测试保姆级教程--pytest详解(精简易懂)

欢迎来到啊妮莫的学习小屋 别让过去的悲伤,毁掉当下的快乐一《借东西的小人阿莉埃蒂》 简介 pytest是一个用于Python的测试框架, 支持简单的单元测试和复杂的功能测试. 和Python自带的UnitTest框架类似, 但是相比于UnitTest更加简洁, 效率更高. 特点 非常容易上手…

基类指针指向派生类对象,基类指针的首地址永远指向子类从基类继承的基类首地址

文章目录 基类指针指向派生类对象&#xff0c;基类指针的首地址永远指向子类从基类继承的基类起始地址。代码代码2 基类指针指向派生类对象&#xff0c;基类指针的首地址永远指向子类从基类继承的基类起始地址。 代码 #include <iostream> using namespace std;class b…