首页 > 代码库 > PE结构、SEH相关知识学习笔记

PE结构、SEH相关知识学习笔记

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题

PE结构的学习

原文中用fasm自己构造了一个pe,这里贴一个用masm的,其实是使用WriteFile API将编写的PE数据写成文件~也没啥好说的,PE结构在这里没有仔细介绍,需要可以另外查询,剩下要说的的基本都在代码注释里了

参考:点击打开链接(PEDIY技术之新思路(二)_用‘高级‘编译器MASM实现自定义PE文件结构)

Pe.asm:

技术分享
REMOTE_CODE_START equ this BYTE  02.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  03.; DOS Header  04.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  05.PE_HEADER_START equ this BYTE  06.DOS_HEADER:  07.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  08.; 事实上,从这里到e_res2无用部分可以用一句DUP(0)来完成  09.; 在这里编译器不会检查你结构的定义是否正确, 这些变量名也是没有意义的  10.; 这样写只是有利于我们认识PE结构而已,除了关键字,你可以任意写  11.; 因为它相当于写二进制型汇编代码, 事实上,和直接写二进制是一样的  12.; 只是这样就轻松多了。当然了, 这种方式的定义给我们带来的问题就是重定位  13.; 所以需要重定位的地方要小心了, 你也可以把PE头部提上来到DOS头部里面来变形PE头  14.; 或者整个最小PE什么的, 随你怎么高兴怎么玩,但是要保证e_lfanew定位的正确  15.; 你想怎么来就怎么来,你自由了!但是,没有绝对的自由,俗话说得好  16.; 任悟空本领再高,他也跳不出如来佛(OS)的手掌心啊  17.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  18.    e_magic             db MZ  19.    e_cplp              dw 0  20.    e_cp                dw 0  21.    e_crlc              dw 0  22.    e_cparhdr           dw 0  23.    e_minalloc          dw 0  24.    e_maxalloc          dw 0  25.    e_ss                dw 0  26.    e_sp                dw 0  27.    e_csum              dw 0  28.    e_ip                dw 0  29.    e_cs                dw 0  30.    e_lfarlc            dw 0  31.    e_ovno              dw 0  32.    e_res               dw 4 dup(0)  33.    e_oemid             dw 0  34.    e_oeminfo           dw 0  35.    e_res2              dw 10 dup(0)  36.    e_lfanew            dd NT_HEADERS - 00401000h  37.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  38.; Dos小程序,写不写都行  39.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  40.Dos_Stub:  41.    mov ah, 4ch  42.    int 21h  43.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  44.; Nt Header  45.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  46.NT_HEADERS:  47.    Signature           dd 4550h    ; ‘PE‘  48.    Machine             dw 14ch  49.    NumberOfSections        dw 2  50.    TimeDateStamp           dd 0  51.    PointerToSymbolTable        dd 0  52.    NumberOfSymbols         dd 0  53.    SizeOfOptionalHeader        dw 0e0h  54.    Characteristics         dw 010fh  55.    Magic               dw 10bh  56.    MajorLinkerVersion      db 0  57.    MinorLinkerVersion      db 0  58.    SizeOfCode          dd 200h  59.    SizeOfInitializedData       dd 0  60.    SizeOfUninitializedData     dd 0  61.    AddressOfEntryPoint     dd 1000h    ; oep  62.    BaseOfCode          dd 1000h    ; Code Section RVA  63.    BaseOfData          dd 0        ; Data Section RVA  64.    ImageBase           dd 00400000h  65.    SectionAlignment        dd 1000h    ; Section Mem Align  66.    FileAlignment           dd 200h     ; Section Disk Align  67.    MajorOperSystemVersion      dw 0  68.    MinorOperSystemVersion      dw 0  69.    MajorImageVersion       dw 0  70.    MinorImageVersion       dw 0  71.    MajorSubsystemVersion       dw 4  72.    MinorSubsystemVersion       dw 0  73.    Win32VersionValue       dd 0        ; Reserved 0  74.    SizeOfImage         dd 3000h    ; PE加载到内存后的映像大小  75.    SizeOfHeaders           dd 200h     ; DosHeaders + DosStub + NtHeader + Section Header  76.    _CheckSum           dd 0  77.    SubSystem           dw 2        ; GUI  78.    DllCharacteristics      dw 0  79.    SizeOfStackReserve      dd 100000h  80.    SizeOfStackCommit       dd 1000h    ; Stack = 4kb  81.    SizeOfHeapReserve       dd 100000h  82.    SizeOfHeapCommit        dd 1000h    ; Heap = 4kb  83.    LoaderFlags         dd 0  84.    NumberOfRvaAndSizes     dd 10h      ; 16  85.    DirectoryData1          dq 0        ; 没有输出表,填0  86.    ImportTableAddress      dd IMPORT_START - 00401000h - 400h + 2000h  87.    ImportTableSize         dd IMPORT_LENGTH  88.    DirectoryData2          dq 14 dup(0)    ; 余下的数据目录填0  89.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  90.; Section 1  91.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  92.SECTION_HEADER1:  93.    Name1               db CODE, 0, 0, 0, 0       ; 节区名,8字节大小  94.    VirtualSize         dd CODE_LENGTH  95.    VirtualAddress          dd 1000h            ; 内存中的偏移  96.    SizeOfRawData           dd 200h  97.    ; 这里的CODE_START - 00401000h = 00401200h - 00401000h  98.    ; 00401000h为Pe.asm, PE头200h,CODE节区在PE头后面,所以为  99.    ; 00401200h,这里这样做可以求出代码段所在文件偏移  100.    PointerToRawData        dd CODE_START - 00401000h   ; 文件中的偏移,也可以直接成为200h,因为定义的PE头大小为200  101.    PointerToRelocations        dd 0  102.    PointerToLinenumbers        dd 0  103.    NumberOfRelocations     dw 0  104.    NumberOfLinenumbers     dw 0  105.    _Characteristics        dd 0e0000020h           ; 节区属性  106.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  107.; Section 2  108.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  109.SECTION_HEADER2:  110.    Name2               db IMPORT, 0, 0       ; 这个块可以去掉,将输入表放入代码块里  111.    VirtualSize2            dd IMPORT_LENGTH  112.    VirtualAddress2         dd 2000h            ; 内存中的偏移  113.    SizeOfRawData2          dd 200h  114.    ; 同上,这里IMPORT_START = 00401400h  115.    ; PE头 + CODE节区 = 400h  116.    PointerToRawData2       dd IMPORT_START - 00401000h ; 文件中的偏移,也可以直接写成400h  117.    PointerToRelocations2       dd 0  118.    PointerToLinenumbers2       dd 0  119.    NumberOfRelocations2        dw 0  120.    NumberOfLinenumbers2        dw 0  121.    _Characteristics2       dd 0e0000020h  122.PE_HEADER_END equ this BYTE  123.PE_HEADER_LENGTH equ offset PE_HEADER_END - offset PE_HEADER_START  124.ZeroSpace1  db 200h - PE_HEADER_LENGTH dup(0)   ; 不足200h, 间隙填0  125.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  126.; Code Start  127.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  128.CODE_START equ this BYTE  129.    mov eax, OFFSET IAT_1  130.    lea eax, [szContextR - 200h]    ; 401200h为code段开始,szContextR = 401220h, 这是在PE.ASM包含在MakePe.asm时的情况  131.    lea ebx, [szCaptionR - 200h]    ; 如果要单独作为程序,其起始地址00401000h,要减去200h  132.    push MB_OK  133.    push ebx  134.    push eax  135.    push 0  136.    ; IAT_1(0040143ch) - 1000h为不在CODE段 = (0040043ch)  137.    ; 0040043ch - 400h = 0040003ch这样就等于ImageBase + 在导入表(400h)中的偏移(文件偏移)  138.    ; 再加上2000h = 0040203ch为实际内存地址  139.    call dword ptr [IAT_1 - 1000h - 400h + 2000h]  140.    push 0  141.    call dword ptr [IAT_2 - 1000h - 400h + 2000h]  142.    szContextR  db Congratulations! You make it!, 0dh, 0ah, 0  143.    szCaptionR  db OK, 0  144.CODE_END equ this BYTE  145.CODE_LENGTH equ offset CODE_END - offset CODE_START  146.ZeroSpace2  db 200h - CODE_LENGTH dup(0)  147.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  148.; Import Start  149.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  150.IMPORT_START equ this BYTE  151.IID_1:  152.    ; IAT_1 - 401000h = 获得IAT_1的文件偏移, 再减400h为获得在导入表中的偏移(文件中)  153.    ; 加2000h获得在内存中的偏移(RVA), 下面的一些和这个一个道理  154.    OriginalFirstThunk      dd IAT_1 - 401000h - 400h + 2000h  155.    TimeDateStemp           dd 0  156.    ForwarderChain          dd 0  157.    DllName             dd DllName1 - 401000h - 400h + 2000h  158.    ; 这里为了方便,把它和OriginalFirstThunk指向同一个地址的IAT结构  159.    FirstThunk          dd IAT_1 - 401000h - 400h + 2000h  160.IID_2:  161.    OriginalFirstThunk2         dd IAT_2-401000h-400h+2000h  162.    TimeDateStemp2          dd 0  163.    ForwarderChain2         dd 0  164.    DllName2                dd _DllName2-401000h-400h+2000h  165.    FirstThunk2             dd IAT_2-401000h-400h+2000h  166.IID_END:  167.    IIDEND              dd 5 dup(0) ; 用一个全0的结构作为结束  168.IAT_1:  169.    AddressOfData1          dd IIBN_1-401000h-400h+2000h  170.    AddressOfDataEnd1       dd 0  171.IAT_2:  172.    AddressOfData2          dd IIBN_2-401000h-400h+2000h  173.    AddressOfDataEnd2       dd 0  174.IIBN_1:  175.    Hint1               dw 0  176.    Nama1               db MessageBoxA,0  177.    DllName1            db user32.dll,0,0  178.IIBN_2:  179.    Hint2               dw 0  180.    Nama2               db ExitProcess,0  181.    _DllName2           db kernel32.dll,0,0  182.IMPORT_END   equ  this BYTE   183.IMPORT_LENGTH  equ offset IMPORT_END - offset IMPORT_START   184.ZeroSpace3 db 200h - IMPORT_LENGTH dup(0)  185.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  186.; THE_PE_END  187.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  188.REMOTE_CODE_END equ this byte  189.REMOTE_CODE_LENGTH equ offset REMOTE_CODE_END - offset REMOTE_CODE_START  
View Code

