Blog. Just Blog

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

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

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

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

Одним из методов перехвата возникших ошибок является SEH (Structured Exception Handling). При возникновении критической ситуации, например, обращении к памяти по неправильному адресу, переполнении стека, делении на ноль и тому подобное будет задействована цепочка обработчиков SEH. Каждый из обработчиков поочередно может выполнить нужные действия и прервать дальнейшую обработку или проигнорировать ошибку и передать ее следующему обработчику. Для каждого потока в процессе должны быть назначены свои обработчики SEH.

Есть вариант внедрения своего обработчика напрямую в цепочку 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
Нравится, не нравится, а, как показала практика, придется рассказать и про встраивание обработчика напрямую в цепочку. Потому что обработать исключения в 32-битной программе, запущенной под 64-битной системой, получится только так. Единый глобальный обработчик в этом случае не поможет. Реализуется встраивание следующим образом:
  1.         ; Добавить наш обработчик в цепочку
  2.         push    ExceptionFilter
  3.         push    dword [fs:0]
  4.         mov     [fs:0],esp
  5.  
  6.         ; Критичный участок кода
  7.         ...
  8.         ...
  9.  
  10.         ; Убрать наш обработчик
  11.         pop     dword[fs:0]
  12.         add     esp, 4
Теперь сам обработчик ошибок. Я постарался сделать его максимально независимым от приложения, вы можете даже описания структур внести внутрь процедуры, чтобы было удобнее подключать его в различные проекты. Ошибки записываются в файл, соответствующий имени исполняемого файла с суффиксом "_errors.log", если такой файл уже есть, то новые сообщения дописываются в конец. После записи пользователю выводится уведомление о произошедшей ошибке и приложение завершает работу.

