Вначале в стек кладется -1 (как оказалось
впоследствии, просто резервируется место в стеке), а затем в стек записывается
адрес статической переменной и адрес обработчика исключения. Если присмотреться
к последним трем инструкциям, то можно увидеть, что из памяти по адресу fs:[0]
считывается какое-то число, и кладется в стек, а на его место заносится текущий
указатель стека. В принципе ничего подозрительного тут нет, но если расположить
эти три инструкции последовательно несколько раз, то станет заметно, что они
формируют связанный список, причем первый элемент этого списка всегда указывает
на предыдущий элемент. На выходе из функции находится код, который
восстанавливает предыдущее значение переменной по адресу fs:[0] :
00411E3B mov ecx,dword ptr [ebp-10h]
00411E3E mov dword ptr fs:[0],ecx
|
Таким образом, если функция имеет в себе конструкцию
__try … __except, то компилятор создает в стеке запись о новом обработчике
исключений и помещает информацию о ней в список обработчиков. Придя к такому
выводу, я начал искать хоть какую-то информацию об обработчиках исключений и
нашел публикацию, написанную Matt Pietrek-ом 7 лет назад (A Crash Course on the Depths
of Win32 Structured Exception Handling). В
этой статье описана структура SEH, и подтверждаются выводы, сделанные путем
анализа кода приведенной выше функции. Изучив эту статью и проверив написанное
в ней, я обнаружил, что с тех пор в области обработки исключений практически
ничего не изменилось.
Из статьи следует, что по адресу fs:[0], находится
начало связанного списка зарегистрированных обработчиков исключения, элементами
которого являются структуры типа _EXCEPTION_REGISTRATION, расположенные в
стеке.
struct _EXCEPTION_REGISTRATION
{
// указатель на следующую запись
_EXCEPTION_REGISTRATION *prev;
// обработчик исключения, созданный Runtime библиотекой
SEHHandler handler;
// указатель на структуру, описывающий блок
__try…__except
PSCOPETABLE scopetable;
// уровень вложенности текущего блока try
int trylevel;
// указатель на следующую запись
int _ebp;
};
|
В этой структуре handler является процедурой обработки
исключения. Прототип этой функции приведен ниже:
typedef
int (*SEHHandler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, void*);
|
Как видите, функция обработчика исключения принимает 4
параметра. Первый параметр имеет тип PEXCEPTION_RECORD – это указатель на
структуру, содержащую информацию об исключении. Вы можете найти объявление этой
структуры в заголовочном файле winnt.h: