Twenty-eight lesson: Win32 debugging API first part
In this tutorial, we will learn Win32 to provide the developer's primitive for debugging. At the end of the tutorial, we will learn how to debug a process. Download example.
theory:
Win32 has some bokeh API that provides features equivalent to the debugger. They are called Win32 debug API (or primitive). Using these APIs, we can:
Load a program or bundled a low-level information that is running on a running program for debooting to obtain debugging programs, such as process ID, entry address, image base address, and the like. When the event is related to the event, notification, such as process / The start / end of the thread, DLL loading / release, etc. Modify the debugged process or thread
In short, we can use these API to write a simple debugger. Because this topic is too large, I divide it into several parts, and this tutorial is its first part. In this tutorial, I will explain some Basic Concepts and Win32 Debug API's Rough Framework. The steps to use the Win32 debug API are as follows:
Create a process or bundled into a running process. This is the first step in using the Win32 debug API. Since our program is to play a debugger role, we have to find a program for debugging. A debugged program is Debuggee can be obtained in two ways:
Create a Debuggee process via createProcess. To create a debugged process, you must specify the debug_process flag. This flag tells Windows We want to debug this process. When you have important events related to debugging in Debuggee (debug event), Windows will give us Program Send Notification. DEBUGEE will hang immediately to wait for our program ready. If Debuggee creates a child process, Windows will send a notice to our program for debug events in each child. This feature is usually It is necessary. We can ban it by specifying debug_only_this_process, with debug_process flags. We can also bundle it to a running process with the debugActiveProcess flag. Waiting for debugging events. After getting a debuggee process, the main thread of Debuggee is hanging From this situation, this situation will continue until our program call WaitFordeBugevent. This function is similar, for example, it blocks the call thread until the wait event occurs. For this function, it waits to send debugging by Windows the event which is defined below are: WaitForDebugEvent proto lpDebugEvent: DWORD, dwMilliseconds: DWORD lpDebugEvent is the address of a DEBUG_EVENT this structure will be filled debug information about events in the debuggee dwMilliseconds the waiting time debug event function to. For milliseconds. If this time does not debug event, WaitFordeBuGevent returns the caller. On the other hand, if this parameter is specified as the Infinite constant, the function will wait until the debugging event occurs. Now let's take a look at the debug_event structure. Debug_event struct dwdebugEventcode DD? DWPROCESSID DD? DWTHREADID DD? U Debugstruct <> Debug_Event Ends DwdeBugeventcode This value specifies the type of debug event waiting to occur. Because there are many types of events, our program wants to check this value, know the type of event to occur And respond. This value may be as follows:
The value of the value crete_process_debug_event process is created. When the Debuggee process is created (yet run) or our program just takes the event when debugactiveProcess is bundled into a running process. This is the first event that our program should get. .Exit_process_debug_event process Exit .create_thead_debug_event When a new thread is created in a DEUGEE process or the first time I bundled the process in the process of running, it should be noted that the notification will not be received when the debugge's main thread is created. EXIT_THREAD_DEBUG_EVENTDEBUGEEE When the thread exits the event. DEBUGEE's main thread does not receive this notification when exiting. We can think that debuggee's main thread is synonymous with the Debugge process. So, when our program sees the Create_Process_Debug_event flag, the main thread Say, it is crete_thread_debug_event flag. Load_dll_debug_eventdebuggee is loaded into a DLL. When the PE loader is first decomposed by the link to the DLL, we will receive this event. (When the calling creageprocess is loaded into debuggee) and when Debuggee calls LoadLibrary The incident occurred when the run_dll_debug_event is unmissive from Debuggee. Exception_debug_event occurs during debuggee. Note: This event only occurs once before Debuggee starts its first instruction. Exception is actually a debug interrupt ( int 3h). If you want to restore debuggee things to DBG_CONTINUE call sign ContinueDebugEvent function. Do not use DBG_EXCEPTION_NOT_HANDLED flag otherwise debuggee will refuse to run under NT (works well under Win98) .OUTPUT_DEBUG_STRING_EVENT when the debuggee calls DebugOutputString function to send a message to our program This event occurs when the string is. RIP_EVENT system debugging error DWProcessID and DWTHREADID Process and thread IDs in debug events occur. We can use these values as the process or thread of our interested process or thread. Remember if we use createProcess to load DE BUGGEE, we can still get Debuggee processes and threads in the Process_info structure. We can use these values to distinguish between debugging events that happen in debuggee or in its child process (when do not specify the debug_only_this_process flag). u is a union, Contains more information for debugging events. It can be the following structure according to DwdeBugeventCode above.
dwDebugEventCodeu CREATE_PROCESS_DEBUG_INFO explanation of the structure EXIT_PROCESS_DEBUG_EVENT CREATE_PROCESS_DEBUG_EVENT named CreateProcessInfo CREATE_THREAD_DEBUG_INFO structure EXIT_THREAD_DEBUG_EVENT EXIT_PROCESS_DEBUG_INFO structure called the ExitProcess CREATE_THREAD_DEBUG_EVENT structure named CreateThread of EXIT_THREAD_DEBUG_EVENT LOAD_DLL_DEBUG_EVENT named ExitThread named LoadDll LOAD_DLL_DEBUG_INFO structure of the UNLOAD_DLL_DEBUG_INFO UNLOAD_DLL_DEBUG_EVENT named UnloadDll EXCEPTION_DEBUG_EVENT structure called the Exception RIP_INFO structure EXCEPTION_DEBUG_INFO structure OUTPUT_DEBUG_STRING_EVENT called DebugString of OUTPUT_DEBUG_STRING_INFO structure RIP_EVENT called RipInfo I can not speak for all the details of these structures in this tutorial, just to talk in detail about the CREATE_PROCESS_DEBUG_INFO structure. suppose we WaitForDebugEvent program calls the function and returns, The first thing we have to do is to check the value in dwdebugeventcode to see the type of debugging event in the Debuggee process. For example, if the value of dwdebugeventcode is create_process_debug_event, you can think that the member of u is CreateProcessInfo and use U.CreateProcessInfo To access. Do respond to debug events in our program. When WaitFordeBugevent returns, this means that debug events have occurred or timeouts in the Debuggee process. So our programs have to check DWDebugeventcode to make an appropriate response. Here Some icons to process Windows messages: Select and ignore messages by the user. Continue to run Debuggee. When the debug event happens, Windows hangs Debuggee, so when we handle the debug event, let D ebuggee continue to call ContinueDebugEvent function to complete this process ContinueDebugEvent proto dwProcessId:.. DWORD, dwThreadId: DWORD, dwContinueStatus: DWORD This function resume the thread of the debugging event suspended thread .dwProcessId and dwThreadId is to be restored and the process ID Thread ID, usually these two values from DWProcessID and DWTHREADID members from the debug_event structure. DWContinuestUS shows how to continue reporting the thread of the debug event. Possible value has two: DBG_CONTINUE and DBG_EXCEPTION_NOT_HANDLED. Most debugging events, these two Value is the same: recovery thread. The only exception is exception_debug_event, if the thread report has an exception debug event, which means an exception occurred in the debuggee thread. If you specify DBG_Continue, the thread will ignore its own exception handling part And continue. In this case, our program must check and handle exception before recovering the thread in DBG_CONTINUE, otherwise abnormal will continue to happen to happen to endlessly .... If we specify the DBG_EXCEPTION_NOT_HANDLED value, tell Windows our The program does not deal with exception:
Windows will handle exceptions using Debuggee's default exception handle. In turn, if our programs don't consider an exception, and debug events point to an exception in the Debuggee process, you should call ContinuedEBugevent functions with DBG_Continue flags. Otherwise, our The program must call ContinueDeBugevent in DBG_EXCEPTION_NOT_HANDLED. But in this case, the DBG_CONTINUE flag must be used: The first Exception_Debug_event event in the ExceptionCode member is exception_debug_event event. When Debuggee begins to execute its first instruction, our function will Accept an exception debug event. It is actually a debug interrupt (INT 3h). If we call ContinueDeBugevent with DBG_EXCEPTION_NOT_HANDLED to respond to debug events, Windows NT will refuse to perform Debuggee (because it does not have an abnormality). So in this case, To tell Windows with the DBG_Continue flag We want the thread to continue. Continue to the steps to loop from the Debuggee process. Our program must end until the debuggee ends until the debuggee is ended. The cycle is generally below: .While True Invoke WaitForDebugEvent, addr DebugEvent, INFINITE .break .if DebugEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT
Create a process or bundled our program to the process in the process. Waiting to debug event respond to debug events. Continue to perform debuggee. Continue this endless loop until the debuggee process
example:
This example debugs a Win32 program and displays such as process handle, process ID, image base, and so on.
.386 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/kernel32.inc include /masm32/include/comdlg32.inc include /masm32/include/user32.inc includelib / masm32 /lib/kernel32.lib includelib /masm32/lib/comdlg32.lib includelib /masm32/lib/user32.lib .data AppName db "Win32 Debug Example no.1", 0 ofn OPENFILENAME <> FilterString db "Executable Files", 0 , "*. EXE", 0 DB "All Files", 0, "*. *", 0, 0 EXITPROC DB "The Debuggee EXITS", 0 NewThread DB "A New Thread Is Created", 0 Endthread DB "A Thread IS destroyed, 0 ProcessInfo DB "File Handle:% LX", 0DH, 0AH DB "Process Handle:% LX", 0DH, 0AH DB "Thread Handle:% LX", 0DH, 0AH DB "Image Base:% LX" 0DH, 0AH DB "Start Address:% lx", 0 .data? Buffer DB 512 DUP (?) StartInfo Startupinfo <> pi process_information <> dbevent debug_event <> .code start: Mov off.lstructSize, Sizeof OFN MOV OFN. LPSTRFILTER, OFFSET FILTERSTRING MOV OFN.LPSTRFILE, OFFSET BUFFER MOV O fn.nMaxFile, 512 mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY invoke GetOpenFileName, ADDR ofn .if eax == TRUE invoke GetStartupInfo, addr startinfo invoke CreateProcess, addr buffer, NULL, NULL, NULL, FALSE, DEBUG_PROCESS DEBUG_ONLY_THIS_PROCESS, NULL, NULL, addr startinfo, addr pi .while TRUE invoke WaitForDebugEvent, addr DBEvent, INFINITE .if DBEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT invoke MessageBox, 0, addr ExitProc, addr AppName, MB_OK
MB_ICONINFORMATION .break .elseif DBEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT invoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFile, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo. lpBaseOfImage, DBEvent.u.CreateProcessInfo.lpStartAddress invoke MessageBox, 0, addr buffer, addr AppName, MB_OK MB_ICONINFORMATION .elseif DBEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT invoke ContinueDebugEvent, DBEvent. dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE .continue .endif .elseif DBEvent.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT invoke MessageBox, 0, addr newThread, addr AppName, MB_OK MB_ICONINFORMATION .elseif DBEvent.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT invoke MessageBox, 0, addr EndThread, addr Appname, MB_OK MB_ICONInformation .endif invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED .endw invoke CloseHandle, pi.hProcess invoke CloseHandle, pi.hThread .endif invoke ExitProcess, 0 end start Analysis:
The program first fills the OpenFileName structure, calling getopenFileName to allow users to choose the program to debug.
Invoke GetStartupinfo, Addr StartInfo Invoke CreateProcess, Addr Buffer, Null, Null, Null, False, Debug_process Debug_only_this_process, Null, Null, Addr StartInfo, Addr Pi
When receiving the user selection, call the CreateProcess loader. And call GetStartupInfo to populate the StartupInfo structure with the default value. Note We combine the debug_process flag with the debug_only_this_process flag to debug this program, not including the child process.
.While True Invoke Waitfordebugevent, Addr DBEvent, Infinite
After the debuggee is loaded, we call WaitFordeBugevent to enter the endless debug loop. WaitFordeBuGevent returns when debug events occur in Debuggee, because we specify the Infinite as the second parameter. When the debug event occurs, WaitFordeBugevent returns and populates and populates the DBEVENT structure. .IF dbevent.dwdebugeventcode == exit_process_debug_event invoke messagebox, 0, addr exitproc, addr appname, MB_OK MB_ICONIONFORMATION .BREAK
We must first check the value of dwdebugeventcode. If you are exit_process_debug_event, use a message box to display "The debuggee eXits" and exit the debug loop.
.elseif DBEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT invoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFile, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo.lpBaseOfImage, DBEvent .u.createprocessinfo.lpstartaddress Invoke Messagebox, 0, Addr Buffer, Addr Appname, MB_OK MB_ICONInInformation
If the value of dwdebugeventcode is CREATE_PROCESS_DEBUG_EVENT, we display some underlying information of interest in the message box. This information is obtained from u.createProcessInfo. CreateProcessInfo is a structure of create_process_debug_info type. You can check the Win32 API to get it more information E .
.elseif DBEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE .continue .endif
If the value of dwdebugeventcode is exception_debug_event, we have to check the exception type. It is a lot of structural nested, but we can get an exception type from the ExceptionCode member. If exceptioncode is exception_breakpoint and is the first time (or We have known that there is no int 3h directive in Deuggee, which we can safely assume this anomalies when DEBUGEE wants to perform the first instruction. After we do these processing, you can use DBG_Continue to call ContinueDeBugevent to continue DEBUGGEEEEEVENT. Next We Continue to wait for the next debug event.
.elseif DBEvent.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT invoke MessageBox, 0, addr NewThread, addr AppName, MB_OK MB_ICONINFORMATION .elseif DBEvent.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT invoke MessageBox, 0, addr EndThread, addr AppName, MB_OK MB_ICONINFORMATION .endif value if dwDebugEventCode For CREATE_THREAD_DEBUG_EVENT or EXIT_THREAD_DEBUG_EVENT, our program displays a message box.
Invoke ContinueDebugevent, DBEvent.dwprocessid, DBEvent.dwthreadId, DBG_EXCEPTION_NOT_HANDED .Endw.
In addition to the exception_debug_event discussed above, call the ContinueDeBugevent function to restore the execution of the debuggee with the DBG_EXCEPTION_NOT_HANDLED flag.
Invoke Closehandle, Pi.hprocess Invoke CloseHandle, Pi.hthread
When the debuggee ends, we jump out of the debug loop, then turn off the debuggee thread and process handle. Close these handles does not mean to close these processes and threads. Just say no longer uses these handles.