
Обработка критических ошибок на Ассемблере

Обработка критических ошибок на Ассемблере
Как гласит один из законов Мерфи для программистов, в каждой программе есть ошибки. Какими бы суровыми ни были тесты, с их помощью невозможно доказать полное отсутствие ошибок. Всегда остается вероятность возникновения ситуации, не охваченной тестовой средой. В таких случаях для разработчиков и тестеров важно получить подробную информацию об ошибке, чтобы попытаться воспроизвести ее и устранить. Делается это различными способами, в том числе и при помощи обработчиков критических ошибок.
Есть разные подходы к обработке ошибок: подавление или игнорирование, корректировка "на лету" по возможности, фатальное завершение работы. Лично я придерживаюсь мнения, что при возникновении любой ошибки, даже самой незначительной, приложение должно сохранять информацию об этом куда-нибудь в лог, сообщать пользователю и завершать работу. Естественно, разработчик должен быть немедленно уведомлен о произошедшем, ему предоставляется сохраненный лог с подробнейшим описанием ситуации, приведшей к падению приложения. После этого в код добавляются все необходимые проверки, исключающие причину и повторное появление ошибки.
Есть вариант внедрения своего обработчика напрямую в цепочку SEH, но мне такой вариант не нравится, он больше подходит для какой-нибудь антиотладки или запутывания алгоритма работы. Для нормальных приложений, на мой взгляд, больше подходит штатная функция SetUnhandledExceptionFilter. Но для начала придется самостоятельно описать недостающие структуры для FASM.
Code (Assembler) : Убрать нумерацию
- EXCEPTION_MAXIMUM_PARAMETERS = 15
- SIZE_OF_80387_REGISTERS = 80
- MAXIMUM_SUPPORTED_EXTENSION = 512
- EXCEPTION_CONTINUE_EXECUTION = -1
- struct FLOATING_SAVE_AREA
- ControlWord dd ?
- StatusWord dd ?
- TagWord dd ?
- ErrorOffset dd ?
- ErrorSelector dd ?
- DataOffset dd ?
- DataSelector dd ?
- RegisterArea rb SIZE_OF_80387_REGISTERS
- Cr0NpxState dd ?
- ends
- struct CONTEXT
- ContextFlags dd ?
- iDr0 dd ?
- iDr1 dd ?
- iDr2 dd ?
- iDr3 dd ?
- iDr6 dd ?
- iDr7 dd ?
- FloatSave FLOATING_SAVE_AREA
- regGs dd ?
- regFs dd ?
- regEs dd ?
- regDs dd ?
- regEdi dd ?
- regEsi dd ?
- regEbx dd ?
- regEdx dd ?
- regEcx dd ?
- regEax dd ?
- regEbp dd ?
- regEip dd ?
- regCs dd ?
- regFlag dd ?
- regEsp dd ?
- regSs dd ?
- ExtendedRegisters rb MAXIMUM_SUPPORTED_EXTENSION
- ends
- struct EXCEPTION_RECORD
- ExceptionCode dd ?
- ExceptionFlags dd ?
- pExceptionRecord dd ?
- ExceptionAddress dd ?
- NumberParameters dd ?
- ExceptionInformation rd EXCEPTION_MAXIMUM_PARAMETERS
- ends
- struct EXCEPTION_POINTERS
- pExceptionRecord dd ?
- pContextRecord dd ?
- ends
Code (Assembler) : Убрать нумерацию
- ; Установить обработчик ошибок
- invoke SetUnhandledExceptionFilter,ExceptionFilter
Code (Assembler) : Убрать нумерацию
- ;-------------------------------------------------------------------------
- ; Обработчик критических ошибок
- ;-------------------------------------------------------------------------
- proc ExceptionFilter lpExcept:DWORD
- locals
- szFile rb MAX_PATH
- szBuffer rb 500h
- endl
- mov eax,[lpExcept]
- ; ESI -> EXCEPTION_RECORD
- mov esi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
- ; EDI -> CONTEXT
- mov edi,[eax+EXCEPTION_POINTERS.pContextRecord]
- ; ReadWrite
- mov ecx,[esi+EXCEPTION_RECORD.ExceptionInformation]
- cmp ecx,2
- jb @f
- mov ecx,2
- @@:
- mov ecx,[.szOperation+ecx*4]
- ; Continuable
- mov edx,[esi+EXCEPTION_RECORD.ExceptionFlags]
- mov edx,[.szLogical+edx*4]
- ; Сформировать текст исключения
- lea ebx,[szBuffer]
- cinvoke wsprintf,ebx,.szMask,\
- [esi+EXCEPTION_RECORD.ExceptionAddress],\
- [esi+EXCEPTION_RECORD.ExceptionCode],\
- edx,[esi+EXCEPTION_RECORD.NumberParameters],ecx,\
- [edi+CONTEXT.regEax],[edi+CONTEXT.regEbx],\
- [edi+CONTEXT.regEcx],[edi+CONTEXT.regEdx],\
- [edi+CONTEXT.regEsp],[edi+CONTEXT.regEbp],\
- [edi+CONTEXT.regEsi],[edi+CONTEXT.regEdi]
- ; Сформировать имя файла для логирования ошибок
- lea ebx,[szFile]
- invoke GetModuleHandle,NULL
- invoke GetModuleFileName,eax,ebx,MAX_PATH
- invoke lstrcat,ebx,.szTail
- ; Поптытаться создать файл
- invoke CreateFile,ebx,GENERIC_WRITE,FILE_SHARE_READ,\
- NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
- cmp eax,-1
- je @f
- mov ebx,eax
- ; Указатель на сформированный текст исключения
- lea esi,[szBuffer]
- ; Дописать текст в конец файла лога
- invoke SetFilePointer,ebx,0,0,FILE_END
- invoke lstrlen,esi
- invoke _lwrite,ebx,esi,eax
- invoke CloseHandle,ebx
- @@:
- ; Сообщение пользователю о возникновении ошибки
- invoke MessageBox,0,esi,.szTitle,\
- MB_OK+MB_ICONHAND+MB_APPLMODAL+MB_TOPMOST
- invoke ExitProcess,0
- .szTail db '_errors.log',0
- .szLogical dd .szFalse,.szTrue
- .szFalse db 'false',0
- .szTrue db 'true',0
- .szOperation dd .szRead,.szWrite,.szOther
- .szRead db 'read',0
- .szWrite db 'write',0
- .szOther db 'other',0
- .szTitle db 'Critical error',0
- .szMask db 'Exception addr: %08Xh',13,10,'Exception type: %08Xh'
- db 13,10,13,10,'Information:',13,10
- db 'Continuable = %s, NumberParameters = %u, ReadWrite = %s'
- db 13,10,13,10,'Registers:',13,10
- db 'eax = %08Xh, ebx = %08Xh, ecx = %08Xh, edx = %08Xh',13,10
- db 'esp = %08Xh, ebp = %08Xh, esi = %08Xh, edi = %08Xh',13,10
- db 13,10,0
- endp

Пример перехвата ошибки
В приложении пример программы с исходным текстом, в которой и есть возможность вызвать несколько видов ошибок и реализуется их перехват при помощи обработчика из статьи.
Просмотров: 1933 | Комментариев: 3
Метки: Assembler

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

ManHunter
(08.07.2017 в 23:18):
На x86 все работает. На x64 да, не срабатывает. Обязательно изучу вопрос, тогда допишу статью.
Вот, кстати, официоз: https://support.microsoft.com/...n-a-64-bit-v
Вот, кстати, официоз: https://support.microsoft.com/...n-a-64-bit-v

pawel97
(08.07.2017 в 22:35):
Что-то не работает демо пример. В оле поставил бряк на ExceptionFilter, жму кнопки - а оно не срабатывает. Пора переставлять винду? :)

Добавить комментарий
Заполните форму для добавления комментария

pawel97, спасибо!