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