Глобальный (последний) обработчик исключений (Unhandled Exception Filter). Получает только один параметр - указатель на структуру EXCEPTION_POINTERS.
  1. ;---------------------------------------------------
  2. ; Обработчик критических ошибок (ASCII)
  3. ;---------------------------------------------------
  4. proc  ExceptionFilter lpExcept:DWORD
  5.         locals
  6.             szFile   rb MAX_PATH
  7.             szBuffer rb 1000h
  8.         endl
  9.  
  10.         push    edi
  11.         mov     eax,[lpExcept]
  12.         ; EDI -> EXCEPTION_RECORD
  13.         mov     edi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
  14.         ; DBG_PRINTEXCEPTION_WIDE_C
  15.         cmp     [edi+EXCEPTION_RECORD.ExceptionCode],4001000Ah
  16.         je      .loc_skip_exept
  17.         ; DBG_PRINTEXCEPTION_C
  18.         cmp     [edi+EXCEPTION_RECORD.ExceptionCode],40010006h
  19.         je      .loc_skip_exept
  20.         jmp     @f
  21.  
  22. .loc_skip_exept:
  23.         pop     edi
  24.         mov     eax,-1
  25.         ret
  26. @@:
  27.         pop     edi
  28.  
  29.         ; EDI -> CONTEXT
  30.         mov     edi,[eax+EXCEPTION_POINTERS.pContextRecord]
  31.         push    [edi+CONTEXT.regEdi]
  32.         push    [edi+CONTEXT.regEsi]
  33.         push    [edi+CONTEXT.regEbp]
  34.         push    [edi+CONTEXT.regEsp]
  35.         push    [edi+CONTEXT.regEdx]
  36.         push    [edi+CONTEXT.regEcx]
  37.         push    [edi+CONTEXT.regEbx]
  38.         push    [edi+CONTEXT.regEax]
  39.  
  40.         ; EDI -> EXCEPTION_RECORD
  41.         mov     edi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
  42.  
  43.         ; ReadWrite
  44.         mov     eax,[edi+EXCEPTION_RECORD.ExceptionInformation]
  45.         cmp     eax,2
  46.         jb      @f
  47.         mov     eax,2
  48. @@:
  49.         push    [.szOperation+eax*4]
  50.  
  51.         ; NumberParameters
  52.         push    [edi+EXCEPTION_RECORD.NumberParameters]
  53.  
  54.         ; Continuable
  55.         mov     eax,[edi+EXCEPTION_RECORD.ExceptionFlags]
  56.         push    [.szLogical+eax*4]
  57.  
  58.         ; Type
  59.         mov     esi,.szType
  60. @@:
  61.         lodsd
  62.         or      eax,eax
  63.         jz      @f
  64.         cmp     eax,[edi+EXCEPTION_RECORD.ExceptionCode]
  65.         je      @f
  66.         lodsd
  67.         jmp     @b
  68. @@:
  69.         push    dword [esi]
  70.  
  71.         ; ExceptionCode
  72.         push    [edi+EXCEPTION_RECORD.ExceptionCode]
  73.  
  74.         ; ExceptionAddress
  75.         push    [edi+EXCEPTION_RECORD.ExceptionAddress]
  76.  
  77.         ; Сформировать текст исключения
  78.         lea     ebx,[szBuffer]
  79.         invoke  wsprintf,ebx,.szMask
  80.         add     esp,40h
  81.  
  82.         ; Сформировать имя файла для логирования ошибок
  83.         lea     ebx,[szFile]
  84.         invoke  GetModuleHandle,NULL
  85.         invoke  GetModuleFileName,eax,ebx,MAX_PATH
  86.         invoke  lstrcat,ebx,.szTail
  87.  
  88.         ; Попытаться создать файл
  89.         invoke  CreateFile,ebx,GENERIC_WRITE,FILE_SHARE_READ,\
  90.                 NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
  91.         cmp     eax,-1
  92.         je      @f
  93.         mov     ebx,eax
  94.  
  95.         ; Дописать текст в конец файла лога
  96.         invoke  SetFilePointer,ebx,0,0,FILE_END
  97.         or      eax,eax
  98.         jz      .loc_write_data
  99.  
  100.         invoke  lstrlen,.divider
  101.         invoke  _lwrite,ebx,.divider,eax
  102.  
  103. .loc_write_data:
  104.         ; Указатель на сформированный текст исключения
  105.         lea     esi,[szBuffer]
  106.         invoke  lstrlen,esi
  107.         invoke  _lwrite,ebx,esi,eax
  108.         invoke  CloseHandle,ebx
  109. @@:
  110.         ; Сообщение пользователю о возникновении ошибки
  111.         invoke  MessageBox,0,esi,.szTitle,\
  112.                 MB_OK+MB_ICONHAND+MB_APPLMODAL+MB_TOPMOST
  113.  
  114.         invoke  ExitProcess,0
  115.  
  116. .szTail      db '_errors.log',0
  117.  
  118. .divider     db '------------------------------------'
  119.              db '------------------------',13,10,13,10,0
  120.  
  121. .szLogical   dd .szFalse,.szTrue
  122. .szFalse     db 'false',0
  123. .szTrue      db 'true',0
  124.  
  125. .szOperation dd .szRead,.szWrite,.szOther
  126. .szRead      db 'read',0
  127. .szWrite     db 'write',0
  128. .szOther     db 'other',0
  129.  
  130. .szType      dd 0xC0000005,.stat1
  131.              dd 0xC000008C,.stat2
  132.              dd 0x80000003,.stat3
  133.              dd 0x80000002,.stat4
  134.              dd 0xC000008D,.stat5
  135.              dd 0xC000008E,.stat6
  136.              dd 0xC000008F,.stat7
  137.              dd 0xC0000090,.stat8
  138.              dd 0xC0000091,.stat9
  139.              dd 0xC0000092,.stat10
  140.              dd 0xC0000093,.stat11
  141.              dd 0x80000001,.stat12
  142.              dd 0xC000001D,.stat13
  143.              dd 0xC0000006,.stat14
  144.              dd 0xC0000094,.stat15
  145.              dd 0xC0000095,.stat16
  146.              dd 0xC0000026,.stat17
  147.              dd 0xC0000008,.stat18
  148.              dd 0xC0000025,.stat19
  149.              dd 0xC0000096,.stat20
  150.              dd 0x80000004,.stat21
  151.              dd 0xC00000FD,.stat22
  152.              dd 0x80000029,.stat23
  153.              dd 0xC000009A,.stat24
  154.              dd 0xC000009B,.stat25
  155.              dd 0,.sUnk
  156.  
  157. .stat1       db 'ACCESS VIOLATION',0
  158. .stat2       db 'ARRAY BOUNDS EXCEEDED',0
  159. .stat3       db 'BREAKPOINT',0
  160. .stat4       db 'DATATYPE MISALIGNMENT',0
  161. .stat5       db 'FLOAT DENORMAL OPERAND',0
  162. .stat6       db 'FLOAT DIVIDE BY ZERO',0
  163. .stat7       db 'FLOAT INEXACT RESULT',0
  164. .stat8       db 'FLOAT INVALID OPERATION',0
  165. .stat9       db 'FLOAT OVERFLOW',0
  166. .stat10      db 'FLOAT STACK CHECK',0
  167. .stat11      db 'FLOAT UNDERFLOW',0
  168. .stat12      db 'GUARD PAGE VIOLATION',0
  169. .stat13      db 'ILLEGAL INSTRUCTION',0
  170. .stat14      db 'IN PAGE ERROR',0
  171. .stat15      db 'INTEGER DIVIDE BY ZERO',0
  172. .stat16      db 'INTEGER OVERFLOW',0
  173. .stat17      db 'INVALID DISPOSITION',0
  174. .stat18      db 'INVALID HANDLE',0
  175. .stat19      db 'NONCONTINUABLE EXCEPTION',0
  176. .stat20      db 'PRIVILEGED_INSTRUCTION',0
  177. .stat21      db 'SINGLE STEP',0
  178. .stat22      db 'STACK OVERFLOW',0
  179. .stat23      db 'UNWIND CONSOLIDATE',0
  180. .stat24      db 'FLOAT_MULTIPLE_FAULTS',0
  181. .stat25      db 'FLOAT_MULTIPLE_TRAPS',0
  182. .sUnk        db 'UNKNOWN',0
  183.  
  184. .szTitle     db 'Critical error',0
  185. .szMask      db 'Exception addr: %08Xh',13,10
  186.              db 'Exception code: %08Xh = %s',13,10,13,10
  187.              db 'Information:',13,10
  188.              db 'Continuable = %s, NumberParameters = %u, '
  189.              db 'ReadWrite = %s',13,10,13,10
  190.              db 'Registers:',13,10
  191.              db 'EAX=%08Xh, EBX=%08Xh, ECX=%08Xh, EDX=%08Xh',13,10
  192.              db 'ESP=%08Xh, EBP=%08Xh, ESI=%08Xh, EDI=%08Xh',13,10,13,10
  193.              db 0
  194. endp
