windbg - Getting Started with WinDBG - Part 2
2015-06-24 18:22
330 查看
This is a multipart series walking you through using WinDBG - we’ve gotten you off the ground with our last blog post, and now we’ll focus on it’s core functionality so that you can start debugging programs!
Part 1 - Installation, Interface, Symbols, Remote/Local Debugging, Help, Modules, and Registers
Part 2 - Breakpoints
Part 3 - Inspecting Memory, Stepping Through Programs, and General Tips and Tricks
Breakpoints can be set in software and within the CPU (hardware), let’s take a look at both:
Software breakpoints are set within WinDBG using the bp, bm, or bu commands. bp (for Break Point) is arguably the most used breakpoint command. In its most basic use, its only argument is the address at which a breakpoint should be set:
With bp, the address should be a memory location where executable code exists. While bp works on locations where data is stored, it can cause issues since the debugger is overwriting the data at that address. To be safe Microsoft suggests that if you want to break on a memory location where data is stored, you should use a different breakpoint command (ba, discussed below).
Let’s take a look at setting a software breakpoint. Here we’ll launch notepad.exe with WinDBG. By default, when the program is launched with WinDBG, it will insert a breakpoint before the entry point of the program is executed and pause the program. First we’ll get the location in memory where notepad.exe is loaded:
Next we’ll determine the program’s entry point by using !dh with the image load address:
Now we’ll set a breakpoint at it’s entry point (load address + 0x3689):
Finally we’ll tell the program to run until it encounters a breakpoint using the g command (more on this later), when the breakpoint is hit, we’ll get a notice:
Most of your debugging will likely use software breakpoints, however there are certain scenarios (read-only memory locations, breaking on data access, etc..) where you need to use hardware breakpoints.
Hardware breakpoints are set within WinDBG using the ba (Break on Access) command. In its most basic usage, it takes 3 attributes:
This command would (we’ll see soon why it doesn’t) accomplish the same thing as the previous bp example, however now we’re setting a hardware breakpoint. The first argument, e, is the type of memory access to break on (execute), while the second is the size (always 1 for execute access). The final is the address. Let’s take a look at setting a hardware breakpoint, keep in mind our load addresses are different because of the whole ASLR thing.
Due to the way Windows resets thread contexts and the place where WinDBG breaks after spawning a process, we wont be able to set a breakpoint in the same way we did in our earlier example. Previously we set our breakpoint on the program’s entry point, however if we try to do that with WinDBG we get an error:
So in order to get around this, we’ll need to use that g command and tell it to run the program until it reaches a specific memory address. This is sort of like setting a software breakpoint in behavior but isn’t exactly the same. So we’ll tell WinDBG to execute until we enter the program’s initial thread context, which will then allow us to set hardware breakpoints.
To confirm we actually set the breakpoint in CPU’s registers, we can use the r command (discussed later). We’ll use the M attribute to apply a register mask of 0x20:
In this specific example, we probably will never hit our breakpoint because it is in the entry point of the program that we’ve already reached. However if our breakpoint was on a function that was called a variety of times in the life of the program, or on a memory address where an often used variable was stored, we’d get a “Breakpoint Hit” message when the memory was accessed just as we would with a software breakpoint.
Here we have one breakpoint defined, the entry is broken into a few columns:
0 - Breakpoint ID
e - Breakpoint Status - Can be enabled or disabled.
006a3689 - Memory Address
e 1 - Memory address access flags (execute) and size - For hardware breakpoints only
0001 (0001) - Number of times the breakpoint is hit until it becomes active with the total passes in parentheses (this is for a special use case)
0:** - Thread and process information, this defines it is not a thread-specific breakpoint
notepad+0x3689 - The corresponding module and function offset associated with the memory address
The only attribute to bc is the Breakpoint ID (learned from bl). Optionally you can provide * to delete all breakpoints.
Now we know that notepad!WinMainCRTStartup is a friendly name for 006a0000 + 3689. Since there isn’t a numeric offset at the end of this friendly name, we can also infer that Symbols exist for this function. Look what happens when we check out the second instruction in this function:
This time we got a function name, notepad!_initterm_e, plus an offset (+0x61). I’m not entirely sure why WinDBG gave the offset to notepad!_initterm_e instead of notepad!WinMainCRTStartup, probably a symbol search order thing - nonetheless, we could have used a notepad!WinMainCRTStartup offset to reference the same location:
The point is that now we can use this offset as a breakpoint and those offsets are always valid even if ASLR is enabled - so we don’t have to waste time calculating addresses at every launch.
Here we’ve set up a first chance exception (sxe) when a module is loaded (ld) and defined IMM32.DLL as the specific module which triggers the exception.
We can use sx (Set Exceptions) to view the configured exceptions. If we look under the Load Module list, we’ll see that we have a break on IMM32.DLL.
To clear it we can use the sxi (Set Exception Ignore) command:
Let’s see what happens when we hit our breakpoint:
As expected, the commands were executed, showing the “Here are the values on the stack” message and the stack. Commands are chained together with a semi-colon, and be sure to escape quotes within the outer-most quotes that contain the entire command. You can even append the g command to have the commands be executed and the program to just continue. This allows you to inspect the state of the program as it runs rather than manually interrupting it every time a breakpoint is hit.
Part 1 - Installation, Interface, Symbols, Remote/Local Debugging, Help, Modules, and Registers
Part 2 - Breakpoints
Part 3 - Inspecting Memory, Stepping Through Programs, and General Tips and Tricks
Breakpoints
Breakpoints are markers associated with a particular memory address that tell the CPU to pause the program. Because programs can contain millions of assembly instructions, manually stepping through each of those instructions would take an incredibly long time. Breakpoints help speed up debugging time by allowing you to set a marker at a specific function which allows the CPU to automatically execute all the code leading up to that point. Once the breakpoint is reached, the program is paused and the debugging can commence.Breakpoints can be set in software and within the CPU (hardware), let’s take a look at both:
Software Breakpoints
Programs get loaded into memory and executed - which allows us to temporarily modify the memory associated with a program without affecting the actual executable. This is how software breakpoints work. The debugger records the assembly instruction where the breakpoint should be inserted, then silently replaces it with an INT 3 assembly instruction (0xcc) that tells the CPU to pause execution. When the breakpoint is reached, the debugger looks at the current memory address, fetches the recorded instruction, and presents it to the user. To the user it appears that the program paused on that instruction however the CPU actually had no idea it ever existed.Software breakpoints are set within WinDBG using the bp, bm, or bu commands. bp (for Break Point) is arguably the most used breakpoint command. In its most basic use, its only argument is the address at which a breakpoint should be set:
0:001> bp 00e61018
With bp, the address should be a memory location where executable code exists. While bp works on locations where data is stored, it can cause issues since the debugger is overwriting the data at that address. To be safe Microsoft suggests that if you want to break on a memory location where data is stored, you should use a different breakpoint command (ba, discussed below).
Let’s take a look at setting a software breakpoint. Here we’ll launch notepad.exe with WinDBG. By default, when the program is launched with WinDBG, it will insert a breakpoint before the entry point of the program is executed and pause the program. First we’ll get the location in memory where notepad.exe is loaded:
0:000> lmf m notepad start end module name 006a0000 006d0000 notepad notepad.exe
Next we’ll determine the program’s entry point by using !dh with the image load address:
0:000> !dh 006a0000 File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (i386) 4 number of sections 4A5BC60F time date stamp Mon Jul 13 16:41:03 2009 0 file pointer to symbol table 0 number of symbols E0 size of optional header 102 characteristics Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # 9.00 linker version A800 size of code 22400 size of initialized data 0 size of uninitialized data 3689 address of entry point 1000 base of code ----- new ----- 006a0000 image base 1000 section alignment 200 file alignment 2 subsystem (Windows GUI) 6.01 operating system version 6.01 image version 6.01 subsystem version 30000 size of image 400 size of headers 39741 checksum 00040000 size of stack reserve 00011000 size of stack commit 00100000 size of heap reserve 00001000 size of heap commit 8140 DLL characteristics Dynamic base NX compatible Terminal server aware 0 [ 0] address [size] of Export Directory A048 [ 12C] address [size] of Import Directory F000 [ 1F160] address [size] of Resource Directory 0 [ 0] address [size] of Exception Directory 0 [ 0] address [size] of Security Directory 2F000 [ E34] address [size] of Base Relocation Directory B62C [ 38] address [size] of Debug Directory 0 [ 0] address [size] of Description Directory 0 [ 0] address [size] of Special Directory 0 [ 0] address [size] of Thread Storage Directory 6D58 [ 40] address [size] of Load Configuration Directory 278 [ 128] address [size] of Bound Import Directory 1000 [ 400] address [size] of Import Address Table Directory 0 [ 0] address [size] of Delay Import Directory 0 [ 0] address [size] of COR20 Header Directory 0 [ 0] address [size] of Reserved Directory SECTION HEADER #1 .text name A68C virtual size 1000 virtual address A800 size of raw data 400 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 60000020 flags Code (no align specified) Execute Read Debug Directories(2) Type Size Address Pointer cv 24 b668 aa68 Format: RSDS, guid, 2, notepad.pdb ( 10) 4 b664 aa64 SECTION HEADER #2 .data name 2164 virtual size C000 virtual address 1000 size of raw data AC00 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0000040 flags Initialized Data (no align specified) Read Write SECTION HEADER #3 .rsrc name 1F160 virtual size F000 virtual address 1F200 size of raw data BC00 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data (no align specified) Read Only SECTION HEADER #4 .reloc name E34 virtual size 2F000 virtual address 1000 size of raw data 2AE00 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 42000040 flags Initialized Data Discardable (no align specified) Read Only
Now we’ll set a breakpoint at it’s entry point (load address + 0x3689):
0:000> bp 6a3689 0:000> bl 0 e 006a3689 0001 (0001) 0:**** notepad+0x3689
Finally we’ll tell the program to run until it encounters a breakpoint using the g command (more on this later), when the breakpoint is hit, we’ll get a notice:
1:001> g ModLoad: 76f90000 76faf000 C:\Windows\system32\IMM32.DLL ModLoad: 75900000 759cc000 C:\Windows\system32\MSCTF.dll Breakpoint 0 hit eax=77143c33 ebx=7ffdb000 ecx=00000000 edx=006a3689 esi=00000000 edi=00000000 eip=006a3689 esp=0011fac0 ebp=0011fac8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 notepad+0x3689: 006a3689 e8c5f9ffff call notepad+0x3053 (006a3053)
Most of your debugging will likely use software breakpoints, however there are certain scenarios (read-only memory locations, breaking on data access, etc..) where you need to use hardware breakpoints.
Hardware Breakpoints
Within most CPUs there are special debug registers that can be used to store the addresses of breakpoints and specific conditions on which the breakpoint should triggered (e.g. read, write, execute). Breakpoints stored here are called hardware (or processor) breakpoints. There is a very finite number of registers (usually 4) which limits the number of total hardware breakpoints that can be set. When the CPU reaches a memory address defined within the debug register and the access conditions are met, the program will pause execution.Hardware breakpoints are set within WinDBG using the ba (Break on Access) command. In its most basic usage, it takes 3 attributes:
0:001> ba e 1 00453689
This command would (we’ll see soon why it doesn’t) accomplish the same thing as the previous bp example, however now we’re setting a hardware breakpoint. The first argument, e, is the type of memory access to break on (execute), while the second is the size (always 1 for execute access). The final is the address. Let’s take a look at setting a hardware breakpoint, keep in mind our load addresses are different because of the whole ASLR thing.
Due to the way Windows resets thread contexts and the place where WinDBG breaks after spawning a process, we wont be able to set a breakpoint in the same way we did in our earlier example. Previously we set our breakpoint on the program’s entry point, however if we try to do that with WinDBG we get an error:
0:000> ba e 1 006a3689 ^ Unable to set breakpoint error The system resets thread contexts after the process breakpoint so hardware breakpoints cannot be set. Go to the executable's entry point and set it then. 'ba e 1 006a3689'
So in order to get around this, we’ll need to use that g command and tell it to run the program until it reaches a specific memory address. This is sort of like setting a software breakpoint in behavior but isn’t exactly the same. So we’ll tell WinDBG to execute until we enter the program’s initial thread context, which will then allow us to set hardware breakpoints.
0:000> g 006a3689 ModLoad: 76f90000 76faf000 C:\Windows\system32\IMM32.DLL ModLoad: 75900000 759cc000 C:\Windows\system32\MSCTF.dll *** ERROR: Module load completed but symbols could not be loaded for notepad.exe eax=77143c33 ebx=7ffdf000 ecx=00000000 edx=006a3689 esi=00000000 edi=00000000 eip=006a3689 esp=0013fbe0 ebp=0013fbe8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 notepad+0x3689: 006a3689 e8c5f9ffff call notepad+0x3053 (006a3053) 0:000> ba e 1 006a3689
To confirm we actually set the breakpoint in CPU’s registers, we can use the r command (discussed later). We’ll use the M attribute to apply a register mask of 0x20:
0:000> rM 20 dr0=00000000 dr1=00000000 dr2=00000000 dr3=00000000 dr6=00000000 dr7=00000000 notepad+0x3689: 006a3689 e8c5f9ffff call notepad+0x3053 (006a3053)
In this specific example, we probably will never hit our breakpoint because it is in the entry point of the program that we’ve already reached. However if our breakpoint was on a function that was called a variety of times in the life of the program, or on a memory address where an often used variable was stored, we’d get a “Breakpoint Hit” message when the memory was accessed just as we would with a software breakpoint.
Common Commands
Now that you have the basics of setting breakpoints, there are a handful of other breakpoint related commands that will be useful. Let’s look at a couple:Viewing Set Breakpoints
To view each of the breakpoints that have been set, you can use the bl (Breakpoint List) command.0:000> bl 0 e 006a3689 e 1 0001 (0001) 0:**** notepad+0x3689
Here we have one breakpoint defined, the entry is broken into a few columns:
0 - Breakpoint ID
e - Breakpoint Status - Can be enabled or disabled.
006a3689 - Memory Address
e 1 - Memory address access flags (execute) and size - For hardware breakpoints only
0001 (0001) - Number of times the breakpoint is hit until it becomes active with the total passes in parentheses (this is for a special use case)
0:** - Thread and process information, this defines it is not a thread-specific breakpoint
notepad+0x3689 - The corresponding module and function offset associated with the memory address
Deleting Breakpoints
To remove a breakpoint, use the bc command:0:000> bc 0
The only attribute to bc is the Breakpoint ID (learned from bl). Optionally you can provide * to delete all breakpoints.
Breakpoint Tips
There are a couple simple tips that I commonly use when setting breakpoints. Here are a few of them, please share any you have in the comments below!Calculated Addresses
The simplest breakpoint tip, is just something that you’ll learn when dealing with memory addresses within WinDBG. You can have WinDBG evaluate expressions to calculate address. For instance, in the above examples, we knew the module load address of notepad.exe and the entry point was at offset 0x3689. Rather than calculating that address ourselves, we can have WinDBG do it for us:0:000> lmf m notepad start end module name 006a0000 006d0000 notepad notepad.exe
0:000> bp 006a0000 + 3689
0:000> bl
0 e 006a3689 0001 (0001) 0:**** notepad+0x3689
Name and Offset Addresses
One of the great things about Symbols (covered in part 1 of this post) is that they give us the locations of known functions. So we can use the offsets to those known functions as addresses in our breakpoints. To figure out the offset, we can use the u (Unassemble) command within WinDBG. u will take a memory address and interpret the data at that memory address as assembly and display the corresponding mnemonics. As part of its output, u will also provide the offset to the nearest symbol:0:000> u 006a0000 + 3689 notepad+0x3689: 006a3689 e8c5f9ffff call notepad+0x3053 (006a3053) 006a368e 6a58 push 58h 006a3690 68a0376a00 push offset notepad+0x37a0 (006a37a0) 006a3695 e872040000 call notepad+0x3b0c (006a3b0c) 006a369a 33db xor ebx,ebx 006a369c 895de4 mov dword ptr [ebp-1Ch],ebx 006a369f 895dfc mov dword ptr [ebp-4],ebx 006a36a2 8d4598 lea eax,[ebp-68h]
Now we know that notepad!WinMainCRTStartup is a friendly name for 006a0000 + 3689. Since there isn’t a numeric offset at the end of this friendly name, we can also infer that Symbols exist for this function. Look what happens when we check out the second instruction in this function:
0:000> u 006a368e notepad!_initterm_e+0x61: 006a368e 6a58 push 58h
This time we got a function name, notepad!_initterm_e, plus an offset (+0x61). I’m not entirely sure why WinDBG gave the offset to notepad!_initterm_e instead of notepad!WinMainCRTStartup, probably a symbol search order thing - nonetheless, we could have used a notepad!WinMainCRTStartup offset to reference the same location:
0:000> u notepad!WinMainCRTStartup+0x5 notepad!_initterm_e+0x61: 006a368e 6a58 push 58h
The point is that now we can use this offset as a breakpoint and those offsets are always valid even if ASLR is enabled - so we don’t have to waste time calculating addresses at every launch.
0:000> bp notepad!WinMainCRTStartup+0x5 0:000> bl 0 e 006a368e 0001 (0001) 0:**** notepad!_initterm_e+0x61
Breaking On Module Load
There may be some occasions when you’d like to set a breakpoint when a module is being loaded. Unfortunately, there doesn’t appear to be an obvious way within the standard breakpoint commands to do this (let know if you know of a way in the comments). Instead a sort of “hacky” way to do this is by defining that an exception be raised when a particular module is loaded using the sxe command:0:000> Sxe ld IMM32.DLL
Here we’ve set up a first chance exception (sxe) when a module is loaded (ld) and defined IMM32.DLL as the specific module which triggers the exception.
We can use sx (Set Exceptions) to view the configured exceptions. If we look under the Load Module list, we’ll see that we have a break on IMM32.DLL.
To clear it we can use the sxi (Set Exception Ignore) command:
0:000> Sxi ld IMM32.DLL
Executing Commands
There may be certain commands that we execute every time a breakpoint is reached. For instance, say we’re always interested at what values are on the stack. We can automate this with WinDBG by building a list of commands and appending it to our breakpoint. In our example, we’ll print out some information, and use the dd command (discussed later) to show the stack. Notice how our command is referenced in the bl output as well:0:000> bp notepad!WinMainCRTStartup ".echo \"Here are the values on the stack:\n\"; dd esp;" 0:000> bl 0 e 00ae3689 0001 (0001) 0:**** notepad!WinMainCRTStartup ".echo \"Here are the values on the stack:\n\"; dd esp;"
Let’s see what happens when we hit our breakpoint:
As expected, the commands were executed, showing the “Here are the values on the stack” message and the stack. Commands are chained together with a semi-colon, and be sure to escape quotes within the outer-most quotes that contain the entire command. You can even append the g command to have the commands be executed and the program to just continue. This allows you to inspect the state of the program as it runs rather than manually interrupting it every time a breakpoint is hit.
Stay Tuned
In our next blog post we’ll cover inspecting memory and stepping through the program!References
http://blog.opensecurityresearch.com/2013/12/getting-started-with-windbg-part-2.html相关文章推荐
- 字体大小自适应纯css解决方案
- [swustoj 771] 奶牛农场
- 一个失败CEO的九大特征
- JIRA项目执行与管理方案
- 户外广告为何青睐LED显示
- Java之美[从菜鸟到高手演变]之设计模式三
- 那些传奇的天才们(一)
- 一致性哈希
- UI--学习模仿QQ未读提醒拖拽删除
- ava.util.ResourceBundle使用详解
- 汽车仪表盘的开发。
- Linux下配置Tomcat使用普通用户启动Daemon进程
- WCF webhttprequest usage
- Oracle 学习笔记
- 字符串-02. 删除字符串中的子串(20)
- HDU 1698 Just a hook 线段树
- hack在微信等webview中无法修改title的情况
- good
- arcII码为0x01,0x02作为分隔符
- ubuntu 下舒畅的使用libreoffice