можно ли как нибудь при исключении узнать какие были значения регистров? Может есть стандартные API? Было бы интересно аналогичное и под линух. (google breakpad такое может) А перед корахом программы всегда ли вызывается исключение если оно установлено, или возможны особые случаи?
Ошибки прикладных программ прекрасно контролирует встроенный вин/отладчик "Dr.Watson". Для этого достаточно запустить его с ключом "-i", т.е. [Win+R-->drwtsn32 -i]. C этого момента, ошибки прикладного уровня будут сохранятся в логе, который найдёшь по адресу: [C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson]. Там и регистры и стек на момент краха.
если только перехватить этот вывод.. общие сведения лежат тут: https://wasm.in/blogs/obrabotka-user-mode-iskljuchenij-v-windows.540/
переведите с С на fasm пожалуйста (в ep значения регисртров) компилятор даже в дебуг версии оптимизирует и ничего не понять... Код (C): #include <Windows.h> #include <exception> int t=0; int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep) { t=4; return EXCEPTION_EXECUTE_HANDLER; } int _tmain(int argc, _TCHAR* argv[]) { __try { t=3/t; } __except(filter(GetExceptionCode(), GetExceptionInformation())) { } return 0; }
используй SetUnhandledExceptionFilter().. эта функция устанавливает юзерский SEH-обработчик, и при возникновении исключения возвращает в стек 2-указателя на структуры "EXCEPTION_RECORD" и "CONTEXT". Вот их содержимое, которое так-же ложится в стек, сразу за указателями (см.скрин ниже): Спойлер: Структуры Код (ASM): struc EXCEPTION_RECORD { ; описание есть по ссылке выше .ExceptionCode dd ? .ExceptionFlags dd ? .ExceptionRecord dd ? .ExceptionAddress dd ? .NumberParameters dd ? .ExceptionInformation dd 15 dup (?) } struc CONTEXT { .ContextFlags dd ? .iDr0 dd ? .iDr1 dd ? .iDr2 dd ? .iDr3 dd ? .iDr6 dd ? .iDr7 dd ? .FloatSave FLOATING_SAVE_AREA .regGs dd ? .regFs dd ? .regEs dd ? .regDs dd ? .regEdi dd ? .regEsi dd ? .regEbx dd ? .regEdx dd ? .regEcx dd ? .regEax dd ? .regEbp dd ? .regEip dd ? .regCs dd ? .regFlag dd ? .regEsp dd ? .regSs dd ? .ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?) } Пример на фасме, который подминает SEH под свой обработчик "mySeh", и генерит исключение. Остаётся распарсить стек и получишь весь контекст на момент ошибки (я просто вывел из обработчика мессагу): Код (ASM): include 'win32ax.inc' .data mess db 'Ошибка!!! Обнаружена недопустимая операция!',0 .code start: invoke SetUnhandledExceptionFilter, mySeh ; указатель на обработчик xor eax,eax ; генерим исключение.. div eax invoke ExitProcess,0 ;------------------------------ mySeh: invoke MessageBox,0,mess,0,10h mov eax,1 ret .end start ;------------------------------ ; Возвращаемые значения из "mySeh" и их описание: ;eax=-1 перезагрузить контекст и продолжить выполнение программы. ;eax= 1 не показывать сообщение о предстоящем закрытии программы. ;eax= 0 показывать сообщение о предстоящем закрытии программы.
Коцит, > эта функция устанавливает юзерский SEH-обработчик, Не совсем так. Регистрируется финальный обработчик, он вызывается после любой другой обработки перед аварийным завершением приложения. В отличие от структурной/векторной обработки это простой стаб, он не синхронный(те нет межпоточных блокировок).
Вообще-то "финальный" с маркером(-1) и прихлопывает процесс аварийно, тихо/мирно отправляя его в небытие по ExitProcess, даже без посмертной надписи. Но поскольку эта функция фильтра позволяет установить свой/юзерский обработчик, значит это не финальный (хотя в доках его и называют именно так)? То-есть после него управление принимает последний в цепочке? Ещё непонятен сл.момент.. ..где он регистрируется? После этой функции TEB остаётся не тронутым, как и содержимое стека. Я так и не нашёл нигде указателей на свой обработчик. В доках есть упоминание, что SEH-фреймы могут находиться где-угодно (не обязательно в стеке), но в любом случае на начало цепочки должен указывать FS:[0] в ТЕВ, однако там - полный штиль и ничего не меняется. Выходит функция просто модифицирует (где-то в ядре) финальный чтоли, тупо добавляя к нему юзерский обработчик? Хрень какая-то.. Другое дело, когда устанавливаешь для треда свой SEH вручную - всё как на ладони, и система от тебя ничего уже не скрывает. Плюс есть бонусы в виде 'Safe-Place' (безопасная область) в возвращаемых значениях, и возможность обернуть в обработчик отдельный участок кода или весь тред целиком. Кстати тут есть SEH для асматиков: https://wasm.in/blogs/obrabotka-iskljuchenij-win32-dlja-programmistov-na-assemblere.381/
Коцит, Код (Text): LPTOP_LEVEL_EXCEPTION_FILTER BasepCurrentTopLevelFilter; LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter ) /*++ Routine Description: This function allows an application to supersede the top level exception handler that Win32 places at the top of each thread and process. If an exception occurs, and it makes it to the Win32 unhandled exception filter, and the process is not being debugged, the Win32 filter will call the unhandled exception filter specified by lpTopLevelExceptionFilter. This filter may return: EXCEPTION_EXECUTE_HANDLER - Return from the Win32 UnhandledExceptionFilter and execute the associated exception handler. This will usually result in process termination EXCEPTION_CONTINUE_EXECUTION - Return from the Win32 UnhandledExceptionFilter and continue execution from the point of the exception. The filter is of course free to modify the continuation state my modifying the passed exception information. EXCEPTION_CONTINUE_SEARCH - Proceed with normal execution of the Win32 UnhandledExceptionFilter. e.g. obey the SetErrorMode flags, or invoke the Application Error popup. This function is not a general vectored exception handling mechanism. It is intended to be used to establish a per-process exception filter that can monitor unhandled exceptions at the process level and respond to these exceptions appropriately. Arguments: lpTopLevelExceptionFilter - Supplies the address of a top level filter function that will be called whenever the Win32 UnhandledExceptionFilter gets control, and the process is NOT being debugged. A value of NULL specifies default handling within the Win32 UnhandledExceptionFilter. Return Value: This function returns the address of the previous exception filter established with this API. A value of NULL means that there is no current top level handler. --*/ { LPTOP_LEVEL_EXCEPTION_FILTER PreviousTopLevelFilter; PreviousTopLevelFilter = BasepCurrentTopLevelFilter; BasepCurrentTopLevelFilter = lpTopLevelExceptionFilter; return PreviousTopLevelFilter; } --- Сообщение объединено, 2 фев 2019 --- Разбирайся. Код (Text): LONG UnhandledExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) { NTSTATUS Status; ULONG_PTR Parameters[ 4 ]; ULONG Response; HANDLE DebugPort; CHAR AeDebuggerCmdLine[256]; CHAR AeAutoDebugString[8]; BOOLEAN AeAutoDebug; ULONG ResponseFlag; LONG FilterReturn; PRTL_CRITICAL_SECTION PebLockPointer; JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimit; // // If we take a write fault, then attampt to make the memory writable. If this // succeeds, then silently proceed // if ( ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION && ExceptionInfo->ExceptionRecord->ExceptionInformation[0] ) { FilterReturn = BasepCheckForReadOnlyResource((PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1]); if ( FilterReturn == EXCEPTION_CONTINUE_EXECUTION ) { return FilterReturn; } } // // If the process is being debugged, just let the exception happen // so that the debugger can see it. This way the debugger can ignore // all first chance exceptions. // DebugPort = (HANDLE)NULL; Status = NtQueryInformationProcess( GetCurrentProcess(), ProcessDebugPort, (PVOID)&DebugPort, sizeof(DebugPort), NULL ); if ( NT_SUCCESS(Status) && DebugPort ) { // // Process is being debugged. // Return a code that specifies that the exception // processing is to continue // return EXCEPTION_CONTINUE_SEARCH; } if ( BasepCurrentTopLevelFilter ) { FilterReturn = (BasepCurrentTopLevelFilter)(ExceptionInfo); if ( FilterReturn == EXCEPTION_EXECUTE_HANDLER || FilterReturn == EXCEPTION_CONTINUE_EXECUTION ) { return FilterReturn; } } if ( GetErrorMode() & SEM_NOGPFAULTERRORBOX ) { return EXCEPTION_EXECUTE_HANDLER; } // // See if the process's job has been programmed to NOGPFAULTERRORBOX // Status = NtQueryInformationJobObject( NULL, JobObjectBasicLimitInformation, &BasicLimit, sizeof(BasicLimit), NULL ); if ( NT_SUCCESS(Status) && (BasicLimit.LimitFlags & JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION) ) { return EXCEPTION_EXECUTE_HANDLER; } // // The process is not being debugged, so do the hard error // popup. // Parameters[ 0 ] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionCode; Parameters[ 1 ] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress; // // For inpage i/o errors, juggle the real status code to overwrite the // read/write field // if ( ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR ) { Parameters[ 2 ] = ExceptionInfo->ExceptionRecord->ExceptionInformation[ 2 ]; } else { Parameters[ 2 ] = ExceptionInfo->ExceptionRecord->ExceptionInformation[ 0 ]; } Parameters[ 3 ] = ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ]; // // See if a debugger has been programmed in. If so, use the // debugger specified. If not then there is no AE Cancel support // DEVL systems will default the debugger command line. Retail // systems will not. // ResponseFlag = OptionOk; AeAutoDebug = FALSE; // // If we are holding the PebLock, then the createprocess will fail // because a new thread will also need this lock. Avoid this by peeking // inside the PebLock and looking to see if we own it. If we do, then just allow // a regular popup. // PebLockPointer = NtCurrentPeb()->FastPebLock; if ( PebLockPointer->OwningThread != NtCurrentTeb()->ClientId.UniqueThread ) { try { if ( GetProfileString( "AeDebug", "Debugger", NULL, AeDebuggerCmdLine, sizeof(AeDebuggerCmdLine)-1 ) ) { ResponseFlag = OptionOkCancel; } if ( GetProfileString( "AeDebug", "Auto", "0", AeAutoDebugString, sizeof(AeAutoDebugString)-1 ) ) { if ( !strcmp(AeAutoDebugString,"1") ) { if ( ResponseFlag == OptionOkCancel ) { AeAutoDebug = TRUE; } } } } except (EXCEPTION_EXECUTE_HANDLER) { ResponseFlag = OptionOk; AeAutoDebug = FALSE; } } if ( !AeAutoDebug ) { Status =NtRaiseHardError( STATUS_UNHANDLED_EXCEPTION | HARDERROR_OVERRIDE_ERRORMODE, 4, 0, Parameters, BasepAlreadyHadHardError ? OptionOk : ResponseFlag, &Response ); } else { Status = STATUS_SUCCESS; Response = ResponseCancel; } // // Internally, send OkCancel. If we get back Ok then die. // If we get back Cancel, then enter the debugger // if ( NT_SUCCESS(Status) && Response == ResponseCancel && BasepAlreadyHadHardError == FALSE) { if ( !BaseRunningInServerProcess ) { BOOL b; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; CHAR CmdLine[256]; NTSTATUS Status; HANDLE EventHandle; SECURITY_ATTRIBUTES sa; BasepAlreadyHadHardError = TRUE; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; EventHandle = CreateEvent(&sa,TRUE,FALSE,NULL); RtlZeroMemory(&StartupInfo,sizeof(StartupInfo)); sprintf(CmdLine,AeDebuggerCmdLine,GetCurrentProcessId(),EventHandle); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.lpDesktop = "Winsta0\\Default"; CsrIdentifyAlertableThread(); b = CreateProcess( NULL, CmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation ); if ( b && EventHandle) { // // Do an alertable wait on the event // do { Status = NtWaitForSingleObject( EventHandle, TRUE, NULL ); } while (Status == STATUS_USER_APC || Status == STATUS_ALERTED); return EXCEPTION_CONTINUE_SEARCH; } } } #if DBG if (!NT_SUCCESS( Status )) { DbgPrint( "BASEDLL: Unhandled exception: %lx IP: %x\n", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress ); } #endif if ( BasepAlreadyHadHardError ) { NtTerminateProcess(NtCurrentProcess(),ExceptionInfo->ExceptionRecord->ExceptionCode); } return EXCEPTION_EXECUTE_HANDLER; }