Вариант обработчика для юникодных программ. Не сильно, но отличается. Если файл только что создан (указатель в начале), пишется BOM. Если файл уже существовал, сначала пишется разделитель, потом текст исключения.
  1. ;--------------------------------------------------
  2. ; Обработчик критических ошибок (UNICODE)
  3. ;--------------------------------------------------
  4. proc  ExceptionFilter lpExcept:DWORD
  5.         locals
  6.             szFile   rw MAX_PATH
  7.             szBuffer rw 1000h
  8.         endl
  9.  
  10.         push    edi
  11.         mov     eax,[lpExcept]
  12.         ; EDI -> EXCEPTION_RECORD
  13.         mov     edi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
  14.         ; DBG_PRINTEXCEPTION_WIDE_C
  15.         cmp     [edi+EXCEPTION_RECORD.ExceptionCode],4001000Ah
  16.         je      .loc_skip_exept
  17.         ; DBG_PRINTEXCEPTION_C
  18.         cmp     [edi+EXCEPTION_RECORD.ExceptionCode],40010006h
  19.         je      .loc_skip_exept
  20.         jmp     @f
  21.  
  22. .loc_skip_exept:
  23.         pop     edi
  24.         mov     eax,-1
  25.         ret
  26. @@:
  27.         pop     edi
  28.  
  29.         ; EDI -> CONTEXT
  30.         mov     edi,[eax+EXCEPTION_POINTERS.pContextRecord]
  31.         push    [edi+CONTEXT.regEdi]
  32.         push    [edi+CONTEXT.regEsi]
  33.         push    [edi+CONTEXT.regEbp]
  34.         push    [edi+CONTEXT.regEsp]
  35.         push    [edi+CONTEXT.regEdx]
  36.         push    [edi+CONTEXT.regEcx]
  37.         push    [edi+CONTEXT.regEbx]
  38.         push    [edi+CONTEXT.regEax]
  39.  
  40.         ; EDI -> EXCEPTION_RECORD
  41.         mov     edi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
  42.  
  43.         ; ReadWrite
  44.         mov     eax,[edi+EXCEPTION_RECORD.ExceptionInformation]
  45.         cmp     eax,2
  46.         jb      @f
  47.         mov     eax,2
  48. @@:
  49.         push    [.szOperation+eax*4]
  50.  
  51.         ; NumberParameters
  52.         push    [edi+EXCEPTION_RECORD.NumberParameters]
  53.  
  54.         ; Continuable
  55.         mov     eax,[edi+EXCEPTION_RECORD.ExceptionFlags]
  56.         push    [.szLogical+eax*4]
  57.  
  58.         ; Type
  59.         mov     esi,.szType
  60. @@:
  61.         lodsd
  62.         or      eax,eax
  63.         jz      @f
  64.         cmp     eax,[edi+EXCEPTION_RECORD.ExceptionCode]
  65.         je      @f
  66.         lodsd
  67.         jmp     @b
  68. @@:
  69.         push    dword [esi]
  70.  
  71.         ; ExceptionCode
  72.         push    [edi+EXCEPTION_RECORD.ExceptionCode]
  73.  
  74.         ; ExceptionAddress
  75.         push    [edi+EXCEPTION_RECORD.ExceptionAddress]
  76.  
  77.         ; Сформировать текст исключения
  78.         lea     ebx,[szBuffer]
  79.         invoke  wsprintf,ebx,.szMask
  80.         add     esp,40h
  81.  
  82.         ; Сформировать имя файла для логирования ошибок
  83.         lea     ebx,[szFile]
  84.         invoke  GetModuleHandle,NULL
  85.         invoke  GetModuleFileName,eax,ebx,MAX_PATH
  86.         invoke  lstrcat,ebx,.szTail
  87.  
  88.         ; Попытаться создать файл
  89.         invoke  CreateFile,ebx,GENERIC_WRITE,FILE_SHARE_READ,\
  90.                 NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
  91.         cmp     eax,-1
  92.         je      @f
  93.         mov     ebx,eax
  94.  
  95.         ; Дописать текст в конец файла лога
  96.         invoke  SetFilePointer,ebx,0,0,FILE_END
  97.         or      eax,eax
  98.         jnz     .no_bom
  99.         invoke  _lwrite,ebx,.szBOM,2
  100.         jmp     .loc_write_data
  101. .no_bom:
  102.         invoke  lstrlen,.divider
  103.         invoke  _lwrite,ebx,.divider,eax
  104. .loc_write_data:
  105.         ; Указатель на сформированный текст исключения
  106.         lea     esi,[szBuffer]
  107.         invoke  lstrlen,esi
  108.         shl     eax,1
  109.         invoke  _lwrite,ebx,esi,eax
  110.         invoke  CloseHandle,ebx
  111. @@:
  112.         ; Сообщение пользователю о возникновении ошибки
  113.         invoke  MessageBox,0,esi,.szTitle,\
  114.                 MB_OK+MB_ICONHAND+MB_APPLMODAL+MB_TOPMOST
  115.  
  116.         invoke  ExitProcess,0
  117.  
  118. .szTail      du '_errors.log',0
  119.  
  120. .divider     du '------------------------------------'
  121.              du '------------------------',13,10,13,10,0
  122.  
  123. .szLogical   dd .szFalse,.szTrue
  124. .szFalse     du 'false',0
  125. .szTrue      du 'true',0
  126.  
  127. .szOperation dd .szRead,.szWrite,.szOther
  128. .szRead      du 'read',0
  129. .szWrite     du 'write',0
  130. .szOther     du 'other',0
  131.  
  132. .szBOM       db 0xFF,0xFE
  133.  
  134. .szType      dd 0xC0000005,.stat1
  135.              dd 0xC000008C,.stat2
  136.              dd 0x80000003,.stat3
  137.              dd 0x80000002,.stat4
  138.              dd 0xC000008D,.stat5
  139.              dd 0xC000008E,.stat6
  140.              dd 0xC000008F,.stat7
  141.              dd 0xC0000090,.stat8
  142.              dd 0xC0000091,.stat9
  143.              dd 0xC0000092,.stat10
  144.              dd 0xC0000093,.stat11
  145.              dd 0x80000001,.stat12
  146.              dd 0xC000001D,.stat13
  147.              dd 0xC0000006,.stat14
  148.              dd 0xC0000094,.stat15
  149.              dd 0xC0000095,.stat16
  150.              dd 0xC0000026,.stat17
  151.              dd 0xC0000008,.stat18
  152.              dd 0xC0000025,.stat19
  153.              dd 0xC0000096,.stat20
  154.              dd 0x80000004,.stat21
  155.              dd 0xC00000FD,.stat22
  156.              dd 0x80000029,.stat23
  157.              dd 0xC000009A,.stat24
  158.              dd 0xC000009B,.stat25
  159.              dd 0,.sUnk
  160.  
  161. .stat1       du 'ACCESS VIOLATION',0
  162. .stat2       du 'ARRAY BOUNDS EXCEEDED',0
  163. .stat3       du 'BREAKPOINT',0
  164. .stat4       du 'DATATYPE MISALIGNMENT',0
  165. .stat5       du 'FLOAT DENORMAL OPERAND',0
  166. .stat6       du 'FLOAT DIVIDE BY ZERO',0
  167. .stat7       du 'FLOAT INEXACT RESULT',0
  168. .stat8       du 'FLOAT INVALID OPERATION',0
  169. .stat9       du 'FLOAT OVERFLOW',0
  170. .stat10      du 'FLOAT STACK CHECK',0
  171. .stat11      du 'FLOAT UNDERFLOW',0
  172. .stat12      du 'GUARD PAGE VIOLATION',0
  173. .stat13      du 'ILLEGAL INSTRUCTION',0
  174. .stat14      du 'IN PAGE ERROR',0
  175. .stat15      du 'INTEGER DIVIDE BY ZERO',0
  176. .stat16      du 'INTEGER OVERFLOW',0
  177. .stat17      du 'INVALID DISPOSITION',0
  178. .stat18      du 'INVALID HANDLE',0
  179. .stat19      du 'NONCONTINUABLE EXCEPTION',0
  180. .stat20      du 'PRIVILEGED_INSTRUCTION',0
  181. .stat21      du 'SINGLE STEP',0
  182. .stat22      du 'STACK OVERFLOW',0
  183. .stat23      du 'UNWIND CONSOLIDATE',0
  184. .stat24      du 'FLOAT_MULTIPLE_FAULTS',0
  185. .stat25      du 'FLOAT_MULTIPLE_TRAPS',0
  186. .sUnk        du 'UNKNOWN',0
  187.  
  188. .szTitle     du 'Critical error',0
  189. .szMask      du 'Exception addr: %08Xh',13,10
  190.              du 'Exception code: %08Xh = %s',13,10,13,10
  191.              du 'Information:',13,10
  192.              du 'Continuable = %s, NumberParameters = %u, '
  193.              du 'ReadWrite = %s'
  194.              du 13,10,13,10
  195.              du 'Registers:',13,10
  196.              du 'EAX=%08Xh, EBX=%08Xh, ECX=%08Xh, EDX=%08Xh',13,10
  197.              du 'ESP=%08Xh, EBP=%08Xh, ESI=%08Xh, EDI=%08Xh',13,10,13,10
  198.              du 0
  199. endp
