Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Обработка критических ошибок на Ассемблере
Обработка критических ошибок на Ассемблере

Как гласит один из законов Мерфи для программистов, в каждой программе есть ошибки. Какими бы суровыми ни были тесты, с их помощью невозможно доказать полное отсутствие ошибок. Всегда остается вероятность возникновения ситуации, не охваченной тестовой средой. В таких случаях для разработчиков и тестеров важно получить подробную информацию об ошибке, чтобы попытаться воспроизвести ее и устранить. Делается это различными способами, в том числе и при помощи обработчиков критических ошибок.

Есть разные подходы к обработке ошибок: подавление или игнорирование, корректировка "на лету" по возможности, фатальное завершение работы. Лично я придерживаюсь мнения, что при возникновении любой ошибки, даже самой незначительной, приложение должно сохранять информацию об этом куда-нибудь в лог, сообщать пользователю и завершать работу. Естественно, разработчик должен быть немедленно уведомлен о произошедшем, ему предоставляется сохраненный лог с подробнейшим описанием ситуации, приведшей к падению приложения. После этого в код добавляются все необходимые проверки, исключающие причину и повторное появление ошибки.

Есть вариант внедрения своего обработчика напрямую в цепочку SEH, но мне такой вариант не нравится, он больше подходит для какой-нибудь антиотладки или запутывания алгоритма работы. Для нормальных приложений, на мой взгляд, больше подходит штатная функция SetUnhandledExceptionFilter. Но для начала придется самостоятельно описать недостающие структуры для FASM.
  1. EXCEPTION_MAXIMUM_PARAMETERS = 15
  2. SIZE_OF_80387_REGISTERS      = 80
  3. MAXIMUM_SUPPORTED_EXTENSION  = 512
  4. EXCEPTION_CONTINUE_EXECUTION = -1
  5.  
  6. struct FLOATING_SAVE_AREA
  7.   ControlWord          dd ?
  8.   StatusWord           dd ?
  9.   TagWord              dd ?
  10.   ErrorOffset          dd ?
  11.   ErrorSelector        dd ?
  12.   DataOffset           dd ?
  13.   DataSelector         dd ?
  14.   RegisterArea         rb SIZE_OF_80387_REGISTERS
  15.   Cr0NpxState          dd ?
  16. ends
  17.  
  18. struct CONTEXT
  19.   ContextFlags         dd ?
  20.   iDr0                 dd ?
  21.   iDr1                 dd ?
  22.   iDr2                 dd ?
  23.   iDr3                 dd ?
  24.   iDr6                 dd ?
  25.   iDr7                 dd ?
  26.   FloatSave            FLOATING_SAVE_AREA
  27.   regGs                dd ?
  28.   regFs                dd ?
  29.   regEs                dd ?
  30.   regDs                dd ?
  31.   regEdi               dd ?
  32.   regEsi               dd ?
  33.   regEbx               dd ?
  34.   regEdx               dd ?
  35.   regEcx               dd ?
  36.   regEax               dd ?
  37.   regEbp               dd ?
  38.   regEip               dd ?
  39.   regCs                dd ?
  40.   regFlag              dd ?
  41.   regEsp               dd ?
  42.   regSs                dd ?
  43.   ExtendedRegisters    rb MAXIMUM_SUPPORTED_EXTENSION
  44. ends
  45.  
  46. struct EXCEPTION_RECORD
  47.   ExceptionCode        dd ?
  48.   ExceptionFlags       dd ?
  49.   pExceptionRecord     dd ?
  50.   ExceptionAddress     dd ?
  51.   NumberParameters     dd ?
  52.   ExceptionInformation rd EXCEPTION_MAXIMUM_PARAMETERS
  53. ends
  54.  
  55. struct EXCEPTION_POINTERS
  56.   pExceptionRecord     dd ?
  57.   pContextRecord       dd ?
  58. ends
Установка обработчика ошибок делается в самом начале критических секций приложения. Ведь чем раньше он будет установлен, тем больше кода сможет охватить.
  1. ; Установить обработчик ошибок
  2. invoke  SetUnhandledExceptionFilter,ExceptionFilter
