中断与异常

中断与异常
- 中断与异常
  • 中断:主要由外部硬件产生,被操作主动通知CPU,产生中断,异步事件,可以不处理
    • 外部硬件与CPU通过PIC(pragmmtable interrupt controller)进行通信(共16根,主IRQ(0-7),从(IRQ2引出))
    • 当外部硬件产生中断,CPU会从PIC读取两个字节(0xcd 0x中断编号)
  • 异常:CPU(程序)内部产生的中断称为异常,同步事件,必须处理
  • 相同:都会产生一个中断号,就是IDT(interrupt descriptor table)的索引号,保存了处理该中断的函数地址
- IDT
  • 中断与异常是由IDT统一管理的,为每一个中断和异常都提供了处理函数:陷阱处理器,操作系统在启动时就会设置IDT表
  • IDT表是位于物理内存的线性表,共256项,32位模式下占2048b,IDT表和其中的函数都由操作系统提供
  1. 0x00-0x14 存放各个异常的陷阱处理器
  2. 0x14-0x20 保留
  3. 0x20-0x29 空白
  4. 0x2A-0x2E windwos自身使用五个
  5. 0x30-0xFF 硬件中断使用
- 异常
  • 指令(机器码opcode)会被细分为若干条微指令,一条机器指令对应一个微程序,所完成的操作由微指令进行解释和执行,如果微指令出错,就需要中断机制,转移执行流

  • 异常的分类:

    1. 错误类异常:通常可以恢复执行,产生异常会保留线程环境,EIP保留的是产生异常的指令地址(除0,内存缺页:执行页交换)
    2. 陷阱类异常:通常可以恢复执行,因为异常产生时错误指令已执行完毕,EIP保留的是产生异常的指令的下一条指令地址(int 3)
    3. 中止类异常:代表严重的错误,一般无法恢复,如程序开始出错执行一段时间才发现,或者异常处理时再次产生异常
- 异常处理机制
  • 异常机制让计算更好的处理程序运行时产生的错误,将错误处理与程序逻辑分开,提供开发效率,统一管理可能出现的异常
- SEH:结构化异常处理
  • structured exception handing

  • 作用域:块

  • 提供四个关键字

    1. __try:被保护的代码块(会产生异常的代码)
    2. __excpte:异常处理器,产生异常执行
    3. __finally:终结处理器(无论以何种方式退出都会被执行)(可以处理错误,释放资源,继续执行或结束)
      1. 正常方式:代码流执行完或执行了__leave
      2. 非正常方式:产生异常,return,goto,break,continue等控制执行流语句离开了保护代码块
    4. __leave:正常离开
  • __try{
        
    }
    __finally{
        
    }
    
    	// 使用 AbnormalTermination 判断 __try 的退出方式
        // 正常退出,返回值是 false
    
  • __try{
        
    }
    __excpet(/*过滤表达式,表达式,值*/){
        //异常处理快
    }
        // 过滤表达式的值():
    	//EXCEPTION_EXECUTE_HANDLER      1  : 执行处理块内容,不会回到原来位置
    	//EXCEPTION_CONTINUE_SEARCH      0  : 异常处理不了了,去找其它的处理
    	//EXCEPTION_CONTINUE_EXECUTION (-1) : 不相信,回到异常位置继续执行
    

    过滤表达式的值:

    1. //EXCEPTION_EXECUTE_HANDLER 1 : 执行处理块内容,不会回到原来位置

      1. //EXCEPTION_CONTINUE_SEARCH 0 : 异常处理不了了,找下一个异常处理
      2. //EXCEPTION_CONTINUE_EXECUTION (-1) : 回到异常位置继续执行
  • 两个异常处理关键函数

  • // 在异常处理过程中提供两个函数
    //GetExceptionCode(); 获取异常代码
    //GetExceptionInformation();获取异常信息
    	
       //异常信息
        	typedef struct _EXCEPTION_POINTERS {
    		PEXCEPTION_RECORD ExceptionRecord;
    		PCONTEXT ContextRecord;
    	} EXCEPTION_POINTERS, * PEXCEPTION_POINTERS;
    
    	// 异常信息结构体
    	//typedef struct   {
    	//	DWORD    ExceptionCode;			 // 异常错误码
    	//	DWORD ExceptionFlags;			 // 异常标志
    	//	struct _EXCEPTION_RECORD* ExceptionRecord; //异常信息
    	//	PVOID ExceptionAddress;			// 异常错误地址
    	//	DWORD NumberParameters;			// 附加参数(内存错误时有效)
    	//	ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
    	//} EXCEPTION_RECORD;
    
