Debugging Applications for Microsoft .NET and Microsoft Windows

Download Report

Transcript Debugging Applications for Microsoft .NET and Microsoft Windows

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.
 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
Types of Debuggers
User-mode
User-mode
Debuggers
Debug user-mode
applications (e.g.
Visual Studio .NET
debugger)
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
Types of Debuggers
User-mode
Kernel-mode
Debuggers
Let us debug the
OS kernel (e.g.
Windows KD)
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
0x20
0xA2
0xCB
0x20
0xD0
0x30
0x2F
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)
0x20
0xA2
0xCB
0xCC
0xD0
0x30
0x2F
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 assemblylanguage 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