Второй - это персональный SEH-обработчик (Structured Exception Handler). Встраивается непосредственно в цепочку SEH через регистр FS:[0], получает 4 параметра (стандартный SEH callback).
  1. ;--------------------------------------------------
  2. ; Обработчик критических ошибок
  3. ;--------------------------------------------------
  4. proc  ExceptionFilter pExcept:DWORD, pFrame:DWORD,\
  5.       pContext:DWORD, pDispatch:DWORD
  6.  
  7.         locals
  8.             szFile   rb MAX_PATH
  9.             szBuffer rb 1000h
  10.         endl
  11.  
  12.         push    edi
  13.         mov     eax,[pContext]
  14.         ; EDI -> EXCEPTION_RECORD
  15.         mov     edi,[eax+EXCEPTION_POINTERS.pExceptionRecord]
  16.         ; DBG_PRINTEXCEPTION_WIDE_C
  17.         cmp     [edi+EXCEPTION_RECORD.ExceptionCode],4001000Ah
  18.         je      .loc_skip_exept
  19.         ; DBG_PRINTEXCEPTION_C
  20.         cmp     [edi+EXCEPTION_RECORD.ExceptionCode],40010006h
  21.         je      .loc_skip_exept
  22.         jmp     @f
  23.  
  24. .loc_skip_exept:
  25.         pop     edi
  26.         mov     eax,-1
  27.         ret
  28. @@:
  29.         pop     edi
  30.  
  31.         ; EDI -> CONTEXT
  32.         mov     edi,[pContext]
  33.         push    [edi+CONTEXT.regEdi]
  34.         push    [edi+CONTEXT.regEsi]
  35.         push    [edi+CONTEXT.regEbp]
  36.         push    [edi+CONTEXT.regEsp]
  37.         push    [edi+CONTEXT.regEdx]
  38.         push    [edi+CONTEXT.regEcx]
  39.         push    [edi+CONTEXT.regEbx]
  40.         push    [edi+CONTEXT.regEax]
  41.  
  42.         ; EDI -> EXCEPTION_RECORD
  43.         mov     edi,[pExcept]
  44.  
  45.         ; ReadWrite
  46.         mov     eax,[edi+EXCEPTION_RECORD.ExceptionInformation]
  47.         cmp     eax,2
  48.         jb      @f
  49.         mov     eax,2
  50. @@:
  51.         push    [.szOperation+eax*4]
  52.  
  53.         ; NumberParameters
  54.         push    [edi+EXCEPTION_RECORD.NumberParameters]
  55.  
  56.         ; Continuable
  57.         mov     eax,[edi+EXCEPTION_RECORD.ExceptionFlags]
  58.         push    [.szLogical+eax*4]
  59.  
  60.         ; Type
  61.         mov     esi,.szType
  62. @@:
  63.         lodsd
  64.         or      eax,eax
  65.         jz      @f
  66.         cmp     eax,[edi+EXCEPTION_RECORD.ExceptionCode]
  67.         je      @f
  68.         lodsd
  69.         jmp     @b
  70. @@:
  71.         push    dword [esi]
  72.  
  73.         ; ExceptionCode
  74.         push    [edi+EXCEPTION_RECORD.ExceptionCode]
  75.  
  76.         ; ExceptionAddress
  77.         push    [edi+EXCEPTION_RECORD.ExceptionAddress]
  78.  
  79.         ; Сформировать текст исключения
  80.         lea     ebx,[szBuffer]
  81.         invoke  wsprintf,ebx,.szMask
  82.         add     esp,40h
  83.  
  84.         ; Сформировать имя файла для логирования ошибок
  85.         lea     ebx,[szFile]
  86.         invoke  GetModuleHandle,NULL
  87.         invoke  GetModuleFileName,eax,ebx,MAX_PATH
  88.         invoke  lstrcat,ebx,.szTail
  89.  
  90.         ; Дописать текст в конец файла лога
  91.         invoke  SetFilePointer,ebx,0,0,FILE_END
  92.         or      eax,eax
  93.         jz      .loc_write_data
  94.  
  95.         invoke  lstrlen,.divider
  96.         invoke  _lwrite,ebx,.divider,eax
  97.  
  98. .loc_write_data:
  99.         ; Указатель на сформированный текст исключения
  100.         lea     esi,[szBuffer]
  101.         invoke  lstrlen,esi
  102.         invoke  _lwrite,ebx,esi,eax
  103.         invoke  CloseHandle,ebx
  104. @@:
  105.         ; Сообщение пользователю о возникновении ошибки
  106.         invoke  MessageBox,0,esi,.szTitle,\
  107.                 MB_OK+MB_ICONHAND+MB_APPLMODAL+MB_TOPMOST
  108.  
  109.         invoke  ExitProcess,0
  110.  
  111. .szTail      db '_errors.log',0
  112.  
  113. .divider     db '------------------------------------'
  114.              db '------------------------',13,10,13,10,0
  115.  
  116. .szLogical   dd .szFalse,.szTrue
  117. .szFalse     db 'false',0
  118. .szTrue      db 'true',0
  119.  
  120. .szOperation dd .szRead,.szWrite,.szOther
  121. .szRead      db 'read',0
  122. .szWrite     db 'write',0
  123. .szOther     db 'other',0
  124.  
  125. .szType      dd 0xC0000005,.stat1
  126.              dd 0xC000008C,.stat2
  127.              dd 0x80000003,.stat3
  128.              dd 0x80000002,.stat4
  129.              dd 0xC000008D,.stat5
  130.              dd 0xC000008E,.stat6
  131.              dd 0xC000008F,.stat7
  132.              dd 0xC0000090,.stat8
  133.              dd 0xC0000091,.stat9
  134.              dd 0xC0000092,.stat10
  135.              dd 0xC0000093,.stat11
  136.              dd 0x80000001,.stat12
  137.              dd 0xC000001D,.stat13
  138.              dd 0xC0000006,.stat14
  139.              dd 0xC0000094,.stat15
  140.              dd 0xC0000095,.stat16
  141.              dd 0xC0000026,.stat17
  142.              dd 0xC0000008,.stat18
  143.              dd 0xC0000025,.stat19
  144.              dd 0xC0000096,.stat20
  145.              dd 0x80000004,.stat21
  146.              dd 0xC00000FD,.stat22
  147.              dd 0x80000029,.stat23
  148.              dd 0xC000009A,.stat24
  149.              dd 0xC000009B,.stat25
  150.              dd 0,.sUnk
  151.  
  152. .stat1       db 'ACCESS VIOLATION',0
  153. .stat2       db 'ARRAY BOUNDS EXCEEDED',0
  154. .stat3       db 'BREAKPOINT',0
  155. .stat4       db 'DATATYPE MISALIGNMENT',0
  156. .stat5       db 'FLOAT DENORMAL OPERAND',0
  157. .stat6       db 'FLOAT DIVIDE BY ZERO',0
  158. .stat7       db 'FLOAT INEXACT RESULT',0
  159. .stat8       db 'FLOAT INVALID OPERATION',0
  160. .stat9       db 'FLOAT OVERFLOW',0
  161. .stat10      db 'FLOAT STACK CHECK',0
  162. .stat11      db 'FLOAT UNDERFLOW',0
  163. .stat12      db 'GUARD PAGE VIOLATION',0
  164. .stat13      db 'ILLEGAL INSTRUCTION',0
  165. .stat14      db 'IN PAGE ERROR',0
  166. .stat15      db 'INTEGER DIVIDE BY ZERO',0
  167. .stat16      db 'INTEGER OVERFLOW',0
  168. .stat17      db 'INVALID DISPOSITION',0
  169. .stat18      db 'INVALID HANDLE',0
  170. .stat19      db 'NONCONTINUABLE EXCEPTION',0
  171. .stat20      db 'PRIVILEGED_INSTRUCTION',0
  172. .stat21      db 'SINGLE STEP',0
  173. .stat22      db 'STACK OVERFLOW',0
  174. .stat23      db 'UNWIND CONSOLIDATE',0
  175. .stat24      db 'FLOAT_MULTIPLE_FAULTS',0
  176. .stat25      db 'FLOAT_MULTIPLE_TRAPS',0
  177. .sUnk        db 'UNKNOWN',0
  178.  
  179. .szTitle     db 'Critical error',0
  180. .szMask      db 'Exception addr: %08Xh',13,10
  181.              db 'Exception code: %08Xh = %s',13,10,13,10
  182.              db 'Information:',13,10
  183.              db 'Continuable = %s, NumberParameters = %u, '
  184.              db 'ReadWrite = %s',13,10,13,10
  185.              db 'Registers:',13,10
  186.              db 'EAX=%08Xh, EBX=%08Xh, ECX=%08Xh, EDX=%08Xh',13,10
  187.              db 'ESP=%08Xh, EBP=%08Xh, ESI=%08Xh, EDI=%08Xh',13,10,13,10
  188.              db 0
  189. endp