Теперь сам обработчик ошибок. Я постарался сделать его максимально независимым от приложения, вы можете даже описания структур внести внутрь процедуры, чтобы было удобнее подключать его в различные проекты. Ошибки записываются в файл, соответствующий имени исполняемого файла с суффиксом "_errors.log", если такой файл уже есть, то новые сообщения дописываются в конец. После записи пользователю выводится уведомление о произошедшей ошибке и приложение завершает работу.
  1. ;-------------------------------------------------------------------------
  2. ; Обработчик критических ошибок
  3. ;-------------------------------------------------------------------------
  4. proc  ExceptionFilter lpExcept:DWORD
  5.         locals
  6.             szFile   rb MAX_PATH
  7.             szBuffer rb 500h
  8.         endl
  9.  
  10.         mov     eax,[lpExcept]
  11.  
  12.         ; ESI -> EXCEPTION_RECORD
  13.         mov     esi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
  14.         ; EDI -> CONTEXT
  15.         mov     edi,[eax+EXCEPTION_POINTERS.pContextRecord]
  16.  
  17.         ; ReadWrite
  18.         mov     ecx,[esi+EXCEPTION_RECORD.ExceptionInformation]
  19.         cmp     ecx,2
  20.         jb      @f
  21.         mov     ecx,2
  22. @@:
  23.         mov     ecx,[.szOperation+ecx*4]
  24.  
  25.         ; Continuable
  26.         mov     edx,[esi+EXCEPTION_RECORD.ExceptionFlags]
  27.         mov     edx,[.szLogical+edx*4]
  28.  
  29.         ; Сформировать текст исключения
  30.         lea     ebx,[szBuffer]
  31.         cinvoke wsprintf,ebx,.szMask,\
  32.                 [esi+EXCEPTION_RECORD.ExceptionAddress],\
  33.                 [esi+EXCEPTION_RECORD.ExceptionCode],\
  34.                 edx,[esi+EXCEPTION_RECORD.NumberParameters],ecx,\
  35.                 [edi+CONTEXT.regEax],[edi+CONTEXT.regEbx],\
  36.                 [edi+CONTEXT.regEcx],[edi+CONTEXT.regEdx],\
  37.                 [edi+CONTEXT.regEsp],[edi+CONTEXT.regEbp],\
  38.                 [edi+CONTEXT.regEsi],[edi+CONTEXT.regEdi]
  39.  
  40.         ; Сформировать имя файла для логирования ошибок
  41.         lea     ebx,[szFile]
  42.         invoke  GetModuleHandle,NULL
  43.         invoke  GetModuleFileName,eax,ebx,MAX_PATH
  44.         invoke  lstrcat,ebx,.szTail
  45.  
  46.         ; Поптытаться создать файл
  47.         invoke  CreateFile,ebx,GENERIC_WRITE,FILE_SHARE_READ,\
  48.                 NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
  49.         cmp     eax,-1
  50.         je      @f
  51.         mov     ebx,eax
  52.  
  53.         ; Указатель на сформированный текст исключения
  54.         lea     esi,[szBuffer]
  55.  
  56.         ; Дописать текст в конец файла лога
  57.         invoke  SetFilePointer,ebx,0,0,FILE_END
  58.         invoke  lstrlen,esi
  59.         invoke  _lwrite,ebx,esi,eax
  60.         invoke  CloseHandle,ebx
  61. @@:
  62.         ; Сообщение пользователю о возникновении ошибки
  63.         invoke  MessageBox,0,esi,.szTitle,\
  64.                 MB_OK+MB_ICONHAND+MB_APPLMODAL+MB_TOPMOST
  65.  
  66.         invoke  ExitProcess,0
  67.  
  68. .szTail      db '_errors.log',0
  69.  
  70. .szLogical   dd .szFalse,.szTrue
  71. .szFalse     db 'false',0
  72. .szTrue      db 'true',0
  73.  
  74. .szOperation dd .szRead,.szWrite,.szOther
  75. .szRead      db 'read',0
  76. .szWrite     db 'write',0
  77. .szOther     db 'other',0
  78.  
  79. .szTitle     db 'Critical error',0
  80.  
  81. .szMask      db 'Exception addr: %08Xh',13,10,'Exception type: %08Xh'
  82.              db 13,10,13,10,'Information:',13,10
  83.              db 'Continuable = %s, NumberParameters = %u, ReadWrite = %s'
  84.              db 13,10,13,10,'Registers:',13,10
  85.              db 'eax = %08Xh, ebx = %08Xh, ecx = %08Xh, edx = %08Xh',13,10
  86.              db 'esp = %08Xh, ebp = %08Xh, esi = %08Xh, edi = %08Xh',13,10
  87.              db 13,10,0
  88. endp
Если нужно, вы можете подкорректировать обработчик по своему усмотрению. Например, вместо принудительного завершения работы попробовать сохранить какие-нибудь данные, или расширить информацию для записи в лог, или еще что-то, все зависит от задачи.

Пример перехвата ошибки
Пример перехвата ошибки

В приложении пример программы с исходным текстом, в которой и есть возможность вызвать несколько видов ошибок и реализуется их перехват при помощи обработчика из статьи.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

SEH.Demo.zip (7,497 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 1356 | Комментариев: 3

Метки: Assembler
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (09.07.2017 в 20:57):
Разобрался. Чтобы работало под x64, надо устанавливать обработчик непосредственно в начале критических мест программы и только через встраивание в цепочку seh. Один глобальный перехватчик для всего процесса, как было для x86, не прокатит. Примеры обновил, в качестве бонуса добавил пример установки обработчика через встраивание в цепочку seh.
pawel97, спасибо!
ManHunter (08.07.2017 в 23:18):
На x86 все работает. На x64 да, не срабатывает. Обязательно изучу вопрос, тогда допишу статью.
Вот, кстати, официоз: https://support.microsoft.com/...n-a-64-bit-v
pawel97 (08.07.2017 в 22:35):
Что-то не работает демо пример. В оле поставил бряк на ExceptionFilter, жму кнопки - а оно не срабатывает. Пора переставлять винду? :)

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2018
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0042 сек.) / Память: 4.75 Mb
Наверх