Введение
Обработчики исключений организованы в односвязный список, каждый элемент которого (т.е. каждый обработчик) связан с неким потоком. Как правило, для узлов списка выделяется место в стеке.
Начало списка ссылается указателем который размещен в начале TEB (Thread Environment Block - структура которая используется для хранения информации о потоках в текущем процессе, каждый поток имеет свой TEB), поэтому, если мы хотим добавить новый обработчик исключений, новый узел будет добавлен в начало списка и указатель в TEB будет ссылаться на новый узел. Каждый узел имеет тип _EXCEPTION_REGISTRATION_RECORD, который содержит в себе адрес обработчика и указатель на следующий узел в списке. Довольно странное поведение указателя на следующий элемент списка, он не указывает на NULL как в большинстве случаев а равен 0xffffffff.
Вот точное определение структуры:
0:000> dt _EXCEPTION_REGISTRATION_RECORD
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
К TEB мы можем получить доступ через селектор fs начиная с fs:[0], так что не удивляйтесь видя в коде следующее:
mov eax, dword ptr fs:[00000000h] ; получить начало списка
push eax ; сохранить старое начало списка
lea eax, [ebp-10h]
mov dword ptr fs:[00000000h], eax ; установить новое начало списка
.
.
.
mov ecx, dword ptr [ebp-10h] ; получить старое начало списка (поле NEXT текущего начала списка)
mov dword ptr fs:[00000000h], ecx ; восстановить старое начало списка
Компиляторы обычно регистрируют один глобальный обработчик который знает о том, какой участок памяти в данный момент исполняется (опираясь на глобальную переменную).
Поскольку каждый поток имеет свой TEB, операционная система гарантирует что сегмент выбранный регистром fs ссылается всегда на правильный TEB (т.е. одного текущего процесса). Что бы получить адрес TEB надо прочитать fs:[18h] который соответствует области Self структуры TEB.
Давайте выведем TEB:
0:000> !teb
TEB at 7efdd000
ExceptionList: 003ef804 <-----------------------
StackBase: 003f0000
StackLimit: 003ed000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7efdd000
EnvironmentPointer: 00000000
ClientId: 00001644 . 00000914
RpcHandle: 00000000
Tls Storage: 7efdd02c
PEB Address: 7efde000
LastErrorValue: 2
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
Теперь проверим ссылается ли fs на TEB:
0:000> dg fs
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0053 7efdd000 00000fff Data RW Ac 3 Bg By P Nl 000004f3
Как было сказано ранее, fs:18h содержит адрес TEB:
0:000> ? poi(fs:[18])
Evaluate expression: 2130563072 = 7efdd000
Вспомните что poi разыменовывает указатель, а «?» используется для вычисления выражения.
Давайте посмотрим какое имя у структуры на которую указывает ExceptionList :
0:000> dt nt!_NT_TIB ExceptionList
ntdll!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
Это значит то, что каждый узел это экземпляр _EXCEPTION_REGISTRATION_RECORD, как и было ранее упомянуто.
Для вывода всего списка используйте !slist:
0:000> !slist $teb _EXCEPTION_REGISTRATION_RECORD
SLIST HEADER:
+0x000 Alignment : 3f0000003ef804
+0x000 Next : 3ef804
+0x004 Depth : 0
+0x006 Sequence : 3f
SLIST CONTENTS:
003ef804
+0x000 Next : 0x003ef850 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x6d5da0d5 _EXCEPTION_DISPOSITION MSVCR120!_except_handler4+0
003ef850
+0x000 Next : 0x003ef89c _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x00271709 _EXCEPTION_DISPOSITION +0
003ef89c
+0x000 Next : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x77e21985 _EXCEPTION_DISPOSITION ntdll!_except_handler4+0
ffffffff
+0x000 Next : ????
+0x004 Handler : ????
Can't read memory at ffffffff, error 0
Помните что $teb это адрес TEB.
Простой способ вывести цепочку обработчиков исключений:
0:000> !exchain
003ef804: MSVCR120!_except_handler4+0 (6d5da0d5)
CRT scope 0, func: MSVCR120!doexit+116 (6d613b3b)
003ef850: exploitme3+1709 (00271709)
003ef89c: ntdll!_except_handler4+0 (77e21985)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+2e (77e21c78)
func: ntdll!__RtlUserThreadStart+63 (77e238cb)
Мы можем также изучить цепочку обработчиков исключений вручную:
0:000> dt 003ef804 _EXCEPTION_REGISTRATION_RECORD
MSVCR120!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0x003ef850 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x6d5da0d5 _EXCEPTION_DISPOSITION MSVCR120!_except_handler4+0
0:000> dt 0x003ef850 _EXCEPTION_REGISTRATION_RECORD
MSVCR120!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0x003ef89c _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x00271709 _EXCEPTION_DISPOSITION +0
0:000> dt 0x003ef89c _EXCEPTION_REGISTRATION_RECORD
MSVCR120!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x77e21985 _EXCEPTION_DISPOSITION ntdll!_except_handler4+0