Analysis of Win32 Debug Interface Design and Implementation [2] Debugging Events

zhaozj2021-02-17  47

http://flier_lu.blogone.net/?id=1324316

[2] Debugging the event, the user status debugger under Win32 is actually a while loop. First waiting for a debug event first, then process it, finally handling the control right to the debug server, as if a window message loop is the same. . The core of debugging event is actually a debug_event structure, defined in the winbase.h file as follows:

The following is quoted:

Typedef strunt _debug_event {

DWORD DWDEBUGEVENTCODE;

DWORD DWPROCESSID;

DWORD DWTHREADID;

Union {

EXCEPTION_DEBUG_INFO EXCEPTION;

CREATE_THREAD_DEBUG_INFO CREATTHREAD;

Create_process_debug_info createprocessinfo;

EXIT_THREAD_DEBUG_INFO EXITTHREAD;

EXIT_PROCESS_DEBUG_INFO EXITPROCESS;

LOAD_DLL_DEBUG_INFO LOADDLL;

UNLOAD_DLL_DEBUG_INFO UNLOADDLL;

Output_debug_string_info debugstring;

RIP_INFO RIPINFO;

} U;

} Debug_event, * lpdebug_event;

The DWDeBugeventCode field gives the type of this debug event, DWProcessID and DWTHREADID fields give the process and thread ID number that occurred in debug events, respectively. Debugging events generally have the following categories:

The following is quoted:

#define eXception_debug_event 1

#define create_thread_debug_event 2

#define create_process_debug_event 3

#define EXIT_THREAD_DEBUG_EVENT 4

#define EXIT_PROCESS_DEBUG_EVENT 5

#define load_dll_debug_event 6

#DEFINE UNLOAD_DLL_DEBUG_EVENT 7

#define output_debug_string_event 8

#define rip_event 9

Every time there is a new / exit a thread CREATE_THREAD_DEBUG_EVENT / EXIT_THREAD_DEBUG_EVENT event is; CREATE_PROCESS_DEBUG_EVENT event is triggered when you create a new process of the first thread; EXIT_PROCESS_DEBUG_EVENT corresponding event in the process being debugged end of the last thread running was triggered Start; Each time you load / uninstall a DLL, there will be a load_dll_debug_event / unload_dll_debug_event / unload_dll_debug_event event; the debugger uses the outputdebugstring function to output a debug string when the debugger accepts an Output_debug_string_event event; an exception is triggered when the debugger accepts the debugger A first time EXCEPTION_DEBUG_EVENT event, if the debugger does not process this exception, enter the normal SEH call chain of the debugged program, if the debug process is not processed, then this event will be triggered again; Rip_Event is generally used to report an error event . Generally, the program debugging event is initiated in order:

The following is quoted:

Create_process_debug_event

LOAD_DLL_DEBUG_EVENT X N // Static Loaded DLLCREATE_THREAD_DEBUG_EVENT & EXIT_THREAD_DEBUG_EVENT // Multi-threaded program in pairs

LOAD_DLL_DEBUG_EVENT & Unload_dll_debug_event // Dynamically loaded DLL

EXCEPTION_DEBUG_EVENT X N // Random appears

OUTPUT_DEBUG_STRING_EVENT X N // The program is written when writing debug information

EXIT_PROCESS_DEBUG_EVENT

Next we analyze the causes and timing of each debug event trigger in detail. The specific debug event content is not coming here, and friends who are interested in writing the debugger can refer to the relevant content in MSDN and . The first is the create_process_debug_event event of the process and create a CREATE_THREAD_DEBUG_EVENT event for the establishment of a thread. Both events are triggered by the DBGKCREATTHREAD function (NTOS / DBGK / DBGKPROC.H: 211). This function first checks if the current thread is an active thread with the debug port; then check if the current thread is the first thread created by the process; if it is not the first thread, or the debugger is an active process (Determination is based on whether this process takes up the user-based CPU time), trigger the CREATE_THREAD_DEBUG_EVENT event to the debug server of the tester; otherwise, the CREATE_PROCESS_DEBUG_EVENT event is reported. DBGKCREATTHREAD function is as follows:

The following is quoted:

Void DBGKCREATTHREAD (PVOID Startaddress)

{

IF (! psgetcurrentprocess () -> debugport || psgetcurrentthread () -> deadthread)

{

Return;

}

PslockProcess (Process, kernelMode, PslockWaitForever); // All threads in the process

IF (psgetcurrentprocess () -> pcb.usertime &&

PsgetCurrentProcess () -> CREATEPROCESSREPORTED == false)

{

PsgetCurrentProcess () -> CREATEPROCESSREPORTED = true;

// trigger CREATE_PROCESS_DEBUG_EVENT event

}

Else

{

// trigger CREATE_THREAD_DEBUG_EVENT event

}

PsunlockProcess (psgetcurrentprocess ());

}

When Win32 is creating a user-state thread, the approximate process is as follows:

The following is quoted:

CreateThread (kernel32.dll)

CreateremoteThread (kernel32.dll)

NtcreateThread (Ntoskrnl.exe)

PSpCreateThread (NTOS / PS / CREATE.C: 237)

When the PSPCreateThread function is created, use the pspuserthreadstartup function (NTOS / PS / CREATE.C: 1639) as a thread portfolio, so the thread is created directly into this function. The pspuserthreadstartup function initiates its APC for a non-derivation thread and the endless thread; then calls the DBGKCREATTHREAD function Notification The debugger takes the corresponding action; finally set the user's NOT CPU time to 1 to indicate that this process is started. For a special thread, a non-zombie thread has stopped when thread starts, then DBGKCREATTHREAD is called directly to call PSPEXITTHREAD to notify the debugger to take the corresponding action. PSPUSERTHREADSTARTUP function is as follows: The following is a reference:

Void pspuserThreadStartup (in pkstart_routine startroutine, in pvoid startcontext)

{

IF (! psgetcurrentthread () -> deadthread &&! psgetcurrentthread () -> HASTERMINATED)

{

// Initialization thread APC

}

Else

{

IF (! psgetcurrentthread () -> deadthread)

{

DBGKCREATTHREAD (STARTCONTEXT);

}

PSPEXITTHREAD (status_thread_is_terminating);

}

DBGKCREATTHREAD (STARTCONTEXT);

IF (psgetcurrentprocess () -> pcb.usertime == 0)

{

PsgetCurrentProcess () -> pcb.usertime = 1;

}

}

And DbgkCreateThread function corresponds DbgkExitThread function (ntos / dbgk / dbgkproc.c: 384) and DbgkExitProcess function (ntos / dbgk / dbgkproc.c: 439), initiating the EXIT_THREAD_DEBUG_EVENT and EXIT_PROCESS_DEBUG_EVENT events to debug server. These two functions are called by the system kernel exiting the thread (NTOS / PS / PSDelete.c: 622) calls at the appropriate time. The PSPEXITTHREAD function detects whether the current process PCB thread list is only a thread of the current thread. If there is no other thread, the DBGKEXITTHREAD function is called. Otherwise, call the DBGKEXITTHREAD function. In the Win32 system, the DLL is loaded and uninstalled, and the actual function call flow is as follows:

The following is quoted:

LoadLibrary (kernel32.dll)

LoadLibraryEx (kernel32.dll)

BaseploadLibraryAsDatafile (kernel32.dll)

NTMapViewOfsection (NTOS / MM / MapView.c: 204)

MmmapViewOfsection (NTOS / MM / MapView.c: 699)