- VEH:向量化异常处理
  • 作用范围:全局,一经注册,全进程有效

  • 需要提供回调函数,使用AddVectorExceptionHandler()注册

    //注册vch回调函数原型
    PVOID
    WINAPI
    AddVectoredExceptionHandler(
        _In_ ULONG First,//插入位置 true从头插入
        _In_ PVECTORED_EXCEPTION_HANDLER Handler//回调函数
        );
    
    //回调函数原型
    typedef LONG (NTAPI *PVECTORED_EXCEPTION_HANDLER)(
        struct _EXCEPTION_POINTERS *ExceptionInfo
        );
    //只有两种返回值
    
      1. //EXCEPTION_CONTINUE_SEARCH           0  : 异常处理不了了,找下一个异常处理
      2. //EXCEPTION_CONTINUE_EXECUTION  (-1) : 回到异常位置继续执行
    
- VCH:向量化异常处理
  • 作用范围:全局,一经注册,全进程有效
  • 需要提供回调函数,使用AddVectorContinueHandler()注册
  • 当异常被处理了执行VCH
//注册函数原型
PVOID
WINAPI
AddVectoredContinueHandler(
    _In_ ULONG First,
    _In_ PVECTORED_EXCEPTION_HANDLER Handler
    );
- UCH:默认的ECH
  • 本质上属于SEH,能够添加一个默认的SEH异常处理函数
  • 只有在没有调试器的情况下执行(反调试)
  • 全局变量,进程有效
//注册函数原型
WINAPI
SetUnhandledExceptionFilter(
    _In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
    );
//回调函数原型
typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(
    _In_ struct _EXCEPTION_POINTERS *ExceptionInfo
    );
- 异常处理执行顺序
  • 总结:VEH -> SEH -> UEH -> VCH

  • 没有调试器条件下

  1. VEH 可以处理 - 不会传递给SEH - 会调用VCH
  2. VEH 不能处理,SEH能执行且不是从except处理块开始执行(从返回异常开始处执行,返回之前调用VCH) - 调用VCH
  3. VEH 不能处理,所有的SEH不能处理,会调用默认的UEH - 调用VCH
  4. VEH,SEH, UEH都不能处理,VCH不会调用
  • 有调试器条件下
  1. VEH 处理了-》会调用VCH
  2. VEH 没有处理,SEH 处理了且返回异常处,没有处理(return之后调用) -》 都调用VCH
  3. VEH 没有处理,SEH 处理了/没有处理 -》 都调用VCH
- VEH与SEH不同

// SEH 是线程相关的处理程序

// VEH 是进程有效的,VEH 链表被保存在一个全局的地址上

- 异常表达式信息

// - GetExceptionCode() 只能在过滤表达式和异常处理块调用

// - GetExceptionInformation() 只能在过滤表达式中使用

  1. SEH
  • static unsigned int nStep = 1;
    
    void Fun_B() {
        int x, y = 0;
        __try { x = 5 / y; } // 引发异常
        __finally { printf("Step %d :执行Fun_B的finally块的内容\n", nStep++); }
    }
    void Fun_A() {
        __try { Fun_B(); }
        __finally { printf("Step %d :执行Fun_A的finally块的内容\n", nStep++); }
    }
    long MyExcepteFilter() {
        printf("Step %d :执行main的异常过滤器\n", nStep++);
        return EXCEPTION_EXECUTE_HANDLER;
    }
    
    
    int main() {
    
        __try { Fun_A(); }
        __except (MyExcepteFilter()) {
            printf("Step %d :执行main的except块的内容\n", nStep++);
        }
        system("pause");
        return 0;
    }
    

    运行结果:

    Step 1 :执行main的异常过滤器
    Step 2 :执行Fun_B的finally块的内容
    Step 3 :执行Fun_A的finally块的内容
    Step 4 :执行main的except块的内容

  1. VEH->SEH->UEH->VCH
  • int a = 0;
    LONG NTAPI  VEH_handler1(
    	struct _EXCEPTION_POINTERS* ExceptionInfo
    )
    { 
    	printf("VEH 1 执行 \n");
    	a = 100;
    	//return EXCEPTION_CONTINUE_EXECUTION; //处理了
    	return EXCEPTION_CONTINUE_SEARCH;		// 0处理不了,找其VEH处理
    }
    
    LONG NTAPI  VCH_handler1(
    	struct _EXCEPTION_POINTERS* ExceptionInfo
    )
    {
    	printf("VCH 1 执行 \n");
    	a = 100;
    	return EXCEPTION_CONTINUE_EXECUTION;		// 0处理不了,找其VEH处理
    }
    
    LONG NTAPI  SEH_handler1(
    	struct _EXCEPTION_POINTERS* ExceptionInfo
    )
    {
    	printf("SEH 1 执行 \n");
    	a = 100;
    	//return EXCEPTION_CONTINUE_EXECUTION;
    	return EXCEPTION_CONTINUE_SEARCH;		// 0处理不了,找其VEH处理
    }
    
    LONG NTAPI  UEH_handler1(
    	struct _EXCEPTION_POINTERS* ExceptionInfo
    )
    {
    	printf("UEH 1 执行 \n");
    	a = 100;
    	//return EXCEPTION_CONTINUE_SEARCH;
    	return EXCEPTION_CONTINUE_EXECUTION;		// 0处理不了,找其VEH处理
    }
    
    
    int main()
    {
    	AddVectoredExceptionHandler(TRUE, VEH_handler1);
    	AddVectoredContinueHandler(TRUE, VCH_handler1);
    	SetUnhandledExceptionFilter(UEH_handler1);
    
    
    	__try {
    
    		int c = 10 / a;
    		printf("c = %d", c);
    
    	}
    	__except (SEH_handler1(GetExceptionInformation()))
    	{
    		printf("SEH处理块\n");
    
    	}
    
    	getchar();
    
    }
    

异常机制,就是为了让计算机能够更好的处理程序运行期间产生的错误,从编程的角度来看,能够将错误的处理与程序的逻辑分隔开。使得我们可以集中精力开发关键功能,而把程序可能出现的异常统一管理。Windows提供了异常处理的机制,使得你有机会挽救自己即将崩溃的程序,大体上来说它提供了以下处理异常的机制:

  • SEH-结构化异常处理
  • VEH-向量化异常处理
  • VCH-向量化异常处理

结构化异常处理

Structed Exception Handler(结构化异常处理)简称SEH,是微软提供的一种处理异常的机制。在VC++中,通过提供四个微软关键字使得程序员能够良好的使用这一机制,分别是:__try、 __finally、 __except、 __leave接下来简要说明一下用法。

>>>>终结处理器

由 __try、 __finally 和 __leave构成。能够保证无论 __try 块中的指令以何种方式退出,都必然会执行 __finally 块。[不会进行异常处理,只进行清理操作]SEH 的使用范围是线程相关的,每个线程都有自己的函数(SEH链表是局部链表,在堆栈中)

__try
{
    // 被检查的代码块,通常是程序的逻辑部分
    printf("__try { ... }\n");
 
    // 使用 __leave 跳出当前的 __try
    __leave;
}
__finally
{
    // 终结处理块,通常用于清理当前程序
    // 无论 __try 以何种方式退出,都会执行这里的指令
    printf("__finally { ... }\n");
 
    // 使用 AbnormalTermination 判断 __try 的退出方式
    // 正常退出,返回值是 false
    if (AbnormalTermination())
        printf("异常退出\n");
    else
        printf("正常退出\n");
}

使用 goto 退出(return 、 break 同理):

__try
{
    printf("__try { ... }\n");
    goto tag;
}
__finally
{
    printf("__finally { ... }\n");
    if (AbnormalTermination())
        printf("异常退出\n");
    else
        printf("正常退出\n");
}
tag:
return 0;

>>>>异常处理器

由关键字 __try 、 __except 构成,能够保证 __try 中如果产生了异常,会执行过滤表达式中的内容,应该在过滤表达式提供的过滤函数中处理想要处理的异常

  • EXCEPTION_EXECUTE_HANDLER(1):表示该异常被处理,从异常处下一条指令继续执行
  • EXCEPTION_CONTINUE_SEARCH(0):表示异常不能被处理,交给下一个SEH
  • EXCEPTION_CONTINUE_EXECUTION(-1):表示异常被忽略,从异常处继续执行
