Ошибки в пользовательском режиме

Тема в разделе "WASM.WIN32", создана пользователем Clerk, 10 апр 2009.

  1. RET

    RET Well-Known Member

    Публикаций:
    17
    Регистрация:
    5 янв 2008
    Сообщения:
    789
    Адрес:
    Jabber: darksys@sj.ms
    Вот пример разбора ошибки в своей собственной программе на Си, с использованием хидера, который я предлагал http://depositfiles.com/files/mommloixs
    может так легче кому будет понять
    Код (Text):
    1. //***************вывод отладочной информации об ошибке***
    2. LONG DebugInformator(PEXCEPTION_POINTERS p_excep)
    3. {
    4.  //Здесь мы можем работать с фреймами о которых говорил Clerk
    5.   PEXCEPTION_RECORD p_excep_record = p_excep->ExceptionRecord;
    6.   char sz_msg[512];
    7.   sprintf(sz_msg,"Error in Address:0x%08X\n",p_excep_record->ExceptionAddress);
    8.   //.....Здесь еще много чего из фреймов полезного извлечь можно,
    9.   // а потом вывести ч/з чего - нибудь,    например MessageBox (кстати он может не
    10.   //работать при некоторых ошибках) или DebugPrint или лог в файл
    11.   LPVOID lpMsgBuf;
    12.   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |    
    13.                         FORMAT_MESSAGE_FROM_SYSTEM,
    14.                         NULL,RtlNtStatusToDosError(p_excep_record->ExceptionCode),
    15.                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    16.                         (LPTSTR) &lpMsgBuf,0,NULL);
    17.   strcat(sz_msg,NTSTATUS_TO_DESCRIPTION(p_excep_record->ExceptionCode));
    18.   strcat(sz_msg,"\n");
    19.   strcat(sz_msg,(LPCSTR)lpMsgBuf);
    20.   LocalFree(lpMsgBuf);
    21.   MessageBoxA(GetActiveWindow(),sz_msg,"ERROR",MB_ICONSTOP | MB_SYSTEMMODAL);
    22.   //здесь можно принять решение что делать дальше:
    23.   return EXCEPTION_CONTINUE_EXECUTION;
    24.   //или return EXCEPTION_EXECUTE_HANDLER;
    25.   //или return EXCEPTION_CONTINUE_SEARCH;
    26. }
    27. /*==================================
    28. В результате получим, в зависимости от ошибки сообщение типа:
    29. Error in Address: 0xFFA......
    30. RPC_NT_SEND_INCOMPLETE
    31. В буфере запроса остались данные для отправки.
    32. */===================================
    33.  
    34.  
    35. //*************в вашем коде***********************
    36. __try
    37. {
    38.    //Здесь помещается критический код (а лучше весь).
    39.   return STATUS_SUCCESS; //например, возврат если все ОК, если возникает исключение
    40.                                       // то обработка в DebugInformator
    41. }
    42. __except (DebugInformator(_exception_info()))
     
  2. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    RET
    Твой код вне всяких похвал. Не хватает только LocalFree.
     
  3. RET

    RET Well-Known Member

    Публикаций:
    17
    Регистрация:
    5 янв 2008
    Сообщения:
    789
    Адрес:
    Jabber: darksys@sj.ms
    Если ты о sz_msg -то она выделяется динамически из стека, и освобождать ее не надо (выделять из стека не есть гуд, поэтому этот код только демонстрационный,естественно нуждающийся в доработке).
    Если о фреймах с отладочной информацией - их заполняет ядро, мы лишь получаем указатели на них. Освобождать их не нужно.
     
  4. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    lpMsgBuf
     
  5. RET

    RET Well-Known Member

    Публикаций:
    17
    Регистрация:
    5 янв 2008
    Сообщения:
    789
    Адрес:
    Jabber: darksys@sj.ms
    Ок, добавил
     
  6. DillerInc

    DillerInc New Member

    Публикаций:
    0
    Регистрация:
    20 авг 2006
    Сообщения:
    41
    Есть ли смысл полностью отказаться от SEH'а в пользу перехвата KiUserExceptionDispatcher в целях повышения быстродействия кода?
     
  7. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    DillerInc
    Только если ваш программный продукт использует seh настолько часто, что проседает производительность, например если реализована и часто используется самотрассировка.
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    DillerInc
    Да, но это должно быть очень специфическое приложение. Например возврат из иключения обычно выполняют возвращая из сех значение, после которого последует перезагрузка контекста в процессор посредством сервиса NtContinue. Это требует значительное время, поэтому оптимизировать можно вручную возвращая управление, например я в большинстве случаев использую это:
    Код (Text):
    1. SEH_PROLOG macro
    2. Local Delta1, Delta2
    3.     assume fs:nothing
    4.     push ebp
    5.     Call Delta1
    6. Delta1:
    7.     add dword ptr [esp],(ExceptionExit - Delta1)
    8.     Call Delta2
    9. Delta2:
    10.     add dword ptr [esp],(ExceptionHandler - Delta2)
    11.     push dword ptr fs:[TEB.Tib.ExceptionList]
    12.     mov dword ptr fs:[TEB.Tib.ExceptionList],esp
    13. endm
    14.  
    15. SEH_EPILOG macro
    16. ;   clc
    17. ExceptionExit:
    18.     pop dword ptr fs:[TEB.Tib.ExceptionList]
    19.     lea esp,[esp + 4*3]
    20. endm
    21.  
    22. ExceptionHandler proc C
    23.     mov esp,dword ptr [esp + 8] ;(esp) -> ExceptionList
    24.     xor eax,eax
    25.     mov ebp,dword ptr [esp + 4*3]
    26. ;   stc
    27.     jmp dword ptr [esp + 4*2]
    28. ExceptionHandler endp
    Можно восстановить вручную также состояние процессора.
    Касательно VEH - изза входа в критическую секцию два треда не могут обрабатывать исключение в нём. Своя реализация сех будет более быстрой, но менее гибкой.
     
  9. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Кстати пикод для перехвата http://files.virustech.org/indy/Teory/Exceptions/Intercept/
     
  10. DillerInc

    DillerInc New Member

    Публикаций:
    0
    Регистрация:
    20 авг 2006
    Сообщения:
    41
    TSS, да,это можно назвать самотрассировкой - передача управления в процедуру протектора и трассировка этой процедуры.
    Clerk, спасибо за код.
    В процедуре ExceptionHandler "xor eax, eax" - это EXCEPTION_CONTINUE_EXECUTION?
     
  11. catwalk_mission

    catwalk_mission New Member

    Публикаций:
    0
    Регистрация:
    23 май 2009
    Сообщения:
    19
    DillerInc
    это не может быть EXCEPTION_CONTINUE_EXECUTION, потому что EXCEPTION_CONTINUE_EXECUTION (== -1) относится к чуть более высокоуровневой системе обработки исключений. точнее, это значение может вернуть фильтр исключений, вызываемый из предоставляемой окружением поддерживающих try\except компиляторов функции _except_handler3\_except_handler4. а вот уже _except_handler3\4 после этого вернёт системе ExceptionContinueExecution (== 0).
    ...ну это так, на всякий случай. чтобы не было путаницы между механизмами, предоставляемыми компиляторами и системой =)

    в xp sp2. в sp3 должны были (?) уже починить это недоразумение. в висте, например, VEH\VCH вызывается вне критической секции (и вне SRWLock).
     
  12. DillerInc

    DillerInc New Member

    Публикаций:
    0
    Регистрация:
    20 авг 2006
    Сообщения:
    41
    catwalk_mission, да,спасибо.Я это и имел в виду - ExceptionContinueExecution (0).
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    catwalk_mission
    Это не недоразуменее, это аналогично и в XP, в 7 и висте. Иначе быть не может - сам список защищён критической секцией(RtlpCalloutEntryLock), иначе один тред могбы устанавливать хэндлер, другой использовать текущий, что привелобы к рассинхронизации и краху.
     
  14. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    Clerk:
    а вы точно замерили выйгрыш времени при многократном(!) прямом выходе из SEH!?
     
  15. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    je_
    Мерить ничего не нужно, задержка очевидна:
    - NtContinue(FastCall) - смена CPL.
    - KiServiceExit(FastExit) - вторая смена CPL.
    - KeContextToKframes().
    Весь код занимает тысячи инструкций, это очень долго. Впрочем измерю, интересна задержка.
     
  16. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    je_
    1. Отношение.
    Код (Text):
    1. .code
    2. CPU1_MASK   equ 01B
    3.  
    4. WAIT_SECONDS    equ 4
    5.  
    6. UsTickCountLow  equ 7FFE0000H
    7. UsTickCountMultiplier   equ 7FFE0004H
    8.  
    9. GET_TICK_COUNT macro
    10.     mov eax,ds:[UsTickCountLow]
    11.     mul dword ptr ds:[UsTickCountMultiplier]
    12.     shrd eax,edx,18h
    13. endm
    14.  
    15. WAIT_NEXT_TICK macro
    16.     mov edx,ds:[UsTickCountLow]
    17. @@:
    18.     mov eax,ds:[UsTickCountLow]
    19.     cmp eax,edx
    20.     je @b
    21. endm
    22.  
    23. BREAKERR macro
    24.     .if Eax
    25.     int 3
    26.     .endif
    27. endm
    28.  
    29. CalculateContinueCalls proc
    30. Local Context:CONTEXT
    31. Local CallCount:ULONG
    32.     mov Context.ContextFlags,CONTEXT_ALL or CONTEXT_EXTENDED_REGISTERS
    33.     invoke ZwGetContextThread, NtCurrentThread, addr Context
    34.     BREAKERR
    35.     mov Context.regEsp,Esp
    36.     mov Context.regEip,offset Continue
    37.     mov CallCount,eax
    38.     WAIT_NEXT_TICK
    39.     GET_TICK_COUNT
    40.     add eax,1000*WAIT_SECONDS   ; Sec.
    41.     mov Context.regEsi,eax
    42. NextCall:
    43.     invoke ZwContinue, addr Context, FALSE
    44. Continue:
    45.     inc CallCount
    46.     GET_TICK_COUNT
    47.     cmp eax,esi
    48.     jb NextCall
    49.     mov ebx,CallCount
    50.     ret
    51. CalculateContinueCalls endp
    52.  
    53. CalculateIdleCalls proc
    54. Local CallCount:ULONG
    55.     WAIT_NEXT_TICK
    56.     GET_TICK_COUNT
    57.     mov CallCount,0
    58.     lea esi,[eax + 1000*WAIT_SECONDS]   ; Sec.
    59. @@:
    60.     inc CallCount
    61.     GET_TICK_COUNT
    62.     cmp eax,esi
    63.     jb @b
    64.     mov eax,CallCount
    65.     ret
    66. CalculateIdleCalls endp
    67.  
    68. .data
    69. Buffer  ULONG 100 DUP (?)
    70.  
    71. .code
    72. Entry proc
    73. Local Affinity:ULONG
    74. Local Loops:ULONG
    75.     cld
    76.     lea edi,Buffer
    77.     mov Affinity,CPU1_MASK
    78.     mov Loops,10
    79.     invoke ZwSetInformationProcess, NtCurrentProcess, ProcessAffinityMask, addr Affinity, 4
    80.     BREAKERR
    81.     invoke ZwSetInformationThread, NtCurrentThread, ThreadAffinityMask, addr Affinity, 4
    82.     BREAKERR
    83. @@:
    84.     invoke CalculateContinueCalls
    85.     invoke CalculateIdleCalls
    86.     xor edx,edx
    87.     div ebx
    88.     dec Loops
    89.     stosd
    90.     jnz @b
    91.     int 3
    92.     ret
    93. Entry endp
    Результат на P4-3014/XPSP3:
    Код (Text):
    1. 00403000   0000020B
    2. 00403004   00000211
    3. 00403008   000001EF
    4. 0040300C   000001D4
    5. 00403010   000001C2
    6. 00403014   000001AF
    7. 00403018   00000170
    8. 0040301C   0000021C
    9. 00403020   0000021D
    10. 00403024   0000021D
    Дабы результаты были стабильными используется большое время измерения(изза планирования). С NtContinue цикл выполняется примерно в ~400 раз медленее, чем без него.
    2. Величина задержки.
    Код (Text):
    1. @@:
    2.     invoke CalculateContinueCalls
    3.     mov eax,ebx
    4.     dec Loops
    5.     stosd
    6.     jnz @b
    7.     int 3
    Результат:
    Код (Text):
    1. 00403000   00269684
    2. 00403004   00266973
    3. 00403008   0026BF06
    4. 0040300C   0026ADBF
    5. 00403010   0026C841
    6. 00403014   0026CBA9
    7. 00403018   0026B79C
    8. 0040301C   0026CF90
    9. 00403020   0026D2AB
    10. 00403024   0026D24F
    Примерно ~640.000 итераций в секунду. Холостыми циклами можно пренебречь(1/400), получаем время затрачиваемое на вызов сервиса ~1,56 микросекунд. Это много(Idle циклов в секунду ~ 350.000.000 при высоком приоритете).
     
  17. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Диспетчер исключений, захват модификацией смещения в инструкции Call RtlDispatchException(код в #29):
    Код (Text):
    1. CPU1_MASK   equ 01B
    2.  
    3. WAIT_SECONDS    equ 4
    4.  
    5. UsTickCountLow  equ 7FFE0000H
    6. UsTickCountMultiplier   equ 7FFE0004H
    7.  
    8. GET_TICK_COUNT macro
    9.     mov eax,ds:[UsTickCountLow]
    10.     mul dword ptr ds:[UsTickCountMultiplier]
    11.     shrd eax,edx,18h
    12. endm
    13.  
    14. WAIT_NEXT_TICK macro
    15.     mov edx,ds:[UsTickCountLow]
    16. @@:
    17.     cmp ds:[UsTickCountLow],edx
    18.     je @b
    19. endm
    20.  
    21. BREAKERR macro
    22.     .if Eax
    23.     int 3
    24.     .endif
    25. endm
    26.  
    27. .data
    28. KiDispatchReference PVOID offset RtlDispatchException
    29. ExceptionCounter    ULONG ?
    30.  
    31. .code
    32. include Dump.inc
    33.  
    34. RtlDispatchException proc C ExceptionRecord:PEXCEPTION_RECORD, ContextRecord:PCONTEXT
    35.     mov eax,ContextRecord
    36.     mov esp,CONTEXT.regEsp[eax]
    37.     inc ExceptionCounter
    38.     GET_TICK_COUNT
    39.     cmp eax,esi
    40.     jnb @f
    41. ;   ret ; Для возврата посредством NtContinue
    42.     cli ; Для возврата вручную.
    43. @@:
    44.     int 3
    45. RtlDispatchException endp
    46.  
    47. Entry proc
    48. Local Affinity:ULONG
    49. Local Loops:ULONG
    50.     mov Affinity,CPU1_MASK
    51.     invoke ZwSetInformationProcess, NtCurrentProcess, ProcessAffinityMask, addr Affinity, 4
    52.     BREAKERR
    53.     invoke ZwSetInformationThread, NtCurrentThread, ThreadAffinityMask, addr Affinity, 4
    54.     BREAKERR
    55.     push offset KiDispatchReference
    56.     Call PiEntry
    57.     BREAKERR
    58.     WAIT_NEXT_TICK
    59.     GET_TICK_COUNT
    60.     lea esi,[eax + 1000*WAIT_SECONDS]   ; Sec.
    61.     cli
    62. Entry endp
    Результат - весьма стабильно вызывается, ~360.000//200.000 раз в секунду для исключения при исполнении привилегированной инструкции(второй результат при использовании NtContinue). Тоесть если вручную передавать управление то в общем это быстрее в 1.8 раза. Время от генерации исключения до возврата на юзермодный диспетчер для предыдущего типа исключений примерно 2.75 микросекунды. Задержка завивит от типа исключения, теоретически самой долгой будет при страничных нарушениях.
    Если у процесса имеется отладочный порт(например под олей запущен), то очень медленно вызывается диспетчер но стабильно, всего ~440 раз в секунду, тоесть в 800 раз медленнее происходит обработка исключений, можно использовать кстати для определения наличия отладочного порта(подобное вероятно можно использовать для обнаружения ядерного дебуггера из юзермода..).
     
  18. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    у меня такой резултЪ

    полный SEH (с INT/IRETD) на 0800h-0900h тиков длиннее.
    .не стоит мороки

    полный SEH квалифицированно восстонавливает всё.

    прямой выход из SEH следует считать "узко-профильным".
     
  19. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    je_
    Каких тиков, на современных процессорах тики считать нет смысла. И причём тут инструкция Int ?
     
  20. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    а если ставить 010003 флаг в context, (только Reg/EIP) то снизим различие к 0400 тикам.
    (INT/IRETD = не-SYSENTER/SYSEXIT)