The NTMapViewOfSecion function is called after the MMMapViewOfSECTION function (NTOS / MM / MAPVIEW.C: 699) completes the actual memory file mapping, depending on whether the marker bit of the map and the target process are the current process, it is determined whether to call the DBGKMapViewOfSECTION function (NTOS / DBGK) /DBGKPROC.C: 495) Notifying the debug server has a new image file being loaded. In response to the MMunmapViewOfSecion function (NTOS / MM / UmapView.c: 88) also calls the DBGKUNMAPVIEWOFSECTION function at the end of the function (NTOS / DBGK / DBGKPROC.C: 567) in the end of the function (NTOS / DBGK / DBGKPROC.C: 567) notification debug server An image file is uninstalled. Unlike the previous events, the OutputDebugString function (kernel32.dll) is actually achieved by exception. And interesting is that this function is an example of a suffix ANSI version to complete the actual function. OutputDebugstringA function (kernel32.dll) actually uses the RaiseException function to trigger an exception of the software exception of 0x40010006, and passed the pointer and length of the string as an exception parameter. DBGKFORWARDEXCEPTION function (NTOS / DBGK / DBGKPORT.C: 96) is called as a function that actually triggered an Exception_Debug_event debug event, is called in the system's exception (NTOS / KE / I386 / Exceptn.c: 797). The KidispatChexception function completions the core and user state's exception processing work according to the state of the abnormality being triggered. For the core state, first give the core debugger a handling opportunity, then try to distribute the frame-based SEH exception chain, no processed, then give the core debugger a chance, if it is still not being processed, you can only call KebugCheckex Function (NTOS / Ke / Bugcheck.c: 157) blue screen, huh, huh. For the user's state, or first try to make the core debugger process, if not, call the DBGKForwardException function distribution, if it is not processed, try, if it is still not processed, stop the thread and report an exception to the user. The kidispatchexception function invigo code is as follows: The following is referenced:

Void KidispatChexception (in pEXception_record exceptionrecord, in pkexception_frame exceptionframe,

In pktrap_frame trapframe, in kprocessor_mode presentmode, in boolean firstchance

{

Context contextframe;

KeconTextFromkframes (TrapFrame, & ContextFrame); // Abnormal context from core abnormal frame (FRAME) Context

IF (ExceptionRecord-> exceptioncode == status_breakpoint) // Processing Trial breakpoint INT 3

{

ContextFrame.eiP -;

}

IF (PreviousMode == KernelMode)

{

IF (firstchance == true)

{

IF (kidebugroutine && kidebugroutine (..., false)! = false) goto handle1

IF (RTLDISPATCHEXCEPTION (ExceptionRecord, & contextFrame) == true) goto handled1;

IF (kidebugroutine && kidebugroutine (..., true)! = false) goto handle1

KebugCheckex (...); // core error, with a controlled way --_- b is white, it is Deadth Blue Screen, huh, huh

}

Else // previousmode = usermode

{

IF (firstchance == true)

{

IF (kidebugroutine && kidebugroutine (..., false)! = false) goto handle1

IF (DBGKFORwardException (ExceptionRecord, true, false) goto handled2;

// Translate exception information to user mode and try distribution

}

IF (DBGKFORwardException (ExceptionRecord, true, true)

{

Goto handled2;

}

Else IF (ExceptionRecord, False, True)

{

Goto handled2;

}

Else

{

ZWTERMINATTHREAD (NTCurrentThread (), ExceptionRecord-> ExceptionCode);

KebugCheckex (...);

}

}

Handled1:

KecontextTokframes (Trapframe, ExceptionFrame, & ContextFrame,

ContextFrame.ContextFlags, PreviousMode;

Handled2:

}

The DBGKForwardException function is called for three combinations of DebugException and SecondChance parameters. DEBUGEXCEPTION Sends information to the debug port when True, otherwise send it to the exception port. At this point, we have a matter of probably aware of several common debug events. The next section will introduce the implementation ideas of win32 in the Win32 associated with these debug events and end-user status debugging.

转载请注明原文地址:https://www.9cbs.com/read-29262.html

New Post(0)