MakePe.asm:

技术分享
 .386  02.    .model flat, stdcall  03.    option casemap:none  04.      05.include windows.inc  06.include user32.inc  07.include kernel32.inc  08.includelib user32.lib  09.includelib kernel32.lib  10.  11.    .data  12.hOutFile    dd 0  13.BytesWrite  dd 0  14.  15.    .const  16.szCaption   db Info, 0  17.szContext   db Success, 0  18.szOutFileName   db Pe.exe, 0  19.  20.    .code  21.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  22.; 将PE数据代码引入,编译并产生Pe.exe文件  23.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  24.include Pe.asm  25.  26.start:  27.    invoke CreateFile, offset szOutFileName, GENERIC_READ or GENERIC_WRITE, \  28.            FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL  29.    mov hOutFile, eax  30.    invoke WriteFile, hOutFile, offset REMOTE_CODE_START, REMOTE_CODE_LENGTH, \   31.            addr BytesWrite, NULL  32.    invoke MessageBox, NULL, offset szContext, offset szCaption, MB_OK  33.    invoke ExitProcess, 0  34.      35.    end start  
View Code

SEH的学习

发生异常时系统的处理顺序(by Jeremy Gordon):
    1. 系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正在被调试,则系统     挂起程序并向调试器发送EXCEPTION_DEBUG_EVENT消息.呵呵,这不是正好可以用来探测调试器的存在吗?
    2. 如果你的程序没有被调试或者调试器未能处理异常,系统就会继续查找你是否安装了线程相关的异常处理例程,如果     你安装了线程相关的异常处理例程,系统就把异常发送给你的程序seh处理例程,交由其处理.
    3. 每个线程相关的异常处理例程可以处理或者不处理这个异常,如果他不处理并且安装了多个线程相关的异常处理例程,     可交由链起来的其他例程处理.
    4. 如果这些例程均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知debugger.
    5. 如果程序未处于被调试状态或者debugger没有能够处理,并且你调用SetUnhandledExceptionFilter安装了最后异     常处理例程的话,系统转向对它的调用.
    6. 如果你没有安装最后异常处理例程或者他没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框,     你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统     就调用ExitProcess终结程序.
    7. 不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开,这是线程异常处理例程最后清理的机会.

 

