410 likes | 602 Views
Debugging Applications for Microsoft .NET and Microsoft Windows John Robbins. Operating System Debugging Support and How Win32 Debuggers Work. Riddhiman Ghosh. Goal. Introduce the capabilities of Windows Operating Systems to support debugging applications.
E N D
Debugging Applicationsfor Microsoft .NET and Microsoft WindowsJohn Robbins Operating System Debugging Support and How Win32 Debuggers Work Riddhiman Ghosh
Goal • Introduce the capabilities of Windows Operating Systems to support debugging applications. • Understand how debuggers operate in the Win32 environment.
Outline • Types of Windows Debuggers • Windows Support for Debuggees • Just In-Time Debugging • UserDebuggerHotKey • Image File Execution Options • Win32 Debugging API • Steps to Debugging • Debugging Events
Outline • Real Debuggers need to… • Reading and Writing Debuggee Memory • Breakpoints and Single-Stepping • Step Into, Step Over, Step Out • Symbol Tables • Summary
User-mode Debuggers Debug user-mode applications (e.g. Visual Studio .NET debugger) Types of Debuggers User-mode Debuggers Kernel-mode
User-Mode Debuggers • Defining Characteristic – they use the Win32 Debugging API • Debuggers for languages that provide their own virtual machine, or interpreted languages don’t use the Win32 Debugging API – the languages provide their own debugging environment, e.g. the .NET CLR. • The debugger and the debuggee (the process being debugged) have a parent-child relationship, if the debugger created the process. • You can also “attach” to an already executing process and “detach” from it – here the debugger and the debuggee are independent.
User-Mode Debuggers • A debuggee can detect if it is being debugged using the API function IsDebuggerPresent • Can be used by debuggee to turn on more diagnostic information to help the debugger. • Security and IP issues
Kernel-mode Debuggers Let us debug the OS kernel (e.g. Windows KD) Types of Debuggers User-mode Debuggers Kernel-mode
Kernel-Mode Debuggers • Sit between the OS and the CPU – stopping in a kernel-mode debugger halts the entire OS. • Helpful in debugging applications with timing / synchronization problems. • Offer extremely detailed information about the state of various aspects of the OS. A must for device driver development. • 3 kernel-mode debuggers mentioned: Windows Kernel Debugger(KD), WinDBG, and SoftICE.
Kernel-Mode Debuggers • Usually debugger and debuggee are on separate machines, as in the case of KD and WinDBG. • The 2 machines communicate through the COM port via a null modem connection or through the FireWire port. • A utility on the target machine does just enough to control the CPU so that the OS can be debugged. The “debugger” on the host machine controls the OS on the target machine remotely.
Windows Support for Debuggees • Just In-Time Debugging • When an application crashes, Windows can automatically start a debugger to debug the crashed application • Looks in the registry to determine which debugger it should call (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ AeDebug )
Windows Support for Debuggees • “Quick Break” Keys • Console applications can be debugged by pressing Ctrl-C or Ctrl-Break. Similarly Windows now provides Quick Break Keys to launch a debugger for GUI applications. • UserDebuggerHotKey registry entry specifies the key combination, using which you can pop the debugger. (Default is F12)
Windows Support for Debuggees • Image File Execution Options • Windows also allows applications to automatically start in a debugger whenever they are executed. • Useful if to debug applications started by another process, e.g. Windows services • Set the appropriate registry key to enable this feature.
Win32 Debugging API • Native code debuggers use the services provided by the Win32 API • Provides functionality load a program for debugging (or attach to an existing program) • Obtain information of interest about the process being debugged • Provides notifications when debugging-related events are generated in the debuggee – process or thread starting or exiting, DLLs being loaded or unloaded • Allows you to read from and write to debuggee memory and instruction stream.
Steps to Debugging • The Debugger creates the debuggee as a child process CreateProcess(...DEBUG_ONLY_THIS_PROCESS…); Or • The Debugger attaches to an existing process DebugActiveProcess(ProcessID);
Steps to Debugging • Begin monitoring debugging notifications • Call the WaitForDebugEvent(…) function. • The calling thread is blocked until a “debug event” is fired • Handle the event • When an event is fired debuggee execution is suspended and control is transferred from the debuggee to the debugger • Take necessary action -- examine process state, get thread context and CPU registers, read process memory, modify instruction stream, etc.
Steps to Debugging • Allow the debuggee to continue execution • When the debugger is processing debug events it has full control over the debuggee – the OS stops all debuggee threads and won’t schedule them until the debugger says so. • Call ContinueDebugEvent(…);
Debugging Events • Windows defines several debug events that are fired during the lifetime of the debuggee • The debugger waiting in the debug loop is notified of these events
Debugging Events • CREATE_PROCESS_DEBUG_EVENT • First event generated by the kernel for a process just before it begins executing in user-mode • CREATE_THREAD_DEBUG_EVENT • Generated whenever a new thread is created in a process being debugged • EXCEPTION_DEBUG_EVENT • Generated whenever an exception occurs in the process being debugged, including the DBG_CTRL_C exception
Debugging Events • EXIT_PROCESS_DEBUG_EVENT • Fired when a process exits • EXIT_THREAD_DEBUG_EVENT • Fired whenever a thread that is a part of the process being debugged exits • LOAD_DLL_DEBUG_EVENT • Fired each time the debuggee loads a DLL. Can be used by the debugger to load the symbol table corresponding to the DLL.
Debugging Events • UNLOAD_DLL_DEBUG_EVENT • Fired whenever a process unloads a DLL. Can be used to unload corresponding loaded symbol tables. • OUTPUT_DEBUG_STRING_EVENT • Fired in response to the debuggee making the OutputDebugString API call
Debugging Events • The CREATE_PROCESS_DEBUG_EVENT event indicates that the process was loaded and not executed. • The EXCEPTION_DEBUG_EVENT is fired before the first instruction of the process is executed – called the initial breakpoint. • The first time this event is fired, we call ContinueDebugEvent(DBG_CONTINUE) “start” the debuggee process.
MinDBG STARTUPINFO stStartInfo; PROCESS_INFORMATION stProcessInfo; memset( &stStartInfo, NULL, sizeof(STARTUPINFO) ); memset( &stProcessInfo,NULL,sizeof(PROCESS_INFORMATION)); stStartInfo.cb = sizeof (STARTUPINFO); BOOL bRet = CreateProcess (NULL,szCmdLine, NULL,NULL, FALSE,CREATE_NEW_CONSOLE | DEBUG_ONLY_THIS_PROCESS , NULL, &stStartInfo, &stProcessInfo) ;
MinDBG DEBUG_EVENT stDE; // Loop until told to stop. while ( TRUE == bContinue ) { // Pause until a debug event notification happens. bContinue = WaitForDebugEvent ( &stDE , INFINITE ) ; switch ( stDE.dwDebugEventCode ) { case CREATE_PROCESS_DEBUG_EVENT : //handle event ... case EXIT_PROCESS_DEBUG_EVENT : ... case LOAD_DLL_DEBUG_EVENT : ... } ContinueDebugEvent ( stDE.dwProcessId , stDE.dwThreadId , DBG_CONTINUE ) ;
Real Debuggers need to… • Read and Write Memory • Reading from and writing to a debuggee process’ memory space is supported through the ReadProcessMemory and WriteProcessMemory API functions (e.g. modifying debuggee code) • Support Debuggees calling OutputDebugString(…) • Used by the debuggee to send a string to the debugger • Get or set current context or CPU registers using GetThreadContext(…) and SetThreadContext(…)
Real Debuggers need to… • Set Breakpoints • Debuggers use breakpoints extensively behind the scenes to control their debuggees (e.g. while stepping over a function call, “running to cursor”, or to Break execution) • A breakpoint corresponds to a “breakpoint instruction” – the instruction mnemonic is ‘INT 3’ on the Pentium (0xCC is the opcode).
Steps to set a breakpoint • Locate memory address where you want to set the breakpoint • Save the value of the opcode at that location so that it can be restored later
Steps to set a breakpoint • Locate memory address where you want to set the breakpoint • Save the value of the opcode at that location so that it can be restored later • Overwrite the memory location in question with the opcode of the breakpoint instruction (0xCC)
Steps to set a breakpoint • On executing the breakpoint exception the CPU throws the EXCEPTION_BREAKPOINT exception to the debugger, debuggee is suspended. • If user decides to continue execution debugger has to restore state of the program since it overwrote a portion of the memory with the breakpoint instruction. • The instruction pointer is moved back to the breakpoint memory address and its opcode is restored from the saved value, and execution is resumed.
Steps to set a breakpoint • We have to again write the breakpoint instruction into the said memory location so that we can stop at it again in the future. • After restoring the original code, the debugger sets the Trap Flag of the CPU turning CPU single-step execution on. • This fires an EXCEPTION_SINGLE_STEP after the restored instruction has been executed – we then overwrite the instruction again with the breakpoint opcode and turn single-stepping off.
Real Debuggers need to… • Access Symbol Information • Reading symbol tables and interpreting symbol information is central to a debugger • Debuggers are not limited to assembly-language level debugging –– debugging symbols are what turn hex numbers into source file lines, function names and variable names.
Different Symbol Formats • COFF (Common Object File Format) • Introduced with the first version of Windows NT. Subset of a larger specification used by UNIX vendors • Not supported by Visual Studio.NET (VC++ 6 was the last MS compiler to support COFF)
Different Symbol Formats • C7/CodeView • First appeared in the early MS-DOS days. • C7 format was self-contained in the executable itself. Hence large binaries. • Not supported by Visual Studio.NET (VC++ 6 was the last MS compiler to support C7)
Different Symbol Formats • PDB (Program Database) • Most common symbol format used today. • Supported by the current MS compilers for C++, C#, and VB.NET • Symbol information is stored separate from the binary in .PDB files • .DBG files hold other types of symbol information such as COFF or C7. Supplied with Windows since they are needed by older debuggers.
Debug Help Library • Dealing with symbol tables is difficult. Most debuggers use the debug help library, DbgHelp supplied by Microsoft. • Contains debugging support routines to allow you to work with executable images in PE format. • Robbins says developing a wrapper around the DbgHelp symbol engine was the most challenging piece of code for this book.
Debug Help Library • Sample routines • SymInitialize: initializes the symbol handler for a process by automatically searching for and loading symbol tables • SymEnumLines: enumerates all lines in the specified module. • SymEnumTypes: enumerates all user-defined types. • SymFromAddr: retrieves symbol information for the specified address. • MiniDumpWriteDump: writes user-mode minidump information (crashdump files containing subset of the process context) • StackWalk64: provides a method for obtaining a stack trace
Real Debuggers need to… • Step Into, Step Over and Step Out • “One-shot” breakpoints used. Requires source and disassembly views. • Step Into • The debugger knows which source line you are currently on, and uses the symbol engine to determine the address of the next line to execute. • Does a partial disassembly to determine if the line is a call instruction. If yes, sets a one-shot breakpoint there and resumes execution.
Real Debuggers need to… • Step Into, Step Over and Step Out • Step Over • Similar to Step Into. Places one-shot breakpoint after the call. • Step Out • Debugger walks the stack to find the return address of the current function. • Sets a one-shot breakpoint on that address.
Samples • MinDBG - minimalist • WDBG – not a “real” debugger but has a lot of functionality
Summary • Discussed different types of debuggers • Presented the core of the Win32 Debugging API • Offered an overview of what debuggers do and how they do it • Presented debugger samples