两种异常(CPU异常、用户模拟异常)的收集
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
两种异常(CPU异常、用户模拟异常)的收集
文章的核心:异常收集的是什么?(TrapFrame与ExceptionRecord);如何收集异常?(看文章)。
1. 异常的分类
① CPU指令异常 (比如除零异常) CPU运行检测到;
② 用户模拟异常 (throw 1)
其在收集是存在不同,但在派发时和处理时是完全相同的,下面我们就来分析一下其存在的不同。
2. CPU指令异常 - 除零异常(分析)
1)Trap00函数分析
除零异常会引发中断,其执行 Trap00 函数,该函数逆向分析如下,我们在三环进零环时,已经学习过一个_TrapFrame结构,其本质也是填写该结构。
注意,我们应该清楚,TrapFrame结构不只是三环进零环使用,而是只要走IDT表都要使用该结构,其是通过TrapFrame怎么返回,至于从三环还是零环进来这不重要。
其分析情况如下图,Trap00总结做已下事情:
① 保存TrapFrame基本的信息;
② 检查DebugActive是否存在调试器,若存在调试器,则保存Dr相关寄存器;
③ 判断来自内核或三环,来确定其错误号(0xc0000094/0xc0000095);
④ 其最后有三次机会走CommonDispatchException函数。
2) CommonDispatchException 异常记录函数分析
该函数就是生成ExceptionRecord这个结构体,填写完成,然后调用nt!KiDispatchException进行下一步分析(三环的模拟异常直接在三环形成,然后拷贝到零环)。
我们要知道ExceptionRecord的作用:存储了异常码、异常属性、异常记录(链表,多次出现异常时记录)、异常发生地址、异常有关参数。
其中该函数的重要一点:其ExceptionFlags置为0,我们之后关注KiDispatchException如何处理的。
3.用户模拟异常分析
像thorw 1 这种引发的是用户模拟异常,我们下面就来分析一下如下代码:
#include "stdafx.h"
#include <windows.h> int main(int argc, char* argv[])
{
throw ; return ;
}
9: throw 11;
00401028 mov dword ptr [ebp-4],0Bh
0040102F push offset __TI1H (00423580)
00401034 lea eax,[ebp-4]
00401037 push eax
00401038 call __CxxThrowException@8 (004011e0)
1)CxxThrowException反汇编分析
如下是CxxThrowException函数的反汇编,该函数需要两个参数,一个是我们模拟的代码,另外一个是ExceptionList,我们下面详细分析该段代码的执行流程:
004011E0 push ebp
004011E1 mov ebp,esp
004011E3 sub esp,20h
004011E6 push esi
004011E7 push edi
004011E8 mov ecx,8
004011ED mov esi,offset string "The value of ESP was not properl"...+0E0h (00422118)
004011F2 lea edi,[ebp-20h]
004011F5 rep movs dword ptr [edi],dword ptr [esi]
// 复制一段结构体
004011F7 mov eax,dword ptr [ebp+8]
004011FA mov dword ptr [ebp-8],eax
004011FD mov ecx,dword ptr [ebp+0Ch]
00401200 mov dword ptr [ebp-4],ecx
// 传入参数
00401203 lea edx,[ebp-0Ch]
00401206 push edx // 参数指针
00401207 mov eax,dword ptr [ebp-10h]
0040120A push eax // 参数个数
0040120B mov ecx,dword ptr [ebp-1Ch]
0040120E push ecx // 异常标志位
0040120F mov edx,dword ptr [ebp-20h]
00401212 push edx // 异常码
00401213 call dword ptr [__imp__RaiseException@16 (0042a154)]
该代码所做的事情如下:
① 先从内存中拷贝一段0x20字节的固定结构体到堆栈中;
② 将ExceptionList也拷贝到堆栈中(该结构体内部);
③ 传入有关参数调用RaiseException函数。
可能光看比较抽象,下面画出其详细的栈帧图就知道了,注意,ThrowCode虽然从用户代码传入进来,但分析其函数并没有用到,而是直接调用一段固定的异常码。
而&ThrowCode以及异常链被作为其参数存储,这样通过分析就可以轻易找到其ThrowCode值,其作为参考之后来处理SEH。
2)kernel32!RaiseException函数分析
前面分析过,其CxxThrowException函数调用该函数,该函数的本质就是生成一个ExceptionRecord结构体,
我们之前分析过,如果是CPU指令异常,其在CommonDispatchException函数中生成该结构体;
函数分析如下,该过程比较好理解,注意其触发异常的地址标记为该地址,并不是ThrowCode的地址,这个是你要明确的,后续在将异常处理时这里用到SEH异常,我们再继续分析
3)ntdll!RtlRaiseException函数分析
该函数分析如下,其在三环进入零环前在堆栈中保存了一个_CONTEXT结构体,我们之前在用户APC分析过,返回三环时要在零环向三环堆栈中写入一个CONTEXT用于保存。
来自用户层的异常我们确定其必须返回,,则就进零环之前就直接在三环中预先保存了一个_CONTEXT结构体,至于其如何使用,我们在异常的处理中会详细分析到。
这里我们要关注CONTEXT几个比较重要的点:eip与esp,因为其是程序的重要落脚点,发现其是该函数的返回地址与上一个函数的堆栈图(本质就是kernel!RaiseException调用时的现场)。
4)nt!RtlRaiseException函数分析分析
上面我们经过分析ntdll!RtlRaiseException,发现其调用ZwRaiseException函数进内核,其对应nt!NtRaiseException函数,其分析如下图
该函数如下所示,其中值得注意的一点是:KThread.TrapFrame保存着TrapFrame.ebx,进零环时,mov edx,esp,其esp保存着call的返回地址,即ebx保存着三环栈顶,也是三环的返回地址。
这是这里一个比较重要的细节,你是一定要明确的。
5)nt!KiRaiseException函数分析
我们之前在三环生成了ExceptionRecord与Context,但是我们要在零环使用,其如何使用的呢?
分析了这个函数你就会明白,其在nt!KiRaiseException函数中调用完成的两者的复制,之后将Context转换为TrapFrame其KiDispatchException要使用。
之前我们存在一个疑问,为什么不能使用三环进零环的一个TrapFrame而非得从三环拿过来一个Context转换?
猜想:因为该TrapFrame必须是异常现场的TrapFrame,而CPU中断直接调用IDT异常表很容易保存,但是用户模拟的必须是从三环到零环的,其TrapFrame是关于系统调用的。
因此必须调用ntdll!RtlRaiseException三环保存的Context,这是直接记录异常现场的,这样你就能很好理解其中的逻辑。
备注:在调用KiDispatchException上方,其存在一句代码:ExceptionCode &= EFu,其表示将用户模拟异常的位置0,因此用户模拟异常最高位不可能为0,CPU异常,比如c0000094,区分。
4. 总结
到此为止,我们分析过上面两种异常的收集流程,其最终都会流向nt!KiDispatchException函数,下面一张图简单汇总,如果看详情,直接回去看有关函数,都说的很详细。
我们要知道异常收集的是什么-ExceptionRecord以及TrapFrame,怎么收集的?按上面来分析即可。
5.通过异常来进行三环与零环通信的思路
对于异常,我们存在一种思路,即通过除零异常触发,然后hook除零异常,接收到消息,然后读取全局变量或者节区。
我们将数据存储在全局变量或者存放在一个专门的PE节区中,我们在Hook的除零异常代码中读取,然后执行,这样就很好理解了。
之后我们会通过这个猜想来实现我们的代码。
两种异常(CPU异常、用户模拟异常)的收集的更多相关文章
- [ACM_模拟] POJ1068 Parencodings (两种括号编码转化 规律 模拟)
Description Let S = s1 s2...s2n be a well-formed string of parentheses. S can be encoded in two diff ...
- 创建线程的两种方式比较Thread VS Runnable
1.首先来说说创建线程的两种方式 一种方式是继承Thread类,并重写run()方法 public class MyThread extends Thread{ @Override public vo ...
- SSH两种验证方式原理
本帖转自 http://www.cnblogs.com/hukey/p/6248468.html SSH验证方式有两种,分别为用户密码认证以及密钥认证. 1.用户密码认证方式 说明: (1) 当客户端 ...
- java的两种异常runtimeException和checkedException
java异常处理机制主要依赖于try,catch,finally,throw,throws五个关键字. try 关键字后紧跟一个花括号括起来的代码块,简称try块.同理:下面的也被称为相应的块. ...
- java程序中抛出异常的两种方式,及异常抛出的顺序
在java中,会经常遇到异常,java提供了两种抛出异常的方式. 方式一: throws ,抛出具体代码中的异常,这种方式编译器都会提示,举例: public static void main(Str ...
- Java语言中两种异常的差别
Java提供了两类主要的异常:runtime exception和checked exception.所有的checked exception是从java.lang.Exception类衍生出来的,而 ...
- Java异常处理机制及两种异常的区别
java异常处理机制主要依赖于try,catch,finally,throw,throws五个关键字. try 关键字后紧跟一个花括号括起来的代码块,简称try块.同理:下面的也被称为相应的块. ...
- 浅析Java语言中两种异常的差别
Java提供了两类主要的异常:runtime exception和checked exception.所有的checked exception是从java.lang.Exception类衍生出来的,而 ...
- Java中的两种异常类型是什么?他们有什么区别?
一.Throwable是所有异常的根,java.lang.Throwable Error是错误,java.lang.Error Exception是异常,java.lang.Exception 二.E ...
随机推荐
- 【笔记3-31】Python语言基础-元组tuple
创建元组 my_tuple = () my_tuple1 = 1, 2, 3, 4, 5, 6 元组解包 与元组元素数量一致 a,s,d,f,g,h = my_tuple1 a, b, c, *f = ...
- 学习GAN必须阅读的10篇论文
本文转载自:魔图互联.欢迎访问网站查看详细教程:Tensorflow(pytorch)系列教程 生成对抗网络是深度学习中最有趣和最受欢迎的应用之一.本文将列出 10 篇关于 GAN 的论文,这些论文详 ...
- 一文看懂神经网络初始化!吴恩达Deeplearning.ai最新干货
[导读]神经网络的初始化是训练流程的重要基础环节,会对模型的性能.收敛性.收敛速度等产生重要的影响.本文是deeplearning.ai的一篇技术博客,文章指出,对初始化值的大小选取不当, 可能造成 ...
- Centos7部署k8s[v1.16]高可用[keepalived]集群
实验目的 一般情况下,k8s集群中只有一台master和多台node,当master故障时,引发的事故后果可想而知. 故本文目的在于体现集群的高可用,即当集群中的一台master宕机后,k8s集群通过 ...
- GitHub 运用实战入门,奶妈级教学
## 前言: 我不会用*官方*的语言告诉你Git 是什么,对此我表示深深得歉意--在我看来像CSDN.博客园.掘金等博客交流平台就是小的“GitHub”,只不过在这里更多的是一些零零散散的笔记或者文章 ...
- Python常用模块之模块的使用
一 模块介绍 1.什么是模块? #常见的场景:一个模块就是一个包含了一组功能的python文件,比如spam.py,模块名为spam,可以通过import spam使用. #在python中,模块 ...
- Javascript-什么是递归?
递归? 程序调用自身的编程技巧就称之为递归(recursion),就是再运行的过程中调用自己,本质上就是循环. 构成递归的条件有: Ⅰ.不能无限制的调用本身,必须有一个出口,化为简单的状况处理(非递归 ...
- 一款基于SVM算法的分布式法律助手
一. 项目简介 与 使用说明 体验网站(适配手机端): http://www.zhuchangwu.com 项目基于 Spring Cloud .Vue 构建,平台针对需要维权的用户而设计,主要提供如 ...
- Redis 哨兵模式(Sentinel)
上一篇我们介绍了 redis 主从节点之间的数据同步复制技术,通过一次全量复制和不间断的命令传播,可以达到主从节点数据同步备份的效果,一旦主节点宕机,我们可以选择一个工作正常的 slave 成为新的主 ...
- Linux 定时实行一次任务命令
当我们想在指定的时间自动执行 一次 任务的时候,可以使用at命令 启动服务 使用时首先检查atq的服务是否启动 service atd status # 检查atd的状态 service atd st ...