有两种类型的异常处理句柄,一种是final型的,这是在你的异常未能得到线程相关处理例程处理操作系统在即将关闭程序之前会 回调的例程,这个例程是进程相关的而不是线程相关的,因此无论是哪个线程发生异常未能被处理,都会调用这个例程

技术分享
.386  02.    .model flat, stdcall  03.    option casemap:none  04.      05.include windows.inc  06.include user32.inc  07.include kernel32.inc  08.includelib user32.lib  09.includelib kernel32.lib  10.  11.    .data  12.szCaption   db TestSEH, 0  13.szMsgOK     db OK, the exception was handled by final handler!, 0  14.szMsgERR1   db It would never Get here!, 0  15.buff        db 200 dup(0)  16.  17.    .code  18.start:  19.    lea eax, Final_Handler  20.    invoke SetUnhandledExceptionFilter, eax  21.    xor ecx, ecx  22.    mov eax, 200  23.    cdq  24.    div ecx  25.    ; 以下不会运行  26.    invoke MessageBox, NULL, addr szMsgERR1, addr szCaption, \  27.        MB_OK or MB_ICONEXCLAMATION  28.    invoke ExitProcess, NULL  29.      30.Final_Handler:  31.    invoke MessageBox, NULL, addr szMsgOK, addr szCaption, \  32.        MB_OK or MB_ICONEXCLAMATION  33.    mov eax, EXCEPTION_EXECUTE_HANDLER  ; 1, 这时不出现非法操作的对话框  34.    ;mov eax, EXCEPTION_CONTINUE_SEARCH ; 0, 出现,这时是调用系统默认的异常处理过程,程序被终结  35.    ;mov eax, EXCEPTION_CONTINUE_EXECUTION  ; -1, 不断出现对话框,你将先去死循环  36.                        ; 因为我们并没有修复ecx,所以不断产生异常,然后不断调用这个例程  37.  38.    ret  39.      40.    end start  
View Code