Важное замечение. Два кода исключений (4001000Ah и 40010006h) - это специальные отладочные исключения, которые генерируются функциями типа OutputDebugString. Они не являются настоящими ошибками, а используются для передачи отладочных сообщений. Если их не обработать специальным образом, они попадут в основной обработчик, который завершит программу, хотя на самом деле ничего критичного не произошло. Возвращая EXCEPTION_CONTINUE_EXECUTION (-1), первый обработчик просто пропускает эти "псевдо-исключения" и позволяет программе продолжить работу. Второй обработчик такой проверки не делает, поэтому любое отладочное сообщение (например, от отладчика или из OutputDebugString) приведет к появлению окна с сообщением о критической ошибке и завершению программы.

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

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

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

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

SEH.Demo.zip (9,735 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 6024 | Комментариев: 7

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (11.03.2026 в 01:27):
Добавлена информация о DBG_PRINTEXCEPTION_WIDE_C и DBG_PRINTEXCEPTION_C, дополнены исключения FLOAT_MULTIPLE_FAULTS и FLOAT_MULTIPLE_TRAPS. Приведены в порядок Unhandled Exception Filter (ASCII и юникод) и Structured Exception Handler. Архив обновлен.
ManHunter (24.08.2021 в 15:13):
Добавил юникодный обработчик
ManHunter (17.08.2021 в 12:53):
Весь VEH сводится к invoke AddVectoredExceptionHandler,1,ExceptionFilter
Остальной код остается без изменений
Petya (10.12.2020 в 16:14):
A VEH не предвидится?
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-2026
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0044 сек.) / Память: 4.5 Mb
Наверх