Writing a Debugger - Part 2: The Debug Loop
2009-12-30 18:49
246 查看
Introduction
Windows NT (and successive operating system versions) expose a set of API functions and structures for debugging a running process. This article shows how these can be accessed from Visual Basic (version 5 or 6). It is recommended that the article Inside the executable: an introduction to the Portable Executable format for VB programmers be read in conjunction with this article, and the source code attached to it has the code for this article included.Getting a Process to Debug
There are two ways to get a process to debug. Either:attach a debugger to a process that is already running
or:
start a new process with a debugger attached to it.
Starting a New Process with a Debugger Attached
To start a process, you can use theCreateProcessAPI call:
Collapse
Copy Code
Private Declare Function CreateProcessLib _
"kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, _
ByVal lpCommandLine As String, _
ByVal lpProcessAttributes As Long,_
ByVal lpThreadAttributes As Long,_
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As ProcessCreationFlags, _
ByVal lpEnvironment As Long, _
ByVal lpCurrentDirectory As String, _
lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION) _
As Long
In addition to the functionality of the Shell command, this allows you to specify additional flags that affect how the process is created:
Collapse
Copy Code
Public Enum ProcessCreationFlags DEBUG_PROCESS = &H1 DEBUG_ONLY_THIS_PROCESS = &H2 CREATE_SUSPENDED = &H4 DETACHED_PROCESS = &H8 CREATE_NEW_CONSOLE = &H10 NORMAL_PRIORITY_CLASS = &H20 IDLE_PRIORITY_CLASS = &H40 HIGH_PRIORITY_CLASS = &H80 REALTIME_PRIORITY_CLASS = &H100 CREATE_NEW_PROCESS_GROUP = &H200 CREATE_UNICODE_ENVIRONMENT = &H400 CREATE_SEPARATE_WOW_VDM = &H800 CREATE_SHARED_WOW_VDM = &H1000 CREATE_FORCEDOS = &H2000 CREATE_DEFAULT_ERROR_MODE = &H4000000 CREATE_NO_WINDOW = &H8000000 End Enum
In order to start the process with an attached debugger, you specify the flags DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS.
Attaching a Debugger to an Existing Process
To attach a debugger to a process which is already running, you need to obtain a handle to it and then attach a debugger using theDebugActiveProcessAPI call:
Collapse
Copy Code
Private Declare Function DebugActiveProcessLib "kernel32" _
(ByVal dwProcessId As Long) As Long
The Debug Loop
Once you have attached your debugger to the process, you need to go into a debug loop. This consists of waiting for a debug event, processing the event when it comes in and then allowing the debugee to continue.Waiting for a Debug Event to Occur
To wait for a debug event, you call theWaitForDebugEventAPI call:
Collapse
Copy Code
Private Declare Function WaitForDebugEventLib "kernel32" _
(lpDebugEvent As DEBUG_EVENT_BUFFER, _
ByVal dwMilliseconds As Long) As Long
This will return
TRUEwhen a debug event has occurred and fill out the
DEBUG_EVENT_...structure, which depends on what event occurred but always starts with a
DEBUG_EVENT_HEADER:
Collapse
Copy Code
Private Type DEBUG_EVENT_HEADER dwDebugEventCode As DebugEventTypes dwProcessId As Long dwThreadId As Long End Type
Processing the Debug Event
How you deal with a debug event depends, naturally enough, on what event occurred. The event types are:Collapse
Copy Code
Public Enum DebugEventTypes EXCEPTION_DEBUG_EVENT = 1& CREATE_THREAD_DEBUG_EVENT = 2& CREATE_PROCESS_DEBUG_EVENT = 3& EXIT_THREAD_DEBUG_EVENT = 4& EXIT_PROCESS_DEBUG_EVENT = 5& LOAD_DLL_DEBUG_EVENT = 6& UNLOAD_DLL_DEBUG_EVENT = 7& OUTPUT_DEBUG_STRING_EVENT = 8& RIP_EVENT = 9& End Enum
EXCEPTION_DEBUG_EVENT
This debug event is thrown whenever an exception occurs in the application being debugged. For example, if there was code in that application that was attempting to divide by zero then you would get an
EXCEPTION_DEBUG_EVENT. The buffer that is passed back for this event is:
Collapse
Copy Code
Public Enum ExceptionCodes EXCEPTION_GUARD_PAGE_VIOLATION = &H80000001 EXCEPTION_DATATYPE_MISALIGNMENT = &H80000002 EXCEPTION_BREAKPOINT = &H80000003 EXCEPTION_SINGLE_STEP = &H80000004 EXCEPTION_ACCESS_VIOLATION = &HC0000005 EXCEPTION_IN_PAGE_ERROR = &HC0000006 EXCEPTION_INVALID_HANDLE = &HC0000008 EXCEPTION_NO_MEMORY = &HC0000017 EXCEPTION_ILLEGAL_INSTRUCTION = &HC000001D EXCEPTION_NONCONTINUABLE_EXCEPTION = &HC0000025 EXCEPTION_INVALID_DISPOSITION = &HC0000026 EXCEPTION_ARRAY_BOUNDS_EXCEEDED = &HC000008C EXCEPTION_FLOAT_DENORMAL_OPERAND = &HC000008D EXCEPTION_FLOAT_DIVIDE_BY_ZERO = &HC000008E EXCEPTION_FLOAT_INEXACT_RESULT = &HC000008F EXCEPTION_FLOAT_INVALID_OPERATION = &HC0000090 EXCEPTION_FLOAT_OVERFLOW = &HC0000091 EXCEPTION_FLOAT_STACK_CHECK = &HC0000092 EXCEPTION_FLOAT_UNDERFLOW = &HC0000093 EXCEPTION_INTEGER_DIVIDE_BY_ZERO = &HC0000094 EXCEPTION_INTEGER_OVERFLOW = &HC0000095 EXCEPTION_PRIVILEGED_INSTRUCTION = &HC0000096 EXCEPTION_STACK_OVERFLOW = &HC00000FD EXCEPTION_CONTROL_C_EXIT = &HC000013A End Enum Public Enum ExceptionFlags EXCEPTION_CONTINUABLE = 0 EXCEPTION_NONCONTINUABLE = 1 '// Noncontinuable exception End Enum Private Type DEBUG_EXCEPTION_DEBUG_INFO Header As DEBUG_EVENT_HEADER ExceptionCode As ExceptionCodes ExceptionFlags As ExceptionFlags pExceptionRecord As Long ExceptionAddress As Long NumberParameters As Long ExceptionInformation(EXCEPTION_MAXIMUM_PARAMETERS) As Long dwFirstChance As Long End Type
The exception flags tell you if it is possible to resume from the exception or not.
CREATE_THREAD_DEBUG_EVENT
This event occurs whenever a new thread is created by the debugee application. The buffer that is passed in is:
Collapse
Copy Code
Private Type DEBUG_CREATE_THREAD_DEBUG_INFO Header As DEBUG_EVENT_HEADER hThread As Long lpThreadLocalBase As Long lpStartAddress As Long End Type
This gives you a thread handle (for thread control API calls) and the base address and start address of the thread in the debugee process which is useful for analyzing the memory of that application.
CREATE_PROCESS_DEBUG_EVENT
This event occurs when the process is created. The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_CREATE_PROCESS_DEBUG_INFO Header As DEBUG_EVENT_HEADER hfile As Long hProcess As Long hThread As Long lpBaseOfImage As Long dwDebugInfoFileOffset As Long nDebugInfoSize As Long lpThreadLocalBase As Long lpStartAddress As Long lpImageName As Long fUnicode As Integer End Type
You can use the file handle passed in as part of this buffer to find the different parts of the process (imports section, exports, debug information, etc.) as per this article.
EXIT_THREAD_DEBUG_EVENT
This event occurs when a thread exits. The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_EXIT_THREAD_DEBUG_INFO Header As DEBUG_EVENT_HEADER dwExitCode As Long End Type
The exit code is whatever the thread set it to but is usually set to be non zero if an error caused the thread exit.
EXIT_PROCESS_DEBUG_EVENT
This event occurs when the process exits. The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_EXIT_PROCESS_DEBUG_INFO Header As DEBUG_EVENT_HEADER dwExitCode As Long End Type
The exit code is whatever the process set it to but is usually set to be non zero if an error caused the thread exit. You should stop the debug loop after you receive this event.
LOAD_DLL_DEBUG_EVENT
This event occurs when the application being debugged loads a dynamic link library. The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_LOAD_DLL_DEBUG_INFO Header As DEBUG_EVENT_HEADER hfile As Long lpBaseOfDllAs Long dwDebugInfoFileOffset As Long nDebugInfoSize As Long lpImageName As Long fUnicode As Integer End Type
You can use the file handle passed in as part of this buffer to find the different parts of the DLL (imports section, exports, debug information, etc.) as per this article.
UNLOAD_DLL_DEBUG_EVENT
This event occurs when the process being debugged unloads a DLL it had loaded. The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_UNLOAD_DLL_DEBUG_INFO Header As DEBUG_EVENT_HEADER lpBaseOfDllAs Long End Type
And you can use the
lpBaseOfDllvalue to identify which DLL was unloaded.
OUTPUT_DEBUG_STRING_EVENT
This event occurs when the debugee calls the API call
OutputDebugStringto send debugging information to a debugger (where one is attached). The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_OUTPUT_DEBUG_STRING_INFO Header As DEBUG_EVENT_HEADER lpDebugStringData As Long fUnicode As Integer nDebugStringLength As Integer End Type
And you can read the string from the debugee using the
ReadProcessMemoryAPI call.
RIP_EVENT
This occurs if your process being debugged dies unexpectedly. The buffer passed in is:
Collapse
Copy Code
Private Type DEBUG_RIP_INFO Header As DEBUG_EVENT_HEADER dwError As Long dwType As Long End Type
Resuming the Debugee
Once you have extracted the information you need form the debug event, you need to resume the debugee so that it can continue running. To do this, you call theContinueDebugEventAPI call:
Collapse
Copy Code
Public Enum DebugStates
DBG_CONTINUE = &H10002
DBG_TERMINATE_THREAD = &H40010003
DBG_TERMINATE_PROCESS = &H40010004
DBG_CONTROL_C = &H40010005
DBG_CONTROL_BREAK = &H40010008
DBG_EXCEPTION_NOT_HANDLED = &H80010001
End Enum
Private Declare Function ContinueDebugEventLib "kernel32" _
(ByVal dwProcessId As Long, _
ByVal dwThreadId As Long, _
ByVal dwContinueStatus As DebugStates) As Long
Further Development
To expand on this framework and create a full debugger requires the ability to walk the memory and stack of the process being debugged and also to set breakpoints. I hope to get to this in the next article.相关文章推荐
- Debug Tutorial Part 6: Navigating The Kernel Debugger
- Debug Tutorial Part 4: Writing WINDBG Extensions
- Using the Chrome Debugger Tools, part 3: The Sources Tab
- Exploit writing tutorial part 10 : Chaining DEP with ROP – the Rubik’s[TM] Cube
- (Visual Studio)Part 4: Setting up Code for the Debugger
- Visual Studio cannot debug managed applications because a kernel debugger is enabled on the system
- Debugger Engine API - Writing a Debugging Tools for Windows Extension, Part 2: Output (最无趣的一篇)
- Exploit writing tutorial part 5 : How debugger modules & plugins can speed up basic exploit developm
- is waiting for the debugger on port 8100" doing Run or Debug
- Debugger Engine API - Writing a Debugging Tools for Windows Extension, Part 3: Clients and Callbacks
- 在 Eclipse 中启动 Debug 模式时提示 Can not find a free socket for the debugger in eclipse
- Waht's new in the VS2005 debugger? -- Remote debug enhancement
- [已解决]Android 5.1 上面进行debug的时候 debug 的界面总是不消失 一直显示 Application is waiting for the debugger to attach
- (Visual Studio)Part 2: Setting up the IDESetting up the IDE for the debugger
- The linux command line--part 4 Writing Shell Scripts
- Debug 切换 Release 编译i The APK file does not exist on disk.Error while Installing APK
- The source file is different from when the module was built. Would you like the debugger to use it anyway?
- Writing Efficient CSS for use in the Mozilla UI
- Using the SharePoint 2010 Client Object Model_part_1