Gravitational plate of three masses and a slashed discABC0Static engraved plate. Three-dimensional view is unavailable or reduced motion is requested.

← back to fieldarchive

archiveJan 3, 2021

Windows SEH chain and TEB ExceptionList

How Windows builds the per-thread SEH singly-linked list on the stack, how FS:[0] and the TEB point at it, and how to walk the chain in WinDbg.

SEH

Exception handlers form a singly-linked list tied to each thread. Nodes live on the stack. The list head is a pointer at the start of the Thread Environment Block (TEB). To install a new handler, code pushes a new node, links it to the old head, then updates the TEB pointer so the new node is first.

Each node is an _EXCEPTION_REGISTRATION_RECORD: the handler address plus a pointer to the next node. The last node's "next" pointer is not NULL — it is 0xFFFFFFFF.

0:000> dt _EXCEPTION_REGISTRATION_RECORD
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
 
0:005> dt _EXCEPTION_REGISTRATION_RECORD
combase!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION

The TEB is also reachable through the FS segment starting at FS:[0], so you often see code like this:

mov eax, dword ptr fs:[00000000h] ; retrieve the head
push eax ; save the old head
lea eax, [ebp-10h]
mov dword ptr fs:[00000000h], eax ; set the new head
mov ecx, dword ptr [ebp-10h] ; get the old head (NEXT field of the current head)
mov dword ptr fs:[00000000h], ecx ; restore the old head

Compilers usually register one global handler and track which region is executing with a global. Each thread has its own TEB, and the OS keeps the FS-selected segment pointed at the current thread's TEB.

To get the TEB address, read FS:[18h] (the TEB Self field).

0:003> !teb
TEB at 00b95000
    ExceptionList:        032af770
    StackBase:            032b0000
    StackLimit:           032ac000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 00b95000
    EnvironmentPointer:   00000000
    ClientId:             0000351c . 00004360
    RpcHandle:            00000000
    Tls Storage:          00000000
    PEB Address:          00b50000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

Confirm FS points at that TEB:

0:003> dg fs
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0053 00b95000 00000fff Data RW Ac 3 Bg By P  Nl 000004f3

FS:[18h] holds the TEB address:

0:003> ?poi(fs:[18])
Evaluate expression: 12144640 = 00b95000

ExceptionList is the first field of _NT_TIB:

0:003> dt nt!_NT_TIB ExceptionList
ntdll!_NT_TIB
   +0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD

Each node is an _EXCEPTION_REGISTRATION_RECORD. Walk the whole list with !slist:

0:003> !slist $teb _EXCEPTION_REGISTRATION_RECORD
SLIST HEADER:
   +0x000 Alignment          : 32b0000032af770
   +0x000 Next               : 32af770
   +0x004 Depth              : 0
   +0x000 Sequence           : 0
 
SLIST CONTENTS:
032af770
   +0x000 Next             : 0x032af7dc _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x77759990     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0
032af7dc
   +0x000 Next             : 0x032af7f4 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x77759990     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0
032af7f4
   +0x000 Next             : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x7776734b     _EXCEPTION_DISPOSITION  ntdll!FinalExceptionHandlerPad27+0
ffffffff
   +0x000 Next             : ???? 
   +0x004 Handler          : ???? 
Can't read memory at ffffffff, error 0

$teb is the TEB address. A shorter walk uses !exchain:

0:003> !exchain
032af770: ntdll!_except_handler4+0 (77759990)
  CRT scope  0, filter: ntdll!DbgUiRemoteBreakin+3b (7778db7b)
                func:   ntdll!DbgUiRemoteBreakin+3f (7778db7f)
032af7dc: ntdll!_except_handler4+0 (77759990)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+3d6c5 (77784c8a)
                func:   ntdll!__RtlUserThreadStart+3d75e (77784d23)
032af7f4: ntdll!FinalExceptionHandlerPad27+0 (7776734b)
Invalid exception stack at ffffffff

You can also dump a single record by address:

0:003> dt 032af770 _EXCEPTION_REGISTRATION_RECORD
ntdll!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : 0x032af7dc _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x77759990     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0

related

  1. Jan 3, 2021/archiveWindows SEH (Structured Exception Handling)
  2. Jan 3, 2021/archiveTEB (Thread Environment Block)
  3. Jan 3, 2021/archiveWindows PEB (Process Environment Block)

graphfeed