windows异常

异常处理


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;
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容