Aperodry 发表于 2018-1-9 23:47

从TEB到PEB再到SEH(二)

本帖最后由 Aperodry 于 2018-1-10 19:13 编辑

上一章:从TEB到PEB再到SEH(一)

目录:

[*]什么是SEH?
[*]Windows下各种异常处理的优先级

[*]认识SEH链及处理机制
[*]SEH的注册及SEH的删除
[*]异常的种类和常见的异常代码
[*]SEH的异常处理
[*]小结

上一章简单介绍了TEB PEB在fs寄存器中的位置及常用的结构体成员,接下来我们开始认识SEH:


(一)、 什么是SEH?
SEH( Structured Exception Handling , 结构化异常处理 )
结构化异常处理(SEH)是Windows操作系统提供的强大异常处理功能。而Visual C++中的__try{}/__finally{}和__try{}/__except{}结构本质上是对Windows提供的SEH的封装
我们知道SEH是基于线程的异常处理,我们利用 __try{}/__except{}来模拟一下SEH的异常处理:

在这里可以看到我们把EAX的值置为空指针,然后向空指针里写入值,引发 STATUS_ACCESS_VIOLATION(内存访问异常) ,然后在异常处理里面把EAX的值设置为 变量dwTest的地址,然后返回 EXCEPTION_CONTINUE_EXECUTION 表示异常被处理,从异常处继续执行,这里是MSDN对于异常处理( Exception Handling )返回值的定义:#define EXCEPTION_EXECUTE_HANDLER       1      //表示异常被处理,从下一条指令开始执行
#define EXCEPTION_CONTINUE_SEARCH       0      //表示异常未被处理,交由下一个SEH
#define EXCEPTION_CONTINUE_EXECUTION    -1    //表示异常被处理,从异常处开始执行对于上面这段定义,很多人给出的注释不同,以上注释是我对他们的实验结果和理解
(二)、 Windows下各种异常处理的优先级
平时我们听说过很多异常处理术语:VEH SEH VCH UEF等等,下面我们用实验整理它们先后的处理顺序;1.VEH(向量化异常处理,最顶端的异常处理)PVOID WINAPI AddVectoredExceptionHandler(
_In_ULONG FirstHandler,
_In_PVECTORED_EXCEPTION_HANDLER VectoredHandler
);向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部,0为底部),异常处理中最先执行
2.VCH(同上,最低端 的异常处理 )PVOID WINAPI AddVectoredContinueHandler(
_In_ULONG FirstHandler,
_In_PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部, 0为底部 ) ,异常处理中最后执行
3.SEH(结构化异常处理,基于线程栈的异常处理)
SEH是基于线程的异常处理,因为SEH链指针是在TEB(线程信息块)的第一个结构体成员(NT_TIB)的头部:fs:
4.UEF(TopLevelEH,顶级异常处理)LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
_In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);