windows根据你的异常处理程序的返回值来决定如何进一步处理 EXCEPTION_EXECUTE_HANDLER            equ 1    表示我已经处理了异常,可以优雅地结束了 EXCEPTION_CONTINUE_SEARCH            equ 0    表示我不处理,其他人来吧,于是windows调用默认的处理程序                                                                                            显示一个错误框,并结束 EXCEPTION_CONTINUE_EXECUTION     equ -1   表示错误已经被修复,请从异常发生处继续执行

 

 

另一种是per_Thread Exception Handler->线程相关的异常处理,通常每个线程初始化准备好运行时fs指向一个TIB结构 (THREAD INFORMATION BLOCK)

这个结构的第一个元素fs:[0]指向一个_EXCEPTION_REGISTRATION结构, 后面_EXCEPTION_REGISTRATION为了简化,用ERR来代替这个结构...不要说没见过啊... fs:[0]->     _EXCEPTION_REGISTRATION struc     prev dd ?                      ;前一个_EXCEPTION_REGISTRATION结构     handler dd ?                  ;异常处理例程入口....呵呵,现在明白该怎么作了吧     _EXCEPTION_REGISTRATION ends     我们可以建立一个ERR结构然后将fs:[0]换成指向他的指针,当然最常用的是堆栈,如果你用静态内存区也可以,没有人阻止你     在asm世界,放心地干吧,除了多S几次之外,通常不会有更大的损失     把handler域换成你的程序入口,就可以在发生异常时调用你的代码了

Ps: 下面的代码运行后会弹出异常处理例程中的对话框,然后弹出系统错误报告对话框,点击关闭后,又会弹出异常处理例程中的对话框,具体原因参见<Windows环境32位汇编语言程序设计>14.3.4节

注意和final返回值的含义不同