// 异常处理器: 由关键字 __try 和 __except 构成
// 如果 __try 中产生了异常,会执行过滤表达式中的内容
// 应该在过滤表达式提供的过滤函数中处理想要处理的异常
 
// 异常过滤表达式中最常见的情况就是编写一个异常过滤函数,对异常进行处理
DWORD ExceptionFilter(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo)
{
    printf("ExceptionCode: %X\n", ExceptionCode);
 
    // 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常
    if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        // 在这里对寄存器执行的所有修改都会直接被应用到程序中
        ExceptionInfo->ContextRecord->Eax = 1;
        ExceptionInfo->ContextRecord->Ecx = 1;
 
        // 如果异常被处理了,那么就返回重新执行当前的代码
        return EXCEPTION_CONTINUE_EXECUTION;
    }
 
    // 如果不是自己能够处理的异常,就不处理只报告
    return EXCEPTION_EXECUTE_HANDLER;
}
 
int main()
{
    int number = 0;
 
    __try
    {
        // __try 中的是可能产生异常的代码
        // idiv eax, ecx
        number /= 0;
    }
 
    // 通常会为异常过滤表达式提供一个异常处理函数用于处理异常,并返回处理结果
    // GetExceptionCode: 用于获取异常的类型,能在过滤表达式和异常处理器中使用
    // GetExceptionInformation: 用于获取异常的信息,只能写在过滤表达式中
 
    // 异常过滤表达式
    __except (ExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
    {
        // 异常处理器,只有 __except 返回 EXCEPTION_EXECUTE_HANDLER 才会执行
        printf("__try 中产生了异常,但是并没有处理异常 %X\n", GetExceptionCode());
    }
 
    printf("numebr = %d\n", number);
 
    return 0;
}

顶层异常处理器

TopLevelEH 全称顶层异常处理器(UEF),这个函数只能有一个,被保存在全局变量中。由于只会被系统默认的最底层 SEH 调用,所以又会被称作是 SEH 的一种,是整个异常处理的最后一环。所以通常都不会再此执行异常处理操作,而是进行内存 dump ,将消息发送给服务器,进行异常分析。在win7 之后,只有在非调试模式下才会被调用,可以用来反调试。

LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
    printf("ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
 
    // 如果当前的异常是除零异常,那么就通过修改寄存器处理异常
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        ExceptionInfo->ContextRecord->Eax = 1;
        ExceptionInfo->ContextRecord->Ecx = 1;
 
        // 异常如果被处理了,那么就返回重新执行当前的代码
        return EXCEPTION_CONTINUE_EXECUTION;
    }
 
    // 如果不是自己能够处理的异常,就不处理只报告
    return EXCEPTION_EXECUTE_HANDLER;
}
 
 
int main()
{
    int number = 0;
 
    // 通过一个函数可以直接的安装 UEF
    SetUnhandledExceptionFilter(TopLevelExceptionFilter);
 
    __try
    {
        number /= 0;
    }
    // 异常一旦被 SEH 处理,就不会再传递给 UEF
    __except (EXCEPTION_CONTINUE_SEARCH)
    {
        printf("不会被执行\n");
    }
 
    printf("number = %d\n", number);
 
    system("pause");
    return 0;
}

直接运行exe结果:

向量化异常处理

>>>>向量异常VEH

Vectored Exception Handler 向量化异常处理的一种,被保存在一个全局的链表中,进程内的所有线程都可以使用这个函数,是第一个处理异常的函数。

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
    printf("ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
 
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        ExceptionInfo->ContextRecord->Eax = 1;
        ExceptionInfo->ContextRecord->Ecx = 1;
 
        return EXCEPTION_CONTINUE_EXECUTION;
    }
 
    return EXCEPTION_EXECUTE_HANDLER;
}
 
int main()
{
    int number = 0;
 
    // 通过一个API可以直接安装VEH
    // 参数一是布尔值,如果为 TRUE,就将当前的函数添加到全局 VEH 函数的链表头部
    // 否则则为尾部
    AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
 
    __try
    {
        number /= 0;
    }
    // 异常首先被 VEH 接收到,如果无法处理才会传递给 SEH
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("永远不会被执行\n");
    }
 
    printf("number = %d\n", number);
 
    system("pause");
    return 0;
}

>>>>向量化异常处理VCH

VCH:和 VEH 类似,但是只会在异常被处理的情况下最后调用。

异常的传递过程

LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
    printf("VEH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
 
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        ExceptionInfo->ContextRecord->Eax = 1;
        ExceptionInfo->ContextRecord->Ecx = 1;
 
        return EXCEPTION_CONTINUE_SEARCH;
    }
 
    return EXCEPTION_EXECUTE_HANDLER;
}
 
DWORD StructedExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
    printf("SEH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
 
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        ExceptionInfo->ContextRecord->Eax++;
        ExceptionInfo->ContextRecord->Ecx = 1;
 
        return EXCEPTION_CONTINUE_SEARCH;
    }
 
    return EXCEPTION_EXECUTE_HANDLER;
}
 
LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
    printf("UEF: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
 
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        ExceptionInfo->ContextRecord->Eax++;
        ExceptionInfo->ContextRecord->Ecx = 1;
 
        return EXCEPTION_CONTINUE_EXECUTION;
    }
 
    return EXCEPTION_EXECUTE_HANDLER;
}
 
LONG WINAPI VectoredContinueHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
    // VCH 不会对异常进行处理,调用的时机和异常处理的情况有关
    printf("VCH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
 
    return EXCEPTION_CONTINUE_SEARCH;
}
 
int main()
{
    int number = 0;
 
    AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
    AddVectoredContinueHandler(TRUE, VectoredContinueHandler);
    SetUnhandledExceptionFilter(TopLevelExceptionFilter);
 
    __try
    {
        number /= 0;
    }
    __except (StructedExceptionFilter(GetExceptionInformation()))
    {
        printf("SEH: 异常处理器\n");
    }
 
    printf("number = %d\n", number);
 
    system("pause");
    return 0;
}

直接运行exe结果:

可以得出,异常的传递过程:VEH -> SEH -> UEH -> VCH

探究SEH

// 带有异常处理函数的函数
void test1()
{
    // 在 VS 的同一个函数中无论编写了多少个 SEH, 编译器
    // 实际上只会安装一个叫做 except_handler4 的函数
    __try
    {
        printf("__try { ... }\n");
        __try
        {
            printf("__try { ... }\n");
        }
        __except (1)
        {
            printf("__except (1) { ... }\n");
        }
    }
    __except (1)
    {
        printf("__except (1) { ... }\n");
    }
}
 
// 没有异常处理函数的函数
void test2() { }
 
// 遍历当前程序中已经存在的异常处理函数
void ShowSEH()
{
    // 定义一个结构体指针,用于保存 SEH 链表的头节点
    PEXCEPTION_REGISTRATION_RECORD header = nullptr;
 
    // 通过 FS:[0] 找到 ExceptionList 的头节点
    __asm push fs:[0]
    __asm pop header
 
    // 遍历异常处理链表,链表以 -1 结尾
    while (header != (EXCEPTION_REGISTRATION_RECORD*)-1)
    {
        printf("function: %08X\n", header->Handler);
        header = header->Next;
    }
 
    printf("\n");
}
 
EXCEPTION_DISPOSITION NTAPI ExceptionRoutine(
    // 产生的异常信息
    _Inout_ struct _EXCEPTION_RECORD* ExceptionRecord,
    _In_ PVOID EstablisherFrame,
    // 产生异常时的线程上下文
    _Inout_ struct _CONTEXT* ContextRecord,
    _In_ PVOID DispatcherContext
)
{
    printf("自定义SEH: ExceptionCode: %X\n", ExceptionRecord->ExceptionCode);
 
    if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionRecord->ExceptionCode)
    {
        ContextRecord->Eax = 1;
        ContextRecord->Ecx = 1;
 
        return ExceptionContinueExecution;
    }
 
    return ExceptionContinueSearch;
}
 
int main()
{
    test1();
    test2();
 
    PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
 
    __asm push fs : [0]
    __asm pop ExceptionList
 
    // 遍历异常处理函数
    ShowSEH();
 
    // 手动安装一个异常处理函数,操作 FS:[0]
    __asm push ExceptionRoutine
    __asm push fs : [0]
    __asm mov fs : [0], esp
 
    ShowSEH();
 
    int number = 0;
    number /= 0;
 
    printf("\n");
 
    __asm mov eax, ExceptionList
    __asm mov fs : [0], eax
    __asm add esp, 0x08
 
    ShowSEH();
 
    return 0;
}

编译器只生成一个 except_handler4 函数:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值