TopLevelEH 为线程顶级异常处理器,通常可以处理到所有线程消息发生的异常。
这里我们可以发现以上异常处理回调函数参数大都为EXCEPTION_POINTERS 结构体,我们查询下它的结构:typedef struct _EXCEPTION_POINTERS {
    PEXCEPTION_RECORD ExceptionRecord;
    PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;EXCEPTION_RECORD:typedef struct _EXCEPTION_RECORD {
    DWORD    ExceptionCode;          //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
    DWORD ExceptionFlags;            //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
    struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
    PVOID ExceptionAddress;          //异常发生的地址
    DWORD NumberParameters;      //下面ExceptionInformation所含有的dword数目
    ULONG_PTR ExceptionInformation; //附加消息,如读或写冲突
} EXCEPTION_RECORD;CONTEXT:typedef struct _CONTEXT {

    //
    // The flags values within this flag control the contents of
    // a CONTEXT record.
    //
    // If the context record is used as an input parameter, then
    // for each portion of the context record controlled by a flag
    // whose value is set, it is assumed that that portion of the
    // context record contains valid context. If the context record
    // is being used to modify a threads context, then only that
    // portion of the threads context will be modified.
    //
    // If the context record is used as an IN OUT parameter to capture
    // the context of a thread, then only those portions of the thread's
    // context corresponding to set flags will be returned.
    //
    // The context record is never used as an OUT only parameter.
    //

    DWORD ContextFlags;

    //
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    // set in ContextFlags.Note that CONTEXT_DEBUG_REGISTERS is NOT
    // included in CONTEXT_FULL.
    //

    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    //

    FLOATING_SAVE_AREA FloatSave;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    //

    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_INTEGER.
    //

    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_CONTROL.
    //

    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;            // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;

    //
    // This section is specified/returned if the ContextFlags word
    // contains the flag CONTEXT_EXTENDED_REGISTERS.
    // The format and contexts are processor specific
    //

    BYTE    ExtendedRegisters;

} CONTEXT;
CONTEXT结构体大家应该都懂!
下面我们简单的写一个Demo,试验一下他们的处理顺序:// SEHTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

//
LONG __stdcall VEHandler(
      EXCEPTION_POINTERS *ExceptionInfo
      )
{
      printf("VEHandler\n");
      return EXCEPTION_CONTINUE_SEARCH;
}

LONG __stdcall VCHandler(
      EXCEPTION_POINTERS *ExceptionInfo
      )
{
      printf("VCHandler\n");
      ExceptionInfo->ContextRecord->Eip++;
      return EXCEPTION_CONTINUE_EXECUTION;
}

//
LONG NTAPI TopLevelExcepFilter(EXCEPTION_POINTERS *pExcepInfo)
{
      printf("TopLevelEHandler\n");
      return EXCEPTION_CONTINUE_EXECUTION;
}
//
LONG NTAPI SEHander(EXCEPTION_POINTERS *ExceptionInfo){
      //异常处理
      printf("SEHandler\n");

      return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
      AddVectoredExceptionHandler(0,VEHandler);
      AddVectoredContinueHandler(0,VCHandler);
      SetUnhandledExceptionFilter(&TopLevelExcepFilter);
      __try
      {
                __asm int 3
      }
      __except (SEHander(GetExceptionInformation()))
      {
      }
      system("Pause");
      return 0;
}

我们分别注册了VEH、VCH、 TopLevalEH 、SEH,我们看下结果:

他们处理异常的优先级为:

[*]调试器
[*]VEH
[*]SEH
[*]UEF
[*]VCH
为什么调试器在第一个呢?因为我们发现在VS里面调试直接接管了异常。(/手动滑稽)

(三)、 认识SEH链及处理机机制
我们上一章发现SEH链表位于结构体 NT_TIB 的第一个结构体成员,而结构体 NT_TIB 也位于TEB的第一个结构体成员,一句话而言SEH链表指针位于寄存器 FS : [ 0 ] 的位置:ntdll!_TEB
   +0x000 NtTib            : _NT_TIB                //SEH链表头指针
   +0x01c EnvironmentPointer : Ptr32 Void
   +0x020 ClientId         : _CLIENT_ID
   +0x028 ActiveRpcHandle: Ptr32 Void
   +0x02c ThreadLocalStoragePointer : Ptr32 Void
   +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
   +0x034 LastErrorValue   : Uint4B
   +0x038 CountOfOwnedCriticalSections : Uint4B
   +0x03c CsrClientThread: Ptr32 Void
   +0x040 Win32ThreadInfo: Ptr32 Void
   +0x044 User32Reserved   : Uint4B
   +0x0ac UserReserved   : Uint4B
   +0x0c0 WOW32Reserved    : Ptr32 Void
   +0x0c4 CurrentLocale    : Uint4B
   +0x0c8 FpSoftwareStatusRegister : Uint4B
   +0x0cc SystemReserved1: Ptr32 Void
   +0x1a4 ExceptionCode    : Int4B
   +0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
   +0x1bc SpareBytes1      : UChar
   +0x1d4 GdiTebBatch      : _GDI_TEB_BATCH
   +0x6b4 RealClientId   : _CLIENT_ID
   +0x6bc GdiCachedProcessHandle : Ptr32 Void
   +0x6c0 GdiClientPID   : Uint4B
   +0x6c4 GdiClientTID   : Uint4B
   +0x6c8 GdiThreadLocalInfo : Ptr32 Void
   +0x6cc Win32ClientInfo: Uint4B
   +0x7c4 glDispatchTable: Ptr32 Void
   +0xb68 glReserved1      : Uint4B
   +0xbdc glReserved2      : Ptr32 Void
   +0xbe0 glSectionInfo    : Ptr32 Void
   +0xbe4 glSection      : Ptr32 Void
   +0xbe8 glTable          : Ptr32 Void
   +0xbec glCurrentRC      : Ptr32 Void
   +0xbf0 glContext      : Ptr32 Void
   +0xbf4 LastStatusValue: Uint4B
   +0xbf8 StaticUnicodeString : _UNICODE_STRING
   +0xc00 StaticUnicodeBuffer : Uint2B
   +0xe0c DeallocationStack : Ptr32 Void
   +0xe10 TlsSlots         : Ptr32 Void
   +0xf10 TlsLinks         : _LIST_ENTRY
   +0xf18 Vdm            : Ptr32 Void
   +0xf1c ReservedForNtRpc : Ptr32 Void
   +0xf20 DbgSsReserved    : Ptr32 Void
   +0xf28 HardErrorsAreDisabled : Uint4B
   +0xf2c Instrumentation: Ptr32 Void
   +0xf6c WinSockData      : Ptr32 Void
   +0xf70 GdiBatchCount    : Uint4B
   +0xf74 InDbgPrint       : UChar
   +0xf75 FreeStackOnTermination : UChar
   +0xf76 HasFiberData   : UChar
   +0xf77 IdealProcessor   : UChar
   +0xf78 Spare3         : Uint4B
   +0xf7c ReservedForPerf: Ptr32 Void
   +0xf80 ReservedForOle   : Ptr32 Void
   +0xf84 WaitingOnLoaderLock : Uint4B
   +0xf88 Wx86Thread       : _Wx86ThreadState
   +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
   +0xf98 ImpersonationLocale : Uint4B
   +0xf9c IsImpersonating: Uint4B
   +0xfa0 NlsCache         : Ptr32 Void
   +0xfa4 pShimData      : Ptr32 Void
   +0xfa8 HeapVirtualAffinity : Uint4B
   +0xfac CurrentTransactionHandle : Ptr32 Void
   +0xfb0 ActiveFrame      : Ptr32 _TEB_ACTIVE_FRAME
   +0xfb4 SafeThunkCall    : UChar
   +0xfb5 BooleanSpare   : UChar我们直接访问 FS: 即为TIB的结构体地址:typedef struct _NT_TIB {
    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID SubSystemTib;
#if defined(_MSC_EXTENSIONS)
    union {
      PVOID FiberData;
      DWORD Version;
    };
#else
    PVOID FiberData;
#endif
    PVOID ArbitraryUserPointer;
    struct _NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;
如上述代码所示 结构体成员 ExceptionList 即为SEH链的头部指针
So、 fs:即为SEH链的指针,我们接着看对于SEH链的定义:typedef struct _EXCEPTION_REGISTRATION_RECORD {
    struct _EXCEPTION_REGISTRATION_RECORD *Next;
    PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
第一个成员 Next 为指向下一个链表的指针,直到遇到 0xFFFFFFFF 结束,而结构体成员 Handler 为SEH的异常处理函数指针,我们接着看它对于
SEH异常处理函数 EXCEPTION_ROUTINE 的定义:typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
    _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
    _In_ PVOID EstablisherFrame,
    _Inout_ struct _CONTEXT *ContextRecord,
    _In_ PVOID DispatcherContext
    );
可以看到它对于SEH定义了以 EXCEPTION_DISPOSITION 为返回值的回调函数,我们接着查看它们的定义:typedef enum _EXCEPTION_DISPOSITION {
    ExceptionContinueExecution,      //继续执行异常代码
    ExceptionContinueSearch,            //运行下一个异常处理器
    ExceptionNestedException,         //在OS内部使用
    ExceptionCollidedUnwind             //在OS内部使用
} EXCEPTION_DISPOSITION;
接着我们整理下它的异常处理过程:从上图可以明白 SEH接收到异常然后处理,处理失败返回 ExceptionContinueSearch(1) 继续运行下一个Handler处理,直到返回ExceptionContinueSearch(0),若是一直处理不了直到遇到0xFFFFFFFF 把异常交给UEF处理。

(四)、 SEH的注册及SEH的删除
通过上述的整理就可以知道,SEH的异常处理的定义为:
EXCEPTION_DISPOSITION NTAPI _except_handler(
      _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,    //指向包含异常信息的EXCEPTION_RECORD结构
      _In_ PVOID EstablisherFrame,      //指向该异常相关的EXCEPTION_REGISTRATION结构
      _Inout_ struct _CONTEXT *ContextRecord,    //指向线程环境CONTEXT结构的指针
      _In_ PVOID DispatcherContext)
现在要来谈SEH的注册了,我们的操作为:
push @_except_handler    ;异常处理器
push dwod ptr fs:   ;取出 SEH链表头
mov dwod ptr fs:,esp;添加链表
卸载SEH:pop dword ptr fs:    ;还原链表头
add esp,4    ;删除 异常处理器
这些操作很简单,很多前辈们帖子里都有,代码可能不一样,反正是一个意思就行。

(五)、 异常的种类和常见的异常代码
这里是MSDN中定义的异常代码:/*lint -save -e767 */
#define STATUS_WAIT_0                           ((DWORD   )0x00000000L)
#define STATUS_ABANDONED_WAIT_0          ((DWORD   )0x00000080L)   
#define STATUS_USER_APC                  ((DWORD   )0x000000C0L)   
#define STATUS_TIMEOUT                   ((DWORD   )0x00000102L)   
#define STATUS_PENDING                   ((DWORD   )0x00000103L)   
#define DBG_EXCEPTION_HANDLED            ((DWORD   )0x00010001L)   
#define DBG_CONTINUE                     ((DWORD   )0x00010002L)   
#define STATUS_SEGMENT_NOTIFICATION      ((DWORD   )0x40000005L)   
#define STATUS_FATAL_APP_EXIT            ((DWORD   )0x40000015L)   
#define DBG_TERMINATE_THREAD             ((DWORD   )0x40010003L)   
#define DBG_TERMINATE_PROCESS            ((DWORD   )0x40010004L)   
#define DBG_CONTROL_C                  ((DWORD   )0x40010005L)   
#define DBG_PRINTEXCEPTION_C             ((DWORD   )0x40010006L)   
#define DBG_RIPEXCEPTION               ((DWORD   )0x40010007L)   
#define DBG_CONTROL_BREAK                ((DWORD   )0x40010008L)   
#define DBG_COMMAND_EXCEPTION            ((DWORD   )0x40010009L)   
#define STATUS_GUARD_PAGE_VIOLATION      ((DWORD   )0x80000001L)   
#define STATUS_DATATYPE_MISALIGNMENT   ((DWORD   )0x80000002L)   
#define STATUS_BREAKPOINT                ((DWORD   )0x80000003L)   
#define STATUS_SINGLE_STEP               ((DWORD   )0x80000004L)   
#define STATUS_LONGJUMP                  ((DWORD   )0x80000026L)   
#define STATUS_UNWIND_CONSOLIDATE      ((DWORD   )0x80000029L)   
#define DBG_EXCEPTION_NOT_HANDLED      ((DWORD   )0x80010001L)   
#define STATUS_ACCESS_VIOLATION          ((DWORD   )0xC0000005L)   
#define STATUS_IN_PAGE_ERROR             ((DWORD   )0xC0000006L)   
#define STATUS_INVALID_HANDLE            ((DWORD   )0xC0000008L)   
#define STATUS_INVALID_PARAMETER         ((DWORD   )0xC000000DL)   
#define STATUS_NO_MEMORY               ((DWORD   )0xC0000017L)   
#define STATUS_ILLEGAL_INSTRUCTION       ((DWORD   )0xC000001DL)   
#define STATUS_NONCONTINUABLE_EXCEPTION((DWORD   )0xC0000025L)   
#define STATUS_INVALID_DISPOSITION       ((DWORD   )0xC0000026L)   
#define STATUS_ARRAY_BOUNDS_EXCEEDED   ((DWORD   )0xC000008CL)   
#define STATUS_FLOAT_DENORMAL_OPERAND    ((DWORD   )0xC000008DL)   
#define STATUS_FLOAT_DIVIDE_BY_ZERO      ((DWORD   )0xC000008EL)   
#define STATUS_FLOAT_INEXACT_RESULT      ((DWORD   )0xC000008FL)   
#define STATUS_FLOAT_INVALID_OPERATION   ((DWORD   )0xC0000090L)   
#define STATUS_FLOAT_OVERFLOW            ((DWORD   )0xC0000091L)   
#define STATUS_FLOAT_STACK_CHECK         ((DWORD   )0xC0000092L)   
#define STATUS_FLOAT_UNDERFLOW         ((DWORD   )0xC0000093L)   
#define STATUS_INTEGER_DIVIDE_BY_ZERO    ((DWORD   )0xC0000094L)   
#define STATUS_INTEGER_OVERFLOW          ((DWORD   )0xC0000095L)   
#define STATUS_PRIVILEGED_INSTRUCTION    ((DWORD   )0xC0000096L)   
#define STATUS_STACK_OVERFLOW            ((DWORD   )0xC00000FDL)   
#define STATUS_DLL_NOT_FOUND             ((DWORD   )0xC0000135L)   
#define STATUS_ORDINAL_NOT_FOUND         ((DWORD   )0xC0000138L)   
#define STATUS_ENTRYPOINT_NOT_FOUND      ((DWORD   )0xC0000139L)   
#define STATUS_CONTROL_C_EXIT            ((DWORD   )0xC000013AL)   
#define STATUS_DLL_INIT_FAILED         ((DWORD   )0xC0000142L)   
#define STATUS_FLOAT_MULTIPLE_FAULTS   ((DWORD   )0xC00002B4L)   
#define STATUS_FLOAT_MULTIPLE_TRAPS      ((DWORD   )0xC00002B5L)   
#define STATUS_REG_NAT_CONSUMPTION       ((DWORD   )0xC00002C9L)   
#define STATUS_HEAP_CORRUPTION         ((DWORD   )0xC0000374L)   
#define STATUS_STACK_BUFFER_OVERRUN      ((DWORD   )0xC0000409L)   
#define STATUS_INVALID_CRUNTIME_PARAMETER ((DWORD   )0xC0000417L)   
#define STATUS_ASSERTION_FAILURE         ((DWORD   )0xC0000420L)   
#if defined(STATUS_SUCCESS) || (_WIN32_WINNT > 0x0500) || (_WIN32_FUSION >= 0x0100)
#define STATUS_SXS_EARLY_DEACTIVATION    ((DWORD   )0xC015000FL)   
#define STATUS_SXS_INVALID_DEACTIVATION((DWORD   )0xC0150010L)   
#endif
/*lint -restore */ 下面我们举例几个常用到的异常代码:
STATUS_ACCESS_VIOLATION(0xC0000005)
非法访问异常,试图访问不存在、没有访问权限,或是试图向没有写入权限的地址或是向内核区域写入发生的异常。
STATUS_BREAKPOINT(0x80000003)
断点异常,这个不用提了吧,就是我们常说的INT 3(0xCC)断点
STATUS_ILLEGAL_INSTRUCTION(0xC000001D)
CPU遇到无法解析的指令时发生该异常STATUS_INTEGER_DIVIDE_BY_ZERO(0xC0000094)
除法中,分母为0时发生的异常STATUS_SINGLE_STEP
单步调试异常,在EFlag寄存器把TF标志位置1发生的单步调试异常。除此之外,也有很多平时可以遇到的,我只是举例了几个简单的。


(六)、 SEH的异常处理
我这里就随便写个Demo了:// SEHList.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
DWORD dwTest;
EXCEPTION_DISPOSITION NTAPI ExceptHandler(
      _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
      _In_ PVOID EstablisherFrame,
      _Inout_ struct _CONTEXT *ContextRecord,
      _In_ PVOID DispatcherContext){
      printf("进入异常处理\n");
      printf("异常地址:%X<异常代码:%X>\n", ExceptionRecord->ExceptionAddress,
                ExceptionRecord->ExceptionCode);
      ContextRecord->Eax = (DWORD)(&dwTest);
      return ExceptionContinueExecution;
}

int _tmain(int argc, _TCHAR* argv[])
{
      printf("注册SEH\n");
      __asm{
                lea eax, ExceptHandler
                        push eax
                        push fs :
                        mov dword ptr fs : , ESP
      }
      __asm{
                xor eax,eax
                        mov dword ptr,1234h
      }
      printf("删除SEH\n");
      __asm{
                pop dword ptr fs :
                        add esp, 4
      }
      printf("dwTest=%X\n", dwTest);
      getchar();
      return 0;
}

运行结果:

(七)、 小结

花了几个小时终于编辑好了,以上许多是个人的理解,如有错误还请指正!有时候分享也是一种学习,整理了这么多自己也终于搞清楚各种异常处理机制之间的关系了,不像以前一样只是有个朦胧的认识。

luli1111 发表于 2018-1-10 08:54

桥段 发表于 2018-1-10 10:54

看这个要有C和数据结构的基础的吧

纳兰容若 发表于 2018-1-11 16:23

多谢楼主分享{:301_978:}

shj2k 发表于 2018-1-12 15:56

先收藏下,有时间拜读

paituo 发表于 2018-7-31 10:23

标记下知识点~~~~~~~~~~

白额吊睛大虫 发表于 2019-4-14 17:48

谢谢楼主讲解 比较清晰

ja2007gg 发表于 2019-5-3 10:10

请问楼主现在还在弄热血江湖定点辅助吗 ?可以提供下地址吗?想学习并且使用

kellygod 发表于 2019-6-15 11:17

其实应该以x64的环境去写的,因为不能内嵌汇编的缘故,很多东西实现起来就要绕一下路了,

htpidk 发表于 2020-6-30 09:08

写的不错,当做复习了
页: [1] 2
查看完整版本: 从TEB到PEB再到SEH(二)