SEH в C++ и Delphi

Тема в разделе "WASM.WIN32", создана пользователем SnugForce, 13 окт 2005.

  1. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    Решил реализовать SEH в Delphi по статье. Сначала есно попробовал в с++, все нормально. Перешел на Delphi. Мдаааа... Чего-то не очень :dntknw:
    Код (Text):
    1.  
    2. const
    3.   ExceptionContinueExecution = 0;
    4.   ExceptionContinueSearch = 1;
    5.   ExceptionNestedException = 2;
    6.   ExceptionCollidedUnwind = 3;
    7.  
    8. function _except_handler(ExceptionRecord: _EXCEPTION_RECORD;
    9.                           EstablisherFrame: DWORD;
    10.                           ContextRecord: _CONTEXT;
    11.                           DispatcherContext: DWORD): Integer; cdecl;
    12. begin
    13.  
    14.   MessageBox(0, 'Error', 'Error', MB_OK or MB_ICONWARNING);
    15.   ContextRecord.Eax := integer(@iData);
    16.   Result := ExceptionContinueExecution;
    17. end;
    18.  
    19. procedure TForm1.Button1Click(Sender: TObject);
    20. var
    21.   p: pointer;
    22. begin
    23.   p := @_except_handler;
    24.   asm
    25.     push p
    26.     push FS:[0]
    27.     mov FS:[0], ESP
    28.     mov EAX, 0
    29.     mov [EAX], 1
    30.   end;
    31.   MessageBox(0, '1', '1', MB_OK);
    32.   asm
    33.     mov EAX, [ESP]
    34.     mov FS:[0], EAX
    35.     add ESP, 8
    36.   end;
    37. end;
    38.  


    Что заметил. Если мой обработчик отказывается обрабатывать сообщения, то выскакивает ошибка, как и должно быть. Если соглашается обработать, то в регистре EAX 0 и опять вызывается мой обработчик. Такая ситуация в стандартном Application. При консольном проекте прога просто вылетает без ошибок... Может наставите на путь...
     
  2. Dimson

    Dimson New Member

    Публикаций:
    0
    Регистрация:
    7 июл 2005
    Сообщения:
    59
    Адрес:
    Russia
    1) В обработчик передаются адреса структур, а не сами структуры;

    2) Чтобы программа нормально продолжила своё выполнение

    нужно в обработчике исключения внести адрес безопасного места в структуру CONTEXT.
     
  3. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Dimson прав по п.1), в _except_handler параметры должны быть либо указателями на соответсвующие структуры, либо var-параметрами (что соб-но одно и тоже)

    Если у тебя _CONTEXT это не указатель, а сама структура, то присваивание ContextRecord.Eax пишет фиг знает куда в стек, но не в структуру контекст
     
  4. LuckyDevil

    LuckyDevil New Member

    Публикаций:
    0
    Регистрация:
    10 мар 2005
    Сообщения:
    278
    Адрес:
    Uzbekistan
    SnugForce, дельфи это работает особо тот случий что ты указал
    Код (Text):
    1.     mov EAX, 0
    2.     mov [EAX], 1
    3. or
    4. PDWORD(0)^ := 0;
    5.  


    только чуть код подправь
    Код (Text):
    1.             asm
    2.                     mov     SEH.SaveEip, offset Exception // это метка, куда тебе нужно будет попасть после исключения
    3.                     mov     SEH.OrgEbp, ebp
    4.                     push    offset _except_handler
    5.                     push    large dword ptr fs:0
    6.                     mov     SEH.OrgEsp, esp
    7.                     mov     large fs:0, esp
    8.             end;
    9.             PDWORD(0)^ := 0;
    10.             Writeln('тут мне не нсуждено оказаться');
    11.             asm
    12.                     Exception:
    13.                     pop     large dword ptr fs:0
    14.                     add     esp, 4
    15.             end;
    16.             Writeln('а тут сам бог велел быть');
    17.  


    :)
     
  5. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    О-о, сколько сторонников "безопасного места" ;)

    Если сам пишешь обработчик под конкретное исключение в конкретном месте (как в данном случае), то все эти излишества

    совершенно никчему. Нужно просто или 1) устранить условие ошибки - в данном случае изменить значение в EAX на валидный адрес для записи и повторить операцию (SnugForce так и делает) или 2) перепрыгнуть ошибочную инструкцию, увеличив CONTEXT.EIP на ее длину (на асме можно конечно непосредственно адрес "безопасного места" в regEIP забить, но на дельфях все метки - локальные для процедур)
     
  6. LuckyDevil

    LuckyDevil New Member

    Публикаций:
    0
    Регистрация:
    10 мар 2005
    Сообщения:
    278
    Адрес:
    Uzbekistan
    leo, я пока еще не встречал нормальный обработчик исключений писанный на асме, если честно сам потратил не мало времени с разборм этой темы, но пришел к выводу, что так легко это не делается, если исключение происходит на уровне выполнения каких либо команд и вам заранее известно какие регистры надо править, то все вери гуд, а вот если вы работаете к примеру в ядре и используете RTL-функции то тут не все просто, если у вас есть наработки по данной теме может поделитесь?
     
  7. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    LuckyDevil

    Собственых наработок по универсальным SEH'ам у меня нет, т.к. сложные вещи пишу на тех же дельфях c try\except, а на асме использую только простые локальные обработчики.

    А в дельфях кстати никакой safeplace тоже специально не сохраняется, а используется хитрая комбинация из 2-3х прыжков (недавно эта тема пролетала). Примерно вот так:
    Код (Text):
    1. _try ;устанавливаем SEH
    2.   push _jmp_to_SEH   ;пушится не сам обработчик, а jmp на него
    3.   push fs:[0]
    4.   mov fs:[0],esp
    5.   .........
    6. _except: ;=safeplace
    7.   .........
    8. _end:
    9.   pop fs:[0]
    10.   add esp,4
    11.   jmp _continue         ;обходим прыжки для продолжения
    12. _jmp_to_SEH:
    13.   jmp @_except_handler  ;прыжок на универсальный обработчик
    14.   jmp _except           ;сюда обработчик возвращает управление
    15. _continue:
    16.   .....
    17. function _except_handler(..):integer; //универсальный обработчик
    18. asm
    19. .......
    20.   mov edx,[esp+8]  ;адрес EstablisherFrame = Exception_Registration
    21.   mov edx,[edx+4]  ;адрес обработчика = _jmp_to_SEH
    22.   add edx,5        ;прибавляем длину инструкции jmp и получаем адрес инструкции jmp _except для возврата
    23.   ;пишем edx в CONTEXT.regEIP
    24. ......
    25. end;
     
  8. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    try except finally я тоже использую, но у меня задача сделать глобальный обработчик исключение в программе, плюс ко всему говорят, что передачу по SEH довольно проблематично найти, нежели простой вызов функции. Т.е. какая конкретно ошибка будет я знаю т.к. сам ее буду вызывать.
     
  9. LuckyDevil

    LuckyDevil New Member

    Публикаций:
    0
    Регистрация:
    10 мар 2005
    Сообщения:
    278
    Адрес:
    Uzbekistan
    SnugForce, на сайте есть очень много интересных статей на эту тему, так что почву для размышлений пожно узреть оттуда, еще стоит посмотреть как устроен обработчик исключений в С\С++, я тут еще прикрепил файлик это код выдернутый из Filemon, реализация глобального исключения, как раз таки быть может из-за него теперь не доступны соурсы самого Filemon, может это поможет тебе в разборе данной темы.



    [​IMG] 2098244473__except_handler3.txt
     
  10. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    all

    Сознаюсь... понадеялся на delphi и не стал смотреть в объявления структуры... Стыдно-то как...
    Код (Text):
    1.  
    2. function _except_handler(ExceptionRecord: PExceptionRecord;
    3.                           EstablisherFrame: DWORD;
    4.                           ContextRecord: PContext;
    5.                           DispatcherContext: DWORD): Integer; cdecl;
    6.  
     
  11. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    LuckyDevil

    Я глобальный имел ввиду про исключение в пределах потока. try except ведь не позволит этого делать.
     
  12. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    SnugForce





    Это тебе нужно смотреть в сторону UnhandledExceptionFilter



    А в самом простом случае на дельфи я когда-то делал так
    Код (Text):
    1. program SEH;
    2. uses
    3.   Windows;
    4. type
    5.   _SEH = record
    6.     SafeEip:DWORD;     // The offset where it's safe to continue execution
    7.     PrevEsp:DWORD;     // The previous value of esp
    8.     PrevEbp:DWORD;     // The previous value of ebp
    9.   end;
    10.  
    11. //
    12. // Exception disposition return values.
    13. //
    14. EXCEPTION_DISPOSITION         =  DWORD;
    15. const
    16.   ExceptionContinueExecution  =  0;
    17.   ExceptionContinueSearch     =  1;
    18.   ExceptionNestedException    =  2;
    19.   ExceptionCollidedUnwind     =  3;
    20.  
    21.   szCaption     : PChar = 'SEH Test' + #0;
    22.   szNotOccured  : PChar = 'Exception NOT occured !' + #0;
    23.   szOccured     : PChar = 'Exception occured !' + #0;
    24. var
    25. __seh           : _SEH;
    26. lpText          : PAnsiChar;
    27. label   _SafePlace;
    28.  
    29.  
    30. {$O+}
    31. function _except_handler(
    32.     ExceptionRecord:PExceptionRecord;
    33.     EstablisherFrame:Pointer;
    34.     ContextRecord:PContext;
    35.     DispatcherContext:Pointer): EXCEPTION_DISPOSITION; cdecl;
    36. begin
    37.   ContextRecord.Eip := __seh.SafeEip;
    38.   ContextRecord.Esp := __seh.PrevEsp;
    39.   ContextRecord.Ebp := __seh.PrevEbp;
    40.   // Tell the OS to restart the faulting instruction
    41.   Result := ExceptionContinueExecution;
    42. end;
    43.  
    44.  
    45. {$O-}
    46. begin
    47.  lpText := szOccured;
    48.  asm
    49.    push  OFFSET _except_handler
    50.    push  DWORD PTR fs:[0]                 // address of next ERR structure
    51.    mov   fs:[0], esp                      // give FS:[0] the ERR address just made
    52.  
    53.    mov   __seh.SafeEip, OFFSET _SafePlace
    54.    mov   __seh.PrevEsp, esp
    55.    mov   __seh.PrevEbp, ebp
    56.  end;
    57.  asm
    58.    db 0CCh
    59.  end;
    60.  lpText := szNotOccured;
    61. _SafePlace:
    62.  asm
    63.    pop   DWORD PTR fs:[0]                 // restore next ERR structure to FS:[0]
    64.    add   esp, 4                           // throw away rest of ERR structure
    65.  end;
    66.  MessageBox(0, lpText, szCaption, MB_OK or MB_ICONINFORMATION);
    67. end.
    68.  
    69.  
     
  13. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Единственное, чего не могу понять - почему все норовят сохранить EIP,ESP,EBP в глобальной переменной, хотя в книжках рекомендуют все это пихать в стек в EstablisherFrame ;))
     
  14. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    leo



    А можно посмотреть как это будет выглядить? :)
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    SnagForce

    Дык это же есть в статьях Питрека (ч.2 Расширенный фрейм обработки исключений) и Гордона (структура ERR).

    Обязательными полями EstablisherFrame является только запись EXCEPTION_REGISTRATION (указатель на предыдущую запись и указатель на наш обработчик). Но никто не запрещает перед указателем на обработчик запушить в стек и другие параметры, в частности safeplace и EBP. Насколько я понимаю, стандартов тут нет (у Гордона и Питрека расширенные записи несколько разные). Поэтому для себя можешь выбрать любой фиксированный порядок. Например:
    Код (Text):
    1. push epb                     ;[Frame+0Ch] = prevEBP
    2. push offset _SafePlace       ;[Frame+08h] = prevEIP
    3. push offset _except_handler  ;Frame+4
    4. push fs:[0]                  ;Frame (указатель на Frame и есть prevESP, поэтому сохранять его незачем)
    5. mov fs:[0],esp
    Сответсвенно в обработчике у тебя будет
    Код (Text):
    1. mov eax,[esp+8]   ;prevESP = указателю EstablisherFrame
    2. mov edx,[eax+8]   ;prevEIP = _SafePlace
    3. mov ecx,[eax+0Ch] ;prevEBP
    Ну и ес-но при снятии обработчика не забываем очистить стек от доп.параметров (pop fs:[0] + add esp,4*3)
     
  16. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    Ничего не понимаю. Вот этот код написано сохраняет адрес предыдущего обработчика.
    Код (Text):
    1.  
    2.    push  OFFSET _except_handler
    3.    push  DWORD PTR fs:[0]                
    4.    mov   fs:[0], esp                      
    5.  


    А чего прога тогда после вызова моего обработчки вылетает? Упарился уже, даже как-то до синего экрана дошло...
     
  17. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    Может это из-за того что занесение нового обработчика я делал в процедуре? Как только перенес в код - Старый обработчик вызывается уже нормально, а как же устанавливать в функции?
     
  18. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Я тоже ничего не понимаю ;) Куда чего перенес, что вызывается нормально, а что вылетает - объясни толком

    Приведенный кусок 100% рабочий, значит дело в чем-то другом - посмотри в отладчике где возникает ошибка
     
  19. SnugForce

    SnugForce New Member

    Публикаций:
    0
    Регистрация:
    2 май 2005
    Сообщения:
    373
    Адрес:
    Из домУ
    вот как раз под отладчиком и синий экран появляется :dntknw:
    Код (Text):
    1.  
    2. procedure Start;
    3. asm
    4.    push  OFFSET _except_handler
    5.    push  DWORD PTR fs:[0]                
    6.    mov   fs:[0], esp
    7. end;
    8.  
    9. ...
    10. Start;
    11. asm
    12.   mov eax, 0
    13.   mov [eax], 1
    14. end;
    15. Stop;
    16.  
     
  20. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Дык в какой момент ошибка, то появляется ? В обработчик _except_handler управление передается или нет ? Может ты чего в обработчике опять намудрил ;)) Хоть код обработчика приведи, что ли...