技术分享
.386  02.    .model flat, stdcall  03.    option casemap:none  04.      05.include windows.inc  06.include user32.inc  07.include kernel32.inc  08.includelib user32.lib  09.includelib kernel32.lib  10.  11.    .data  12.szCaption   db TestSEH, 0  13.szMsgOK     db "It‘s now in the Per_Thread handler!", 0  14.szMsgERR1   db It would never Get here!, 0  15.buff        db 200 dup(0)  16.  17.    .code  18.start:  19.    assume fs:nothing  20.    push offset perThread_Handler  21.    push fs:[0]  22.    mov fs:[0], esp  23.    xor ecx, ecx  24.    mov eax, 200  25.    cdq  26.    div ecx  27.    ; 以下不会运行  28.    invoke MessageBox, NULL, addr szMsgERR1, addr szCaption, \  29.        MB_OK or MB_ICONINFORMATION  30.    pop fs:[0]  31.    add esp, 4  32.    invoke ExitProcess, NULL  33.      34.perThread_Handler:  35.    invoke MessageBox, NULL, addr szMsgOK, addr szCaption, \  36.        MB_OK or MB_ICONINFORMATION  37.    mov eax, 1  ; ExceptionContinueSearch, 不处理,由其他例程或系统处理  38.    ;mov eax, 0 ; ExceptionContinueExecution, 表示修复CONTEXT, 可以从已成发生处继续执行  39.            ; 如果返回0,你会陷入死循环,不断跳出对话框  40.    ret  41.      42.    end start  
View Code

 

当异常发生时,系统给了我们一个处理异常的机会,他首先会调用我们自定义的seh处理例程,当然也包括  了相关信息,在调用之前,系统把包含这些信息结构的指针压入stack,供我们的异常处理例程调用, 传递给例程的参数通常是四个,其中只有三个有明确意义,另一个到现在为止还没有发现有什么作用, 这些参数是:pExcept:DWORD,pErr:DWORD,pContext:DWORD,pDispatch意义如下: pExcept:  ---  EXCEPTION_RECORD结构的指针 pErr:    ---  前面ERR结构的指针 pContext: --- CONTEXT结构的指针 pDispatch:---没有发现有啥意义 ERR结构是前面介绍的_EXCEPTION_REGISTRATION结构,往前翻翻,Dispatch省略,下面介绍 EXCEPTION_RECORD和CONTEXT结构的定义(Windows核心编程第五版P657, Windows环境下32位汇编程序设计第二版P508):01.EXCEPTION_RECORD STRUCT  02.  ExceptionCode        DWORD      ?      ;//异常码  03.  ExceptionFlags        DWORD      ?      ;//异常标志  04.  pExceptionRecord      DWORD      ?      ;//指向另外一个EXCEPTION_RECORD的指针  05.  ExceptionAddress      DWORD      ?      ;//异常发生的地址  06.  NumberParameters      DWORD      ?      ;//下面ExceptionInformation所含有的dword数目  07.  ExceptionInformation  DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)  08.EXCEPTION_RECORD ENDS                      ;//EXCEPTION_MAXIMUM_PARAMETERS ==1   ExceptionCode 异常类型,SDK里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多  的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下:                C0000005h----读写内存冲突               C0000094h----非法除0                C00000FDh----堆栈溢出或者说越界               80000001h----由Virtual Alloc建立起来的属性页冲突               C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常               C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码,                            如果RtlUnwind时没有Exception Record参数也同样会填入这个代码               80000003h----调试时因代码中int3中断               80000004h----处于被单步调试状态               注:也可以自己定义异常代码,遵循如下规则:                _____________________________________________________________________+               位:      31~30            29~28          27~16          15~0                _____________________________________________________________________+               含义:    严重程度          29位            功能代码        异常代码                         0==成功        0==Mcrosoft    MICROSOFT定义  用户定义                         1==通知        1==客户                         2==警告          28位                         3==错误        被保留必须为0  ExceptionFlags 异常标志               0----可修复异常               1----不可修复异常               2----正在展开,不要试图修复什么,需要的话,释放必要的资源 pExceptionRecord 如果程序本身导致异常,指向那个异常结构 ExceptionAddress 发生异常的eip地址 ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时含义如下               第一个dword 0==读冲突 1==写冲突               第二个dword 读写冲突地址 

 

PE结构、SEH相关知识学习笔记