Программа выполнила недопустимую операцию?

Тема в разделе "WASM.PROJECTS", создана пользователем wasm_test, 6 янв 2010.

  1. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Кросспост из моего блога http://the-gr8.cih.ms/2010/01/blog-post_7649.html
    [​IMG]
    Всем знакомое назойливое окошко позволяет вызвать just-in-time отладчик, но самое обидное, что этот отладчик может быть только один единственный. А мне вот захотелось иметь возможность выбора между отладчиками - запустить олли, WinDbg или же создать минидамп с помощью ntsd.
    [​IMG]
    Список отладчиков хранится в подключах HKLM\Software\Microsoft\Windows NT\AeDebug в виде подключей "0", "1", "2", ...
    default value в них - путь к отладчику, а значение Name - отображаемое имя.
    Добавить новый можно с помощью regedit. По умолчанию выбирается отладчик "0", имейте в виду, когда будете составлять список.

    Список параметров (мини-хелп):

    * -i установка. Старый отладчик ставится в список нулевым HKLM\Software\Microsoft\Windows NT\AeDebug\0 с названием "Default" (если оно свободно, если занято, то ничего не меняется - на случай установки поверх уже установленного). Так же это можно юзать, если какой-то другой отладчик "занял" место jitmgr, тогда он восстановит себя в качестве jit отладчика. Параметр Auto меняется на 1 - окно dwwin более не отображается (его заменяет окошко jitmgr с расширенной информацией и выбором отладчика).
    * -u uninstall. Нулевой отладчик из списка ставится на место jitmgr.
    * %ld %ld параметры для вызова как jit. Первый - Process ID, второй - Event Handle. Так же может быть третий параметр - --log. В этом случае jitmgr логирует свой собственный отладочный вывод DbgPrint в jitmgr.log в корень системного диска. Рекомендуется установить этот флажок - jitmgr иногда лажает с определением EXCEPTION_RECORD. Эти файлы в случае фейла jitmgr (покажет messagbox) прошу присылать мне: [​IMG]

    Отдельного внимания заслуживает поиск EXCEPTION_RECORD* указателя в рухнувшем процессе. Для этого вкратце опишу механизм работы стандартного UnhandledExceptionDispatcher:

    * если параметр Auto==0, то подгружается faultrep.dll и вызывается ее ф-я ReportFault
    * ReportFault создает секцию с информацией об исключении и запускает dwwin.exe -x -s SectionHandle, который отобржает окно "xxx has encountered a probled and needs to close" с кнопками Debug и Close (кнопка Send у меня отключена :).
    * при нажатии Debug ReportFault() возвращает определенное значение, по которому UnhandledExceptionFilter запускает процесс по пути из реестра параметр AeDebug\Debugger, передавая в параметры PID и хендл евента, который отладчик должен сигнальнуть по окончании (видимо, для корректного завершения процесса).
    * UnhandledExceptionFilter делает ZwWaitForMultipleObject() на объектах процесса отладчика (на случай, если он завершится, так и не сигнальнув евент) и на евенте. По причине ожидания на процессе отладчика, jitmgr не завершается, пока не отработает запускаемый отладчик, иначе завершение jitmgr потенциально может убить рухнувший процесс, если отладчик решит его разморозить. Впрочем, он тогда и евент отсигналит, но в любом случае надо повисеть, пока дочерний отладчик не приаттачится, иначе он не успеет за завершающимся процессом.

    Поскольку UnhandledExceptionFilter thread-safe функция, во время ожидания на евенте и процессе отладчика в других потоках могут возникнуть каскадные исключения, являющимися следствиями первого или имеющими общую причину. Поэтому даже нажатие на Close в окне dwwin не всегда завершает процесс (многие замечали?). Вероятно, он просто сигналит евент и завершается. Для предотвращения этого у меня в jitmgr а) на время работы jitmgr оборачивается в ZwSuspendProcess/ZwResumeProcess, замораживая процесс и б) есть кнопка "Kill Process", которая насильно убивает процесс сразу же.
    Алгоритм поиска EXCEPTION_RECORD:

    * перебрать все потоки, найти потоки, у которых State == THREAD_STATE_WAIT
    * для каждого потока в состоянии ожидания получить контекст, прочитать содержимое стека по адресу Context.Ebp, которое в случае вызова из UnhandledExceptionFilter должно иметь следующую раскладку (подчеркиваю - содержимое по EBP. По ESP будет адрес возврата в ZwWaitForMultipleObjects):

    1. (ebp+0) old EBP
    2. (ebp+4) адрес возврата из UnhandledExceptionFilter в фильтр исключений SEH (блок __try/__except) функции BaseThreadStart или BaseProcessStart
    3. (ebp+8) указатель на EXCEPTION_POINTERS - первый параметр UEF.

    * найти базу kernel32.dll в дочернем процессе через раскрутку SEH: я никогда не видел Address Space Layout Randomization (ASLR), поэтому предположил, что это сработает :)
    * проверить, что *(ebp+4) принадлежит кернелу (еще можно проверить, что *esp - адрес возврата в ZwWaitForMultipleObjects действительно указывает туда, но это, имхо, уже лишнее; также можно проверить, что *ebp и *(ebp+8) принадлежат стеку этого потока)
    * проверить, что по адресу *(ebp+4)-5 действительно расположен call UnhandledExceptionFilter
    * в случае успеха всех условий, прочитать **(ebp+8) - указатель на EXCEPTION_RECORD

    Замечания и критика приветствуются. Выводится инфа в OutputDebugString (смотреть в DbgView), в случае ошибки поиска EXCEPTION_RECORD инфа дублируется в jitmgr.log - прошу высылать этот файл, если будет такая ошибка.

    P.S. Раньше была кнопочка Minidump (между Debug и KillProcess), но была выпилена по причине, что проще добавить ntsd явно в список отладчиков.
    P.P.S. Была мысля добавить автодобавление нового отладчика при указании пути к exe и выборе типа отладчика (чтобы автоматом дописывались параметры для известных отладчиков).

    Исходники и EXE в аттаче
     
  2. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    В догонку еще один кросспост из моего блога насчет отладчика ntsd и генерирования дампов:

    Все слышали про kd/windbg? Думаю, многие. Но немногие знают, что есть так же и хороший аналогичный отладчик ntsd (NT Symbolic Debugger) для юзермодных программ. Старая версия его даже входит в Windows по умолчанию (%systemroot%\system32\ntsd.exe). Нормальная версия поставляется в Debugging Tools for Windows вместе с kd/WinDbg.

    Интерфейс - мощный текстовый, как у kd. Команды аналогичны kd при работе в юзер моде.
    Одна из полезных команд - !analyze -v (как для ядра), делает анализ исключения. Вместе с возможностью аттачиться к процессам в режиме just-in-time и генерацией минидампа - весьма заманчиво.
    Отладчик поддерживает скрипты, что делает его еще более заманчивым.
    Есть так же поддержка отладки по сети (ключ -remote).

    Примеры:

    * ntsd -p PID - Аттачнуться к процессу по его PID. Так же можно добавить параметр -g (Auto 'Go' - автоматически выполнит Go, не брякаясь в процессе).
    * ntsd -p PID -e EVENTHANDLE - используется для Just-In-Time отладки, второй параметр - event handle, который нужно сигналить, чтобы "разморозить" упавший процесс, который ждет отладчика (там выполняется ZwWaitForMultipleObjects на хендлах евента и процесса отладчика).
    * Опция -с позволяет написать список команд для выполнения при старте. Например -c ".dump C:\Dumps\jit.dmp;q" сгенерирует минидамп по указанному пути и завершит отладчик. Можно использовать в just-in-time вместе с опцией -noio, которая подавляет показ консольного окна. В саму .dump можно добавить флаг /u, который сделает имя дампа уникальным в виде jit_046C_2010-01-06_03-15-26-921_06D8.dmp и /ma, которая будет генерировать расширенный дамп. Весьма полезно для тех случаев, когда комп тестится на машине тестера и нужно узнать инфу об исключении.
    * Ключики как в kd/WinDbg: -y указывает путь к символам, -z путь к крешдампу, который надо загрузить.

    Just-In-Time
    Как я уже говорил, его можно прописать в just-in-time отладку, чтобы упавшим программам генерился минидамп, или, например, выполнялся определенный скрипт - ограничения навязаны только полетом фантазии.
    Для этого надо прописать примерно следующее в HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger:
    ntsd -p %ld -e %ld -g -noio -c ".dump /u /ma C:\jit.dmp;q"
    Можно и не делать это вручную, в JIT его прописывает свитч -iae.

    Поскольку хочется иметь одновременно еще и "гуйный" отладчик, например, Olly, нужно иметь возможность выбора JIT-Отладчика. Об этом мой следующий пост (про мою софтинку jitmgr).

    http://www.opferman.net/Text/ntsd.txt - некоторая инфа про отладку в нем.
     
  3. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    отличная мысля
     
  4. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    jit'ом регаешь свой fault.exe ?
     
  5. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    n0name
    fault.exe делает mov dword [12345678], ABCDEF01
    диалог просто сообщает, что она выполнила некорректную операцию. как окно dwwin, только информативнее и с возможностью выбора отладчика. Добавил картинку для сравнения (начало 1 поста)
     
  6. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Обновил софтинку.
    Changelog for v0.2:
    * изменен алгоритм поиска EXCEPTION_RECORD. Тут сказали, что в случае ASLR база длл в каждом процессе одинакова + я убрал проверку, что адрес возврата из UEF лежит в kernel32 - он может лежать в любом месте, поскольку можно установить сех и в обработчике вызвать UnhandledExceptionFilter - все равно будет запущен dwwin. Поэтому убрана эта проверка и добавлена проверка, что адрес возврата *esp == ZwWaitForMultipleObjects+0x0c (UEF вызывает ZwWaitForMultipleObjects для ожидания на объектах процесса отладчика и синхронизируещего евента).
    Код (Text):
    1. HMODULE hNtdll = GetModuleHandle ("ntdll.dll");
    2. PVOID pZwWaitForMultipleObjects = GetProcAddress (hNtdll, "ZwWaitForMultipleObjects");
    3.  
    4. // Zw service has the following code:
    5. //  [ZwXXX+00] MOV EAX, (ServiceNumber)
    6. //  [ZwXXX+05] MOV EDX, 7FFE0300 ; ServiceCall
    7. //  [ZwXXX+0A] CALL DWORD PTR DS:[EDX]
    8. //  [ZwXXX+0C] RETN (argumentSize)
    9. // ReturnAddress should be ZwWaitForMultipleObjects+0c (address of RETN)
    10.  
    11. ULONG_PTR ReturnAddress = 0;
    12. ReadProcessMemory (hProcess, (LPCVOID)ctx.Esp, &ReturnAddress, sizeof(ULONG_PTR), &BytesRead);
    13.  
    14. if (ReturnAddress - 0x0c == (ULONG_PTR)pZwWaitForMultipleObjects)
    * поправлена небольшая ошибка с ведением лога