🥖
Windows SEH (Structured Exception Handler) 1
January 03, 2021
SEH
exception handlers
는 각 Thread와 관련된Singly-linked list
구성된다.- 원칙적으로 해당 목록의 노드는 stack에 할당된다.
- 목록의 Head는 TEB(Thred Environment Block)의 시작 부분에 있는 포인터로 가리키므로 코드가 새 예외처리기를 추가하려는 경우 새 노드가 목록의 헤드와 포인터에 추가된다.
- TEB에서 새 노드를 가리키도록 변경된다.
- 각 노드는
_EXCEPTION_REGISTRATION_RECORD
유형이며 핸들러의 주소와 목록의 다음 노드에 대한 포인터를 저장한다. - 이상하게도 목록의 마지막 노드의 “next pointer” 는 NULL이 아니지만
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
- TEB는 FS:[0] 부터 시작하는
selector
FS를 통해서도 액세스 할 수 있으므로 다음과 같은 코드르 보는 것이 일반적이다.
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
-
컴파일러는 일반적으로 프로그램의 어느 영역이 실행되고 있는지 (전역 변수에 의존) 알고 호출될 될 때 그에 따라 동작하는 단일 전역 처리기를 등록한다.
-
각 스레드에는 다른
TEB
가 있으므로 운영 체제는FS
에 의해 선택된 세그먼트가 항상 올바른 TEB(즉, 현재 스레드 중 하나)를 참조하는지 확인한다. -
TEB의 주소를 얻을려면 TEB의 Self 필드에 해당하는
FS:[18h]
를 읽어야 한다. -
TEB를 확인해 보자
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
FS 세그먼트가 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] 에는 TEB의 주소가 포함되어 있다.
0:003> ?poi(fs:[18])
Evaluate expression: 12144640 = 00b95000
- ExceptionList가 가리키는 Structure를 확인해보도록 하겠다.
0:003> dt nt!_NT_TIB ExceptionList
ntdll!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
- 각 노드는 _EXCPETION_REGISTRATION_RECORD의 Instance이다. 전체 목록을 확인하고 싶으면
!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
는 TEB의 주소이다. -
SEH chain을 표시하는 더 간단한 방법은 다음을 사용하는 것이다.
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
- SEH chain을 수동으로 검사 할 수 있다.
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