Windows process unhandled exceptions and C++ exceptions

——  Windows Core Programming Learning Handbook No. 25

When an exception filter returns the EXCEPTION_CONTINUE_SEARCH identifier, it tells the system to continue up the call tree, looking for another exception filter, but when each filter returns the EXCEPTION_CONTINUE_SEARCH identifier, a so-called "unhandled exception" occurs.

Each thread starts to execute, and actually uses a function in kernel32.dll to call BaseProcessStart or BaseThreadStart. These two functions are actually the same, the difference is that one function is used for the main thread of the process (primary thread):

       VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnStartAddr){

                     __try{

                                   ExitThread((pfnStartAddr)());

                     }

                     __except(UnhandledExceptionFilter(GetExceptionInfomation())){

                            ExitProcess(GetExceptionCode());

                     }

            //note:we never get here

       }

Another function for all secondary threads of the process:

       VOID BaseThreadStart(PTHREAD_START_ROUTINE pfnStartAddr,PVOID pvParam){

                     __try{

                                   ExitThread((pfnStartAddr)( pvParam));

                     }

                     __except(UnhandledExceptionFilter(GetExceptionInfomation())){

                            ExitProcess(GetExceptionCode());

                     }

            //note:we never get here

       }

Both functions contain a SHE framework, each function has a try block, and from this try block, the entry point function of the main thread or the auxiliary thread is called. When a thread throws an exception and all filters return EXCEPTION_CONTINUE_SEARCH, a special filter function provided by the system is automatically called:

       LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);

This function is responsible for displaying a message box indicating that there is an unhandled exception in the thread of a process and allowing the user to terminate or debug the process. Following the exception description in the message box, the user is presented with two options:

1) Selecting the "ok" button will cause the UnhandledExceptionFilter to return EXCEPTION_CONTINUE_SEARCH, which will cause the global unwind, all finally blocks to execute, and then the handler in BaseProcessStart or BaseThreadStart to execute. Both of these handlers are ExitProcess, which exits the process, and the exit code is the exception code, which is the thread of the process that ends the process itself, not the operating system , which means that the programmer can control this behavior and change it.

2) Select the "Cancel" button, UnhandledExceptionFilter attempts to load a debugger and attach the debugger to the process. By attaching the debugger to the process, you can check the status of global, local and static variables, set breakpoints, Inspect call trees, restart processes, and debug anything a process can do.

Note: The above is for program development in user-mode, for unhandled exceptions running in kernel-mode, the unhandled exception is in the operating system or more likely in the device driver, and Not in the application, such an unhandled exception indicates a serious bug! If a low-level virtual memory function generates an exception, the system looks to see if there are kernel mode exception filters ready to handle the exception. If the system cannot find it, the exception is unhandled. If an unhandled exception occurs in kernel mode, let the system continue It is not safe to run, so the system does not call the UnhandledExceptionFilter function in this case, but displays a so-called Blue Screen of Death, the display switches to a text-only blue screen video mode, and the computer is shut down ( balt), the displayed text tells which device driver was loaded, and the module contains code that throws an unhandled exception.

When the programmer selects the cancel button, it tells the UnhandledExceptionFilter function to debug the process. The ability to attach the debugger to any process at any time is called Just-in-time Debugging. Internally, UnhandledExceptionFilter invokes the debugger and needs to look at the following registry subkeys:

       HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/WindowsNT/

CurrentVersion/AeDebug

In this keyword, there is a value of Debugger, which is set to the following value when installing Visual Studio:

       C:/Program Filese/Microsoft Visual Studio/Common/MSDev98/Bin/msdev.exe

                           -p% ld –e% ld

In windows98, these values ​​are not stored in the registry, but in the Win.ini file.

This value tells the system which program (in this case msdev.exe) to run as the debugger, although other debuggers can be selected. UnhandledExceptionFilter also passes two parameters to the debugger on the command line. The first parameter is the process ID to be debugged; the second parameter specifies an inheritable manual reset event. This event is created by UnhandledExceptionFilter in a no-signal state. You must implement your own debugger to recognize the -p and -e options that specify the process ID and event handler.

After the process ID and event handle are combined into this string, UnhandledExceptionFilter executes the debugger by calling CreateProcess. This is, the debugger process starts running and checks its command line arguments. If the -p option is present, the debugger obtains the process ID , and attach itself to the process by calling DebugActiveProcess:

       BOOL DebugActiveProcess(DWORD dwProcessID);

Once the debugger finishes hooking itself, the operating system notifies the debugger of the status of the debuggee. After the debugger is fully initialized, check its command line again to find the -e option. If the item exists, the debugger obtains the corresponding event handle and calls SetEvent.

In addition, you don't have to wait for an exception before debugging a process, you can connect a debugger to any process at any time, just run MSDEV -p PID, where PID is the ID of the process to be debugged, which is actually selected in the Task Manager of Windows 2000 A process, and selecting the debug menu, will cause Task Manager to look at the previous registry subkey, call CreateProcess, and pass the selected process ID as a parameter, where Task Manage passes a value of 0 for the event handler.

If you don't want to display a message box on the screen when an exception occurs, there are several methods:

