2020.05.11-2020.05.17

Windows下的异常处理

操作系统处理程序错误或异常的一系列流程和技术的总称。主要有两种异常处理技术,SEH(结构化异常处理)和VEH(向量化异常处理)

基本概念

中断是由外部硬件设备或异步事件产生的,异常是由内部事件产生的,可分为故障、陷阱、终止 3类。前两个是可恢复的,第三个是不可恢复的,系统必须重启

异常列表

YfskvR.png
由cpu引发的称为硬件异常,如访问一个无效的地址;由操作系统或程序引发的称为软件异常
代码中可以用RaiseException()主动引发一个软件异常

异常处理的基本过程

中断或异常发生时,cpu会通过中断描述符表(IDT)寻找处理函数。IDT是硬件和软件交接中断和异常的关口

IDT

IDT是一张位于物理内存中的线性表,共有256项,32位下每个项的长度是8字节,64位下每个项的长度是64字节。
IDT的位置和长度是由cpu的IDTR寄存器描述的,IDTR共有84位,高32位为基址,低16位是表度长度。可以使用SIDT和LIDT指令读写该寄存器,LIDT只能在ring0下运行
IDT的每一项都是一个门结构,包括:

  1. 任务门描述符,用于cpu的任务切换
  2. 中断门,用于描述中断处理程序的入口
  3. 陷阱门,用于描述异常处理程序的入口

用windbg的本地内核调试模式可以观察IDT

1
lkd> !idt /a

异常处理的准备工作

YfsVDx.png
当由异常或者中断发生时,cpu会根据中断类型号执行对应的中断处理程序。
各个异常处理函数除了针对本异常的特定处理之外,通常会将异常信息进行封装。
封装的内容中,一部分是异常记录,包含本次异常的信息,包括异常代码(异常产生的原因)、异常标志、异常发生的地址等;另一部分是陷阱帧,它精确的描述了发生异常时的线程状态,该结构与处理器有关,在不同平台上结构不同。
封装后,异常处理函数会调用nt!KiDispatchException函数处理异常,

内核态的异常处理过程

即当PreviousMode为KernelMode时。此时KiDispatchException会进行以下分发异常

  1. 检测系统是否正在被内核调试器调试。如果内核调试器存在,系统就把异常处理的控制权转交给内核调试器。内核调试器取得控制权后会根据用户对异常处理的设置来确定是否要处理异常。如果处理了,那么异常的线程就会回到原来的位置继续执行;如果无法确定是否处理,就发生中断,由用户决定处理
  2. 如果不存在内核调试器,或者1中不处理该异常,系统将调用nt!RtlDispatchException,根据线程注册的结构化异常处理过程来处理
  3. 如果2时没有处理异常,系统会让内核调试器再次取得对异常的处理权
  4. 如果不存在内核调试器或3中调试器仍不处理,系统就认为不能继续运行了,会直接KeBugCheckEX,产生蓝屏错误
    在上述异常处理过程中,只有在某一步中异常没有处理才会进行下一个过程,只要异常被处理了,就会终止整个异常处理过程

用户态异常处理过程

即当PreviousMode为UserMode时。此时KiDispatchException依然会检测内核调试器是否存在,如果存在就会优先把控制权交给内核调试器。多数情况下,内核调试器不处理用户态的异常,nt!KiDispatchException就会进行如下过程:

  1. 如果发生异常的程序正在被调试,那么将异常信息发送给正在调试的用户态调试器;如果没有被调试就跳过这个步骤
  2. 如果没有被调试或调试器未处理异常,那么在栈上放置EXCEPTION_RECORD和CONTEXT两个结构,并将控制权返回用户态ntdll.dll中的KiUserExceptionDispatche函数,由它调用其它函数进行用户态的异常处理,这一部分涉及SEH和VEH两种处理机制。如果没有调试器能附加或调试器处理不了异常,系统就调用ExitProcess函数结束程序
  3. 如果2未能处理该异常,那么会再次回到nt!KiDispatchException,再次把异常信息发送给用户态的调试器,如果没有调试器则直接结束进程
  4. 如果3中调试器仍不处理,则nt!KiDispatchException再次把异常分发给进程的异常端口进行处理。该端口通常由子系统进程csrss.exe进行监听。子系统监听后会显示一个“应用程序错误”对话框。可以单击按钮,将其附加到调试器。如果没有调试器或者处理不了,则程序被终结
  5. 在终结程序之前,系统会再次调用发生异常的线程中的所有异常处理过程,这是线程异常处理过程中清理未释放资源的机会

C++

按照http://c.biancheng.net/cplus/ 的顺序

类和对象

在之前的周报中有记过2020.03.30-2020.04.05,现在忘了一些,再看一看,加深印象。

类可以看作 c 语言中结构体的升级版。
类的成员不但可以是变量,还可以是函数,通过类定义出的变量叫做对象
(也将类的成员变量称为属性,类的成员函数称为方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student{
public:
char *name;
int age;
float score;
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
class student stu1;
stu1.name = "小明";
stu1.age = 15;
stu1.score = 92.5f;
stu1.say();

先用关键字class定义了一个student,用这个类创建了一个对象stu1,后面这个过程叫做类的实例化,所以也称对象是类的一个实例

在面向对象编程中,类由一组相关联的函数和变量组成,可以将一个或多个类放在一个源文件中,在使用时引入对应的类,调用需要的函数。