美文网首页
windows异常

windows异常

作者: MagicalGuy | 来源:发表于2018-10-10 00:48 被阅读0次

    异常处理


    image.png

    异常和中断是由CPU触发的.
    操作系统怎么接收到异常的?
    IDT表, 操作系统在启动时,就会将中断处理的地址存入到IDT中.
    当产生中断的时候,CPU(硬件)就会调用IDT中的函数(软件).
    Windows的异常处理机制, 都是由Windows操作系统提供.

    1. SEH
    2. VEH

    SEH的原理

    1. SEH的异常处理函数是怎么被调用?
      产生异常后 , 操作系统使用fs段寄存器找到TEB, 通过TEB.ExceptionList 找到SEH链表的头节点, 通过节点中记录的异常处理函数的地址调用该函数.

    2. 异常过滤函数是怎么被调用的?
      由编译器提供的异常处理函数(except_handler4)内部所调用的,
      except_handler4这个函数被充当为注册SEH节点时的异常处理函数.
      系统调用的是except_handler4函数,except_handler4 调用我们在except块的异常过滤表达式中给出的异常过滤函数.

    3. except语句块怎么被执行的?
      也是由except_handler4函数调用的.

    4. seh是怎么找到上一层的异常处理块的.
      通过节点的Next找到下一个节点, 然后找到节点的异常出来函数.

    =====================

    1. 异常是在哪里产生的?
      1.1 CPU外部硬件。
      1.2 CPU内部中断(中断指令)
    1. SEH异常处理函数是被谁调用的?异常过滤函数呢?
      异常处理函数 :保存在SEH节点中的,被操作系统调用。是系统的原生SEH异常处理。
      异常过滤函数 : 保存在编译器所提供异常机制的某个结构中(《软件调试》),最终被exceptHandler4所调用。是对编译器在原生SEH异常处理机制的封装。

    2. SEH节点保存在内存中的什么位置?
      保存在栈中, 一般和函数是相关, 一般在进入到函数的时候,SEH的节点被安装在栈中,在离开函数之后就会删除节点。

    1. 从哪可以找到SEH的头节点?
      TEB.TIB.ExceptionList,也就是FS:[0]
    1. 异常是怎么从CPU转发到操作系统中
      1.1 CPU的处理:
      1.1.1 当发生异常的时候,CPU根据异常号调用IDT表中对应的异常处理程序。
      1.2 操作系统的处理:
      1.2.1 将各种异常处理函数填充到IDT
      1.2.2 等待IDT的异常处理函数被调用
      1.2.2.1 KiTrap03 (IDT中3号处理函数),相当于异常的源头。
      base\ntos\ke\i386\trap.asm
      1.2.2.2 函数的功能:开辟栈空间保存了产生异常时的线程上下文。通过寄存器传参,调用CommonDispatchException函数。
      主要传递了:通过寄存器传递的有:异常地址,异常代码,异常附加参数。通过栈传递有:线程的上下文。
      1.2.3 CommonDispatchException
      1.2.3.1 函数的功能:
      1.2.3.1.1 开辟一个栈空间将异常记录信息保存到栈中。
      1.2.3.1.2 获取异常发生的模式(用户层/内核层)
      1.2.3.1.3 KiDispatchException
      参数1: 异常记录结构体的地址
      参数2: NULL
      参数3:异常记录帧
      参数4: 异常发生的模式
      参数5:是否是第一次处理异常(通过KiTrapXX系列函数处理的异常都是第一次)
      1.2.4 KiDispatchException
      base\ntos\ke\i386\exceptn.c
      1.2.4.1 功能:
      1.2.4.1.1 内核模式的处理:
      1.2.4.1.1.1 将异常交给内核调试器处理
      1.2.4.1.1.2 内核调试器处理不了才交给异常处理机制处理
      1.2.4.1.1.3 异常处理机制也处理不了,则进入到第二次异常分发:
      再将异常交给调试器处理
      1.2.4.1.1.4 还处理不了,就KeBugCheckEx
      1.2.4.2 用户模式异常的处理
      1.2.4.2.1 通过发送消息,将调试记录发送到用户层的调试器,并等待调试器的处理结果。
      1.2.4.2.2 如果调试器处理不了, 则轮到异常处理机制处理。
      1.2.4.2.2.1 将内核栈的数据拷贝到用户栈,并将esp指向拷贝后数据的首地址
      1.2.4.2.2.2 将eip设置到ntdll!KiUserExceptionDispatcher函数中。这样当执行流从内核回到用户层的时候, esp指向了EXCEPTION_POINTERS的变量的地址, eip指向了用户层的异常分发函数的地址。

    1.2.5 用户层的异常分发(用户层的SHE,VEH的调用过程):
    1.2.5.1 遍历VEH,并调用异常处理函数
    1.2.5.2 遍历SEH,并调用异常处理函数

    1. 用户异常处理程序怎么接收到的异常
    2. 分析操作系统处理异常的源码
      3.1 系统异常处理的步骤
      3.2 系统对调试机制的支持
      3.2.1 反调试
    3. 反调试和反反调试

    =====================

    // 01_try_finally.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        __try{
            printf("try块\n");
            //return 0;
            *(int*)0 = 0;
            __leave; // 以正常方式离开try的关键字
        }
        __finally{
            printf("finally块\n");
        }
        printf("main\n");
        return 0;
    }
    

    ====================

    // 02_try_except.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    int* g_pNum = NULL;
    
    
    void fun()
    {
        __try{
            *(int*)0 = 0;
        }
        __except (EXCEPTION_CONTINUE_SEARCH){
    
        }
    }
    
    // 异常过滤函数, 用于处理程序中出现的异常.
    int seh(EXCEPTION_POINTERS* pExce)
    {
        
        printf("在%08X处产生了%08X异常\n",
            pExce->ExceptionRecord->ExceptionAddress,
            pExce->ExceptionRecord->ExceptionCode);
        printf("EAX:%08X ECX:%08X\n",
            pExce->ContextRecord->Eax,
            pExce->ContextRecord->Ecx);
    
        pExce->ContextRecord->Eax = (DWORD)new int;
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        //执行处理程序(except块)
        EXCEPTION_EXECUTE_HANDLER;
    
        //继续搜索
        // 将异常传递到上一层的try和except,将异常交给它执行
        EXCEPTION_CONTINUE_SEARCH;
        
        //继续执行
        // 继续执行产生异常的那条指令
        EXCEPTION_CONTINUE_EXECUTION;
    
        __try{
            fun();
            //*(int*)0 = 0;
            *g_pNum = 10;
            printf("try块\n");
        }
        __except ( seh(GetExceptionInformation())){
            printf("finally块\n");
        }
    
    
        printf("main()\n");
        __try{
            *(int*)0 = 0;
            printf("try块\n");
        }
        __except (EXCEPTION_CONTINUE_EXECUTION){
            printf("finally块\n");
        }
    
        return 0;
    }
    

    =============================

    // 03_veh.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    
    LONG WINAPI veh(EXCEPTION_POINTERS* pExce)
    {
        printf("veh\n");
        // 继续执行, 说明异常已被处理,产生异常的指令将会
        // 被继续执行
        EXCEPTION_CONTINUE_EXECUTION;
        // 让下一个veh节点处理异常.
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    LONG WINAPI seh(EXCEPTION_POINTERS* pExce){
        printf("seh\n");
        // 让下一个veh节点处理异常.
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        //1. 将异常处理函数注册到系统
        AddVectoredExceptionHandler(TRUE, veh);
        __try{
    
            *(int*)0 = 0;
        }
        __except (seh(GetExceptionInformation())){
    
        }
    
        return 0;
    }
    

    ============================

    // 04_异常 处理的优先级.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    LONG WINAPI vch(EXCEPTION_POINTERS* pExcept){
        printf("vch\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    
    LONG WINAPI veh(EXCEPTION_POINTERS* pExcept){
        printf("veh\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    LONG WINAPI seh(EXCEPTION_POINTERS* pExcept){
        printf("seh\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    
    LONG WINAPI ueh(EXCEPTION_POINTERS* pExcept){
        printf("ueh\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        AddVectoredContinueHandler(TRUE, vch);//vch
        AddVectoredExceptionHandler(TRUE, veh);//veh
        // 在64位系统下, 当程序被调试时,UEH不会被调用
        // 不被调试才会被调用.
        // 在32位系统下,被调试时也会被调用.
        SetUnhandledExceptionFilter(ueh);
        __try{
            *(int*)0 = 0;
        }
        __except (seh(GetExceptionInformation())){
    
        }
        return 0;
    }
    

    ======================

    // 05_手工安装SEH节点.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    EXCEPTION_DISPOSITION NTAPI seh(struct _EXCEPTION_RECORD *ExceptionRecord,PVOID EstablisherFrame,struct _CONTEXT *ContextRecord,PVOID DispatcherContext)
    {
        printf("seh\n");
        // 继续执行
        return ExceptionContinueExecution;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    //  EXCEPTION_REGISTRATION_RECORD node;
        /*
          * 产生异常后 , 操作系统使用fs段寄存器找到TEB, 
          * 通过TEB.ExceptionList 找到SEH链表的头节点, 
          * 通过节点中记录的异常处理函数的地址调用该函数.
        */
    //  node.Handler = seh;
    //  node.Next = NULL;
    
        _asm
        {
            push seh; // 将SEH异常处理函数的地址入栈
            push fs:[0];//将SEH头节点的地址入栈
            ;// esp + 0 -- > [fs:0]; node.Next;
            ;// esp + 4 -- > [seh]; node.handler;
            mov fs:[0], esp;// fs:[0] = &node;
        }
    
    
        *(int*)0 = 0;
    
    
        // 平衡栈空间
        // 还原FS:[0]原始的头节点
        _asm{
            pop fs : [0]; // 将栈顶的数据(原异常头节点的地址)恢复到FS:[0],然后再平衡4个字节的栈
            add esp, 4; // 平衡剩下的4字节的栈.
        }
        return 0;
    }
    

    =========================

    相关文章

      网友评论

          本文标题:windows异常

          本文链接:https://www.haomeiwen.com/subject/hjhraftx.html