1) Force the process to terminate running

To prevent the UnhandledExceptionFilter from displaying an exception message box, you can call the SetErrorMode function, passing it a SEM_NOGPFAULTERRORBOX identifier:

       UNINT SetErrorMode (UINT fuErrorMode) ;

When the UnhandledExceptionFilter function is called to handle an exception, seeing that this flag has been set, it immediately returns EXCEPT_EXECUTE_HANDLER, which causes the global to expand and execute the handler in BaseProcessStart or BaseThreadStart, which ends the process.

2) Wrap a thread function

Arrange a try-except block on the entire content of the main thread entry point function (main, wmain, WinMain, wWinMain) to ensure that the result value of the exception filter is always EXCEPT_EXECUTE_HANDLER, thus ensuring that the exception can always be handled and preventing the system from calling UnhandledExceptionFilter again function. The disadvantage is that only exceptions that occur in the main thread can be caught, and other secondary threads cannot override them.

3) Wrap all thread functions

Windows provides the SetUnhandledExceptionFilter function to wrap all thread functions in SHE format.

       PTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(

                            PTOP_LEVEL_EXCEPTION_FILTER pTopLevelExceptionFilter);

After the process calls this function, an unhandled exception occurs in any thread of the process, which will cause the calling program's own exception filter, which needs to be passed as a parameter to SetUnhandledExceptionFilter. The filter function prototype is:

       LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);

3) Automatically call the debugger

In the same keyword in the registry where the debugger is set, there is an Auto value, which is used to specify whether the UnhandledExceptionFilter should display a message box or just start the debugger. If Auto is set to 1, the UnhandledExceptionFilter will not display a message box to report to the user exception, but immediately call the debugger, if set to 0, as described above.

The internal implementation of the UnhandledExceptionFilter function:

1) If an access violation occurs and is caused by an attempt to write memory (as opposed to reading memory), the system needs to check whether to modify a resource in an exe module or dll module. By default, the resource is read-only. Try to modify The resource will cause an access exception. However, 16-bit Windows allows modification of resources. From the perspective of compatibility, 32-bit and 64-bit should also allow modification of resources, so when you want to modify resources, UnhandledExceptionFilter calls VirtualProtect, changes the protection on the resource page to PAGE_READWRITE, and returns EXCEPTION_CONTINUE_EXECUTION;

2) If SetUnhandledExceptionFilter has been called to specify its own filter, UnhandledExceptionFilter will call the specified filter. If its own filter returns EXCEPTION_EXECUTE_HANDLER or EXECEPTION_CONTINUE_EXECUTION, UnhandledExceptionFilter will return this value to the system. If it does not set its own unhandled exception filter , or your own unhandled exception filter returns EXCEPTION_CONTINUE_SEARCH, then go to step 3;

3) If the process is running under the debugger, return EXCEPTION_CONTINUE_SEARCH. The debugger displays a message box and runs the debug process (IsDebuggerPresent function is used to determine whether a process is being debugged);

4) If a thread in the process calls SetErrorCode with the SEM_NOGPFAULTERRORBOX flag as a parameter, UnhandledExceptionFilter returns EXCEPTION_EXECUTE_HANDLER;

5) If the process is in a job job and the job's limit information is set with the JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION flag, the UnhandledExceptionFilter returns EXCEPTION_EXECUTE_HANDLER;

6) UnhandledExceptionFilter consults the registry and takes out the Auto value, if it is 1, go to step 7, if it is 0, display a message box to the user;

7) UnhandledExceptionFilter generates a debugger, first calls CreateEvent to create a signalless, manually reset event, the handle of this event can be inherited, then takes the Debugger value from the registry, and calls sprintf to paste it to the process ID (by calling GetCurrentProcessID function get) and event handlers. The lpDesktop member of STARTUPINFO is also set to "Winsta0//Default" so that the debugger appears on the interactive desktop.

8) When the debugger completes initialization, the event handle is set, which will wake up the thread in the UnhandledExceptionFilter, so the process runs under the debugger, and the UnhandledExceptionFilter returns EXCEPTION_CONTINUE_SEARCH.

SHE (Structured Exception Handling) can be used with operating system facilities of any programming language, while C++ exceptions can only be used to write C++ code. If you are writing a C++ program, you should use C++ exception handling instead of structured exception handling. The reason is that C++ exception handling is part of the language, and the compiler knows what a C++ class object is, which means that the compiler can automatically generate code to call C++ objects. Destructor, which guarantees the cleanup of the object.

However, the Microsoft Visual C++ compiler also uses the SHE of the operating system to implement C++ exception handling, so when a C++ try block is created, the compiler generates a SHE __try block, and a C++ catch test becomes a SHE The exception filter, and the code in the catch becomes the code in the __except block of SHE, in fact, the C++ throw statement, the compiler generates a call to the RaiseException function corresponding to windows, and the variable used for the throw statement is passed to RaiseException as an additional , as above, C++ exceptions are implemented internally by SHE.

Under normal circumstances, C++ exception handling does not allow programs to recover from hardware exceptions, such as access violations or division by zero exceptions, but Microsoft has added support for this to its compiler.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325768137&siteId=291194637