Низкоуровневый перехват эксепшнов

Тема в разделе "WASM.WIN32", создана пользователем nerd, 29 июн 2010.

  1. nerd

    nerd New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    12
    Вот нашел здесь статью http://www.wasm.ru/article.php?article=Win32SEHPietrek1

    Под дебагом перехват эксепшна работает, а под релизом не ловит. В чем может быть причина?
    Компилю в VS 2005 с дефолтными настройками.
    Код (Text):
    1. //==================================================
    2. // MYSEH - Matt Pietrek 1997
    3. // Microsoft Systems Journal, January 1997
    4. // FILE: MYSEH.CPP
    5. // To compile: CL MYSEH.CPP
    6. //==================================================
    7. #define WIN32_LEAN_AND_MEAN
    8. #include <windows.h>
    9. #include <stdio.h>
    10.  
    11. DWORD  scratch;
    12.  
    13. EXCEPTION_DISPOSITION
    14. __cdecl
    15. _except_handler(
    16.     struct _EXCEPTION_RECORD *ExceptionRecord,
    17.     void * EstablisherFrame,
    18.     struct _CONTEXT *ContextRecord,
    19.     void * DispatcherContext )
    20. {
    21.     unsigned i;
    22.  
    23.     // Indicate that we made it to our exception handler
    24.     printf( "Hello from an exception handler\n" );
    25.  
    26.     // Change EAX in the context record so that it points to someplace
    27.     // where we can successfully write
    28.     ContextRecord->Eax = (DWORD)&scratch;
    29.  
    30.     // Tell the OS to restart the faulting instruction
    31.     return ExceptionContinueExecution;
    32. }
    33.  
    34. int main()
    35. {
    36.     DWORD handler = (DWORD)_except_handler;
    37.  
    38.     __asm
    39.     {                           // Build EXCEPTION_REGISTRATION record:
    40.         push    handler         // Address of handler function
    41.         push    FS:[0]          // Address of previous handler
    42.         mov     FS:[0],ESP      // Install new EXECEPTION_REGISTRATION
    43.     }
    44.  
    45.     __asm
    46.     {
    47.         mov     eax,0           // Zero out EAX
    48.         mov     [eax], 1        // Write to EAX to deliberately cause a fault
    49.     }
    50.  
    51.     printf( "After writing!\n" );
    52.  
    53.     __asm
    54.     {                           // Remove our EXECEPTION_REGISTRATION record
    55.         mov     eax,[ESP]       // Get pointer to previous record
    56.         mov     FS:[0], EAX     // Install previous record
    57.         add     esp, 8          // Clean our EXECEPTION_REGISTRATION off stack
    58.     }
    59.  
    60.     return 0;
    61. }
     
  2. ohne

    ohne New Member

    Публикаций:
    0
    Регистрация:
    28 фев 2009
    Сообщения:
    431
    exe собранный прилаживай, который не пашет
     
  3. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    /SAFESEH:NO нужно добавить
     
  4. nerd

    nerd New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    12
    TSS, в точку, спасибо! Удивляюсь, откуда люди такие вещи знают ))
     
  5. nerd

    nerd New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    12
    Может подскажете, как подобным образом реализовать __except(EXCEPTION_EXECUTE_HANDLER). Посути мне надо просто перейти в определенную точку в коде. Для этого насколько я понимаю надо записать нужный адрес в ContextRecord->Eip. Но как же тут быть со стеком и с текущими значениями регистров?
     
  6. izl3sa

    izl3sa New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2010
    Сообщения:
    164
    Адрес:
    Spb
    оно ж из ContextRecord все восстановится. У Питрека все написано =)
     
  7. nerd

    nerd New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    12
    Восстановится всё для того места где произошел эксепшн, а не для того адреса куда мне перейти надо.. надо ведь как-то вычислять сколько стека освобождать надо, и т.д.
     
  8. nerd

    nerd New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    12
    Или просто посоветуйте решение как можно продолжить выполнение потока после такого эксепшна как вызов инструкции по кривому адресу. Вот пример:
    Код (Text):
    1. #define WIN32_LEAN_AND_MEAN
    2. #include <windows.h>
    3. #include <stdio.h>
    4.  
    5. //==================================================
    6. // MYSEH - Matt Pietrek 1997
    7. // Microsoft Systems Journal, January 1997
    8. // FILE: MYSEH.CPP
    9. // To compile: CL MYSEH.CPP
    10. //==================================================
    11.  
    12.  
    13.  
    14.  
    15. EXCEPTION_DISPOSITION
    16. __cdecl
    17. _except_handler(
    18.     struct _EXCEPTION_RECORD *ExceptionRecord,
    19.     void * EstablisherFrame,
    20.     struct _CONTEXT *ContextRecord,
    21.     void * DispatcherContext )
    22. {
    23.     DWORD  scratch;
    24.  
    25.     // Indicate that we made it to our exception handler
    26.     printf( "Hello from an exception handler\n" );
    27.  
    28.     // Change EAX in the context record so that it points to someplace
    29.     // where we can successfully write
    30.     ContextRecord->Eax = (DWORD)&scratch;
    31.  
    32.     // Tell the OS to restart the faulting instruction
    33.     return ExceptionContinueExecution;
    34. }
    35. typedef void(*badfunc)();
    36. int main()
    37. {
    38.     DWORD handler = (DWORD)_except_handler;
    39.  
    40.     __asm
    41.     {                           // Build EXCEPTION_REGISTRATION record:
    42.         push    handler         // Address of handler function
    43.         push    FS:[0]          // Address of previous handler
    44.         mov     FS:[0],ESP      // Install new EXECEPTION_REGISTRATION
    45.     }
    46.     badfunc f=NULL;
    47.    f();
    48.  
    49.     printf( "After writing!\n" );
    50.  
    51.     __asm
    52.     {                           // Remove our EXECEPTION_REGISTRATION record
    53.         mov     eax,[ESP]       // Get pointer to previous record
    54.         mov     FS:[0], EAX     // Install previous record
    55.         add     esp, 8          // Clean our EXECEPTION_REGISTRATION off stack
    56.     }
    57.  
    58.     return 0;
    59. }
     
  9. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    А чем __try // __except плох?
     
  10. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    7mm
    отличайте сишную обработку исключений (расширение msvc) и встроенный в винду сех
     
  11. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    Ну я в общем-то отличаю :) И если уж говорить о SEH, то это никак не встроенный в венду механизм обработки исключений, скорее наоборот: SEH есть расширение системного механизма обработки исключений Windows.

    По поводу темы дискуссии, автору рекомендую к прочтению мою статью, не так давно опубликованную на этом сайте: обработка user-mode исключений в Windows. Для решения вашей задачи потребуется реализовать механизм, подобный SEH.
     
  12. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Ну как бы доступные пользователю в юзермоде обработчики - SEH/VEH. Более низкоуровневый - это уже хукать KiUserExceptionDispatcher, undoc. В ядре тоже есть SEH. Поэтому о чем речь-то?

    А __try/__except/__finally (_except_handler/_global_unwind/_local_unwind) - это надстройка над SEH, причем спецефичная для msvc.
     
  13. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    Я про то, что название SEH не корректно для системной обработки исключений. SEH - это, по-логике, расширение msvc, хотя обычно, говоря SEH, подразумевают то, что вы имели в виду.
     
  14. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Да называйте SEH хоть бананом, но смысл не меняется =))
     
  15. baldr

    baldr New Member

    Публикаций:
    0
    Регистрация:
    29 апр 2010
    Сообщения:
    327
    nerd,

    В контексте есть Ebp, теоретически можно доскакать до нужного фрейма (аргумент EstablisherFrame указывает на структуру EXCEPTION_REGISTRATION, через которую был вызван обработчик). С правильным Esp, естественно, несколько сложнее.

    Есть ещё RtlUnwind(), которая, если я правильно понимаю, вызывает обработчики вплоть до указанного с кодом STATUS_UNWIND (0xC0000027) а потом переходит куда сказано. Что при этом с регистрами — вопрос.

    Если бы средствами C (без __try / __except / __finally, но с __asm) можно было бы реализовать человеческий инлайновый SEH (т.е. с возможностью обработать исключение и осмысленно перейти куда-нибудь в теле функции), это уже было бы сделано. Компилятор попросту не предоставляет достаточно информации для этого. А уж если это C++ с его деструкторами — всё гораздо веселей (на OpenRCE есть неплохая статья про художества MSVC++).

    Поиском нашёл здесь интересную тему. Стоит глянуть.
     
  16. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    VEH регайте, SEH локальный и не стабильный, не юзайте его для решения задач, отличных от защиты стековых фреймов.