SEH 机制探索1 --- TEB 结构

本文详细解析了Windows下的SEH(结构化异常处理)机制,并深入探讨了TEB(线程环境块)结构,介绍了如何通过TEB访问异常处理程序链。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SEH 机制探索1 --- TEB 结构


在 windows 中的 SEHStructured Exception Handling)结构化异常处理需要使用 TIBThread Information Block)线程信息块的 _EXCEPTION_REGISTRATION_RECORD结构。而 TIB 结构包括在 TEBThread Environment Block) 结构下,下面来探索下 TEB 结构。


1. TEB 结构

实际上关于 TIBThread Information Block)和 TEBThread Environment Block)的定义比较混乱,在 NT 类的系统应该称为 TEB,我没在 SDK 的 include 的文件里找到 TEB 结构的定义,也没有在 VC 的 crt 头文件里找到 TEB 的定义。在 winternl.h 文件里的 __TEB 显然不是我们想要的 TEB 结构。

TEB 和 TIB 都分为 32 位版本和 64 位版本,我们需要借助于 windbg 工具来观察 TEB 结构。

应该要有个认识在 x64 windows 平台,ntdll 模块是 64 位的,而 ntdll32 模块是 32 位的。

因此,32 位的TEB 结构符号是:ntdll32!_TEB,64 位的 TEB 结构符号是: ntdll!_TEB


1.1 TEB 整体结构

下面是在我的环境里(x64 windows7 平台)的 windbg 得到的 32 位 TEB 结构:

0:000:x86> dt ntdll32!_TEB
   +0x000 NtTib            : _NT_TIB
   +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   : [26] Uint4B
   +0x0ac UserReserved     : [5] Uint4B
   +0x0c0 WOW32Reserved    : Ptr32 Void
   +0x0c4 CurrentLocale    : Uint4B
   +0x0c8 FpSoftwareStatusRegister : Uint4B
   +0x0cc SystemReserved1  : [54] Ptr32 Void
   +0x1a4 ExceptionCode    : Int4B
   +0x1a8 ActivationContextStackPointer : Ptr32 _ACTIVATION_CONTEXT_STACK
   +0x1ac SpareBytes       : [36] UChar
   +0x1d0 TxFsContext      : Uint4B
   +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  : [62] Uint4B
   +0x7c4 glDispatchTable  : [233] Ptr32 Void
   +0xb68 glReserved1      : [29] 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 : [261] Wchar
   +0xe0c DeallocationStack : Ptr32 Void
   +0xe10 TlsSlots         : [64] Ptr32 Void
   +0xf10 TlsLinks         : _LIST_ENTRY
   +0xf18 Vdm              : Ptr32 Void
   +0xf1c ReservedForNtRpc : Ptr32 Void
   +0xf20 DbgSsReserved    : [2] Ptr32 Void
   +0xf28 HardErrorMode    : Uint4B
   +0xf2c Instrumentation  : [9] Ptr32 Void
   +0xf50 ActivityId       : _GUID
   +0xf60 SubProcessTag    : Ptr32 Void
   +0xf64 EtwLocalData     : Ptr32 Void
   +0xf68 EtwTraceData     : Ptr32 Void
   +0xf6c WinSockData      : Ptr32 Void
   +0xf70 GdiBatchCount    : Uint4B
   +0xf74 CurrentIdealProcessor : _PROCESSOR_NUMBER
   +0xf74 IdealProcessorValue : Uint4B
   +0xf74 ReservedPad0     : UChar
   +0xf75 ReservedPad1     : UChar
   +0xf76 ReservedPad2     : UChar
   +0xf77 IdealProcessor   : UChar
   +0xf78 GuaranteedStackBytes : Uint4B
   +0xf7c ReservedForPerf  : Ptr32 Void
   +0xf80 ReservedForOle   : Ptr32 Void
   +0xf84 WaitingOnLoaderLock : Uint4B
   +0xf88 SavedPriorityState : Ptr32 Void
   +0xf8c SoftPatchPtr1    : Uint4B
   +0xf90 ThreadPoolData   : Ptr32 Void
   +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
   +0xf98 MuiGeneration    : 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 FlsData          : Ptr32 Void
   +0xfb8 PreferredLanguages : Ptr32 Void
   +0xfbc UserPrefLanguages : Ptr32 Void
   +0xfc0 MergedPrefLanguages : Ptr32 Void
   +0xfc4 MuiImpersonation : Uint4B
   +0xfc8 CrossTebFlags    : Uint2B
   +0xfc8 SpareCrossTebBits : Pos 0, 16 Bits
   +0xfca SameTebFlags     : Uint2B
   +0xfca SafeThunkCall    : Pos 0, 1 Bit
   +0xfca InDebugPrint     : Pos 1, 1 Bit
   +0xfca HasFiberData     : Pos 2, 1 Bit
   +0xfca SkipThreadAttach : Pos 3, 1 Bit
   +0xfca WerInShipAssertCode : Pos 4, 1 Bit
   +0xfca RanProcessInit   : Pos 5, 1 Bit
   +0xfca ClonedThread     : Pos 6, 1 Bit
   +0xfca SuppressDebugMsg : Pos 7, 1 Bit
   +0xfca DisableUserStackWalk : Pos 8, 1 Bit
   +0xfca RtlExceptionAttached : Pos 9, 1 Bit
   +0xfca InitialThread    : Pos 10, 1 Bit
   +0xfca SpareSameTebBits : Pos 11, 5 Bits
   +0xfcc TxnScopeEnterCallback : Ptr32 Void
   +0xfd0 TxnScopeExitCallback : Ptr32 Void
   +0xfd4 TxnScopeContext  : Ptr32 Void
   +0xfd8 LockCount        : Uint4B
   +0xfdc SpareUlong0      : Uint4B
   +0xfe0 ResourceRetValue : Ptr32 Void

一个完整的 TEB 结构是那么的复杂。TEB 结构第 1 个成员就是 TIB 结构(注意是整个结构)


1.2 TIB 结构

我们可以观察 NT_TIB 结构是怎样的,承接上面我们来看 32 位版本的 TIB 结构:

0:000:x86> dt ntdll32!_NT_TIB
   +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 StackBase        : Ptr32 Void
   +0x008 StackLimit       : Ptr32 Void
   +0x00c SubSystemTib     : Ptr32 Void
   +0x010 FiberData        : Ptr32 Void
   +0x010 Version          : Uint4B
   +0x014 ArbitraryUserPointer : Ptr32 Void
   +0x018 Self             : Ptr32 _NT_TIB

上面就是 32 位版本的 TIB 结构,然而它的第 1 个成员是个指针,指向另一个结构:_EXCEPTION_REGISTRATION_RECORD,这是 SEH 机制中的最重要的一部分。

而在 WinNT.h 文件中对 NT_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;

我们重点关注的是这个结构前面有个指向 _EXCEPTION_REGISTRATION_RECORD 结构的指针,这个结构是用来注册我们的 _except_handler() 即:异常处理程序。


1.3 _EXCEPTION_REGISTRATION_RECORD 结构

最后,我们观察 _EXCEPTION_REGISTRATION_RECORD 结构是怎样的:

0:000:x86> dt _EXCEPTION_REGISTRATION_RECORD
MSVCR100D!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION

这个结构不是定义在 ntdll32 模块里的,而是在 VC++ 的运行时库里(MSVCR100D)里,这是调试版本的运行时库里的结构,当然非调试版本的也是这样的。

这个结构里的 Next 成员是指向另一个 _EXCEPTION_REGISTRATION_RECORD 结构,这样就构成一条链,SEH 机制根据这条链可以查找线程中所有的 exception handler,许多文档中这个成员解为 prev 即:前一个,这里并不矛盾。以当前的 EXCETPTION_REGISTRATION_RECORD 结构为参照,当前的是最顶的一层,那么上一个和下一个意思是一样的。

Handler 成员是指向真正处理的 exception handler,这个 exception handler 可以是我们自己定义的异常处理程序,这个异常处理程序的形式是:

typedef
__drv_sameIRQL
__drv_functionClass(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
    __inout struct _EXCEPTION_RECORD *ExceptionRecord,
    __in PVOID EstablisherFrame,
    __inout struct _CONTEXT *ContextRecord,
    __in PVOID DispatcherContext
    );

typedef EXCEPTION_ROUTINE *PEXCEPTION_ROUTINE;

在 VC 的 CRT 头文件 except.inc 里找到的定义:

; exception registration record structure.

__EXCEPTIONREGISTRATIONRECORD struc
       prev_structure          dd      ?
       ExceptionHandler        dd      ?
        ExceptionFilter         dd      ?
        FilterFrame             dd      ?
        PExceptionInfoPtrs      dd      ?
__EXCEPTIONREGISTRATIONRECORD ends

每个版本的 VC 可能会不一样,上面是在 visual studio 2010 中得到的。这似乎和在 windbg 中所显示的有所不同,不管怎样我们关注的是前面两个用红色标注的成员。

 

2. FS 寄存器

在 Windows 中 TEB 由 FS:0 指出,FS:0 是被设定为永远指向线程的 TEB 块,由于 TEB 的前面是 _EXCEPTION_REGISTRATION_RECORD 结构的指针,因此 FS:[0] 可以获得这个指针值。

因此,我们可以使用下面代码来人工调用 exception handler:

mov eax, dword ptr fs:[0]        ; get exception_registration_record
call dword ptr [eax + 4]         ; call exception handler

这里只是演示系统如何是查找并调用 exception handler,代码中不可能使用人工调方法用来调用 exception handler

于是在几乎所有的构建 SEH 链的示例中,都使用类似的这几行代码:

push exception_handler            ; set current exception handler
push dword ptr fs:[0]             ; set prev TEB
mov fs:[0], esp                   ; set current exception_registration_record

 

上一页  目录  下一页


版权 mik 所有,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值