zoukankan      html  css  js  c++  java
  • windbg

    http://blog.opensecurityresearch.com/2013/12/getting-started-with-windbg-part-1.html

    http://blog.opensecurityresearch.com/2013/12/getting-started-with-windbg-part-2.html

    http://blog.opensecurityresearch.com/2013/12/getting-started-with-windbg-part-3.html

     要使用 SymChk.exe 实用程序下载 WindowsSystem32 文件夹中所有组件的符号文件,请使用以下命令行命令:

    symchk /r c:windowssystem32 /s SRV*c:symbols*http://msdl.microsoft.com/download/symbols
     
     
     
     
     
     
     
     
     
     

    Tuesday, December 17, 2013

    Getting Started with WinDBG - Part 1

     
    By Brad Antoniewicz.

     
    WinDBG is an awesome debugger. It may not have a pretty interface or black background by default, but it still one of the most powerful and stable Windows debuggers out there. In this article I'll introduce you to the basics of WinDBG to get you off the ground running.

    This is part one of a multipart series, here's our outline of whats in store:

    • 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
    In this blog post we'll cover installing and attaching to a process, then in the next blog post we'll go over breakpoints, stepping, and inspecting memory.

    Installation

    Microsoft has changed things slightly in WinDBG's installation from Windows 7 to Windows 8. In this section we'll walk through the install on both.

    Windows 8

     
    For Windows 8, Microsoft includes WinDBG in the Windows Driver Kit (WDK) You can install Visual Studio and the WDK or just install the standalone "Debugging Tools for Windows 8.1" package that includes WinDBG.

    This is basically a thin installer that needs to download WinDBG after you walk through a few screens. The install will ask you if you'd like to install locally or download the development kit for another computer. The later will be the equivalent of an offline installer, which is my preference so that you can install on other systems easily in the future.

     


    From there just Next your way to the features page and deselect everything but "Debugging Tools for Windows" and click "Download".

     


    Once the installer completes you can navigate to your download directory, which is c:UsersUsernameDownloadsWindows Kits8.1StandaloneSDK by default, and then next through that install. Then you're all ready to go!

    Windows 7 and Below

    For Windows 7 and below, Microsoft offers WinDBG as part of the "Debugging Tools for Windows" package that is included within the Windows SDK and .Net Framework. This requires you to download the online/offline installer, then specifically choose the "Debugging Tools for Windows" install option.

    My preference is to check the "Debugging Tools" option under "Redistributable Packages" and create a standalone installer which makes future debugging efforts a heck of lot easier. That's what I'll do here.

     


    Once the installation completes, you'll should have the redistributable for various platforms (x86/x64) in the c:Program FilesMicrosoft SDKsWindowsv7.1RedistDebugging Tools for Windows directory.

     


    From there the installation is pretty simple, just copy the appropriate redistributable to the system you're debugging and then click through the installation.

    Interface

     
    When you run WinDBG for the first time, you'll realize that its intimidatingly simple. Most of WinDBG's interface is experienced while you're actually debugging a process. So you're not going to do to much with WinDBG until you attach it to a process. Rather then having a section dedicated to the interface (too late!) we'll point out the important parts in the upcoming sections.

    The most basic thing about the interface you should know is the Command window. It's the default window opened once you're attached to a process. The Command window is mostly an output only window, with a small input field on the bottom which you'll enter commands into to control WinDBG.

     


    Symbols

    WinDBG doesn't really need much of a configuration, most things work right out of the box. The one important thing to do is set up Symbols. Symbols are basically special files that are generated with the program binary at compile time that provide debugging information such as function and variable names. This can really help demystify a lot of the functionality of an application when debugging or disassembling. Many Microsoft components are compiled with Symbols which are distributed via the Microsoft Symbol Server. For non-Microsoft binaries, you're usually out of luck - sometimes you'll find them laying around somewhere but mostly all companies keep that stuff protected.

    To configure WinDBG to use the Microsoft Symbol server go to File:Symbol File Path and set the path appropriately to the one below. The syntax is a little weird, asterisks are the delimiter, so in the value below, we'll download symbols to the C:Symbols directory.

    SRV*C:Symbols*http://msdl.microsoft.com/download/symbols
    


     


    WinDBG will automatically load Symbols for binaries that it has them for when needed. To add a file containing symbols you can just append it to the path:

    SRV*C:Symbols*http://msdl.microsoft.com/download/symbols;c:SomeOtherSymbolFolder
    


    Adding Symbols during Debugging

    If you do run into a situation where you have Symbols and would like to import them while the debugging, you can do so via the .sympath command option within the command window (this requires you to be attached to a process). For instance to append c:SomeOtherSymbolFolder you can:

    0:025> .sympath+ c:SomeOtherSymbolFolder
    Symbol search path is: SRV*C:Symbols*http://msdl.microsoft.com/download/symbols;c:SomeOtherSymbolFolder
    Expanded Symbol search path is: srv*c:symbols*http://msdl.microsoft.com/download/symbols;c:someothersymbolfolder
    


    It's always good to reload the symbols after you make changes to the path:

    0:025> .reload
    Reloading current modules
    ................................................................
    ...............................................
    


    Checking Symbols

    To view what modules have symbols loaded, you can use the x*! command. However, WinDBG doesn't load Symbols until it needs them so x*! will show most of the module symbols are deferred. We can force WinDBG to load symbols, with the ld * command (which may take a little time, you can stop it by going to Debug:Break):

     


    Now we can view the symbols for each for the modules:

     


    Debugging a Local Process

    You have a couple options when debugging a local process. You can start the process then attach to it, or have WinDBG launch the process for you. I'm really sure of all the advantages/disadvantages of each - I know that when you launch a program with WinDBG, it enables some special debugging options (e.g. debug heap) that the program may not like, and it will crash. Additionally, those debugging options alters the code paths in which the programs takes which modifies how things are arranges in memory. That being said, there are also programs that will crash when you attach the debugger, so ymmv. Some applications (malware in particular) will look for the presence of the debugger at launch and may not later on, which would be a reason why you'd attach. And sometimes you're debugging a service that is controlled by Windows which sets up a variety of things during its launch, so to simplify things, you'd attach rather then launch via the debugger. Some people say there is a significant performance impact when launching a process via the debugger. Test it out yourself, and see what works best for you. If you have any particular reasons why you'd do one over the other, please let me know the comments!

    Starting a Process

    If you're debugging a self contained application that just runs locally and doesn't communicate via the network, you may want to have WinDBG start the application. However, that's not to say you can't attach to these programs after they've been launched.

    Starting a process is pretty straight forward, go to "File:Open Executable". From there, select the executable you'd like to debug. You can also provide command line arguments and define the start directory:

     


    Attaching to a Process

    Attaching to an already running process is just as simple. Note, that in some cases, you'll need to need to spend a little time identifying the true process you're looking to target. For instance, some web browsers will create one parent process, then an additional process for each tab. So depending on the crash you're debugging, you might want to attach to the tab process rather than the parent.

    To attach to an already existing process, go to "File:Attach to a Process" then select the PID or process name to attach to. Keep in mind you'll also need the appropriate rights to attach to your target process.

     


    If the program has stopped responding, you can noninvasively by using the "Noninvaise" checkbox.

    Debugging a Remote Process

    Now there may be times where you have to debug a process on a remote system. For instance, it may just be more convenient to use a local debugger rather than one within a VM or via RDP. Or perhaps you are debugging LoginUI.exe - which is only available while the system is locked. In these situations you can have a local WinDBG instance running then remotely connect to it. There are a couple ways to do this as well - we'll cover two of the most common ways.

    Existing Debugging Sessions

    If you've already started to debug the program locally (via attaching or launching mentioned above) you can use the command input field to have WinDBG launch a listener that a remote debugger can connect to. This is done with the .server command:

    .server tcp:port=5005
    


    You'll likely get a security alert that you should allow:

     


    Then a positive message within WinDBG telling you the server has started:

    0:005> .server tcp:port=5005
    Server started.  Client can connect with any of these command lines
    0: <debugger> -remote tcp:Port=5005,Server=USER-PC
    


    Then from the remote host, you can connect to the existing debugging session via "File:Connect to a Remote Session":

     


    tcp:Port=5005,Server=192.168.127.138
    


    Once connected you'll get a confirmation on remote client:

    Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    Server started.  Client can connect with any of these command lines
    0: <debugger> -remote tcp:Port=5005,Server=USER-PC
    MACHINENAMEUser (tcp 192.168.127.138:13334) connected at Mon Dec 16 09:03:03 2013
    
    


    and the locally debugging instance:

    MACHINENAMEUser (tcp 192.168.127.138:13334) connected at Mon Dec 16 09:03:03 2013
    
    


    Remote Server

    You can also have a standalone WinDBG server running on a system, remotely connect to it, then have the ability to select what process to attach to. This can be done using the dbgsrv.exe

    executable on the system where the process is (or will be) running:

     dbgsrv.exe -t tcp:port=5005
    
    


     


    And you'll likely get a Windows Firewall notice, which you should allow:

     


    From the remote system, you can connect by going to "File: Connect to Remote Stub" and defining the server:

     
    tcp:Port=5005,Server=192.168.127.138
    


    You won't get any obvious indicator that you're connected, but when you go to "File:Attach to a Process", you'll see the process list of the system you're running dbgsrv.exe on. Now you can attach to a process as you normally would as if the process was local.

    Help

    WinDBG's help system is awesome. As with all new things, you should become familiar with how to get help on a specific command or concept. From the command input you can use the .hh command to access WinDBG's help:

    windbg> .hh 
    


    You can also use .hh on a specific command. For instance, to get more information on the .reload command, you can use:

    windbg> .hh .reload
    


    Or just go to "Help:Contents".

    Modules

    As program runs it pulls in a number of modules that provide functionality - thus if you're able to gain insight into what modules are imported by the application, it can help identify what the application does and how it may work. In many scenarios, you'll be debugging a particular module loaded by a program, rather than the program executable itself.

    When you attach to process, WinDBG will automatically list the loaded modules, for instance, here's what WinDBG's output when I attached to calc.exe:

    Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    *** wait with pending attach
    Symbol search path is: SRV*C:Symbols*http://msdl.microsoft.com/download/symbols
    Executable search path is: 
    ModLoad: 00a70000 00b30000   C:Windowssystem32calc.exe
    ModLoad: 77630000 7776c000   C:WindowsSYSTEM32
    tdll.dll
    ModLoad: 77550000 77624000   C:Windowssystem32kernel32.dll
    ModLoad: 75920000 7596a000   C:Windowssystem32KERNELBASE.dll
    ModLoad: 76410000 77059000   C:Windowssystem32SHELL32.dll
    ModLoad: 77240000 772ec000   C:Windowssystem32msvcrt.dll
    ModLoad: 76300000 76357000   C:Windowssystem32SHLWAPI.dll
    ModLoad: 75cd0000 75d1e000   C:Windowssystem32GDI32.dll
    ModLoad: 75fa0000 76069000   C:Windowssystem32USER32.dll
    ModLoad: 777b0000 777ba000   C:Windowssystem32LPK.dll
    ModLoad: 774b0000 7754d000   C:Windowssystem32USP10.dll
    ModLoad: 73110000 732a0000   C:WindowsWinSxSx86_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.7600.16385_none_72fc7cbf861225cagdiplus.dll
    ModLoad: 75a80000 75bdc000   C:Windowssystem32ole32.dll
    ModLoad: 76360000 76401000   C:Windowssystem32RPCRT4.dll
    ModLoad: 777c0000 77860000   C:Windowssystem32ADVAPI32.dll
    ModLoad: 75be0000 75bf9000   C:WindowsSYSTEM32sechost.dll
    ModLoad: 76270000 762ff000   C:Windowssystem32OLEAUT32.dll
    ModLoad: 74590000 745d0000   C:Windowssystem32UxTheme.dll
    ModLoad: 74710000 748ae000   C:WindowsWinSxSx86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfcCOMCTL32.dll
    ModLoad: 703d0000 70402000   C:Windowssystem32WINMM.dll
    ModLoad: 74c80000 74c89000   C:Windowssystem32VERSION.dll
    ModLoad: 77770000 7778f000   C:Windowssystem32IMM32.DLL
    ModLoad: 75c00000 75ccc000   C:Windowssystem32MSCTF.dll
    ModLoad: 74130000 7422b000   C:Windowssystem32WindowsCodecs.dll
    ModLoad: 74260000 74273000   C:Windowssystem32dwmapi.dll
    ModLoad: 756d0000 756dc000   C:Windowssystem32CRYPTBASE.dll
    ModLoad: 75e60000 75ee3000   C:Windowssystem32CLBCatQ.DLL
    ModLoad: 6ef10000 6ef4c000   C:Windowssystem32oleacc.dll
    
    


    Later on in a debugging session you can reproduce these results with the lmf command:

    0:005> lmf
    start    end        module name
    00a70000 00b30000   calc     C:Windowssystem32calc.exe
    6ef10000 6ef4c000   oleacc   C:Windowssystem32oleacc.dll
    703d0000 70402000   WINMM    C:Windowssystem32WINMM.dll
    73110000 732a0000   gdiplus  C:WindowsWinSxSx86_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.7600.16385_none_72fc7cbf861225cagdiplus.dll
    74130000 7422b000   WindowsCodecs C:Windowssystem32WindowsCodecs.dll
    74260000 74273000   dwmapi   C:Windowssystem32dwmapi.dll
    74590000 745d0000   UxTheme  C:Windowssystem32UxTheme.dll
    74710000 748ae000   COMCTL32 C:WindowsWinSxSx86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfcCOMCTL32.dll
    74c80000 74c89000   VERSION  C:Windowssystem32VERSION.dll
    756d0000 756dc000   CRYPTBASE C:Windowssystem32CRYPTBASE.dll
    75920000 7596a000   KERNELBASE C:Windowssystem32KERNELBASE.dll
    75a80000 75bdc000   ole32    C:Windowssystem32ole32.dll
    75be0000 75bf9000   sechost  C:WindowsSYSTEM32sechost.dll
    75c00000 75ccc000   MSCTF    C:Windowssystem32MSCTF.dll
    75cd0000 75d1e000   GDI32    C:Windowssystem32GDI32.dll
    75e60000 75ee3000   CLBCatQ  C:Windowssystem32CLBCatQ.DLL
    75fa0000 76069000   USER32   C:Windowssystem32USER32.dll
    76270000 762ff000   OLEAUT32 C:Windowssystem32OLEAUT32.dll
    76300000 76357000   SHLWAPI  C:Windowssystem32SHLWAPI.dll
    76360000 76401000   RPCRT4   C:Windowssystem32RPCRT4.dll
    76410000 77059000   SHELL32  C:Windowssystem32SHELL32.dll
    77240000 772ec000   msvcrt   C:Windowssystem32msvcrt.dll
    774b0000 7754d000   USP10    C:Windowssystem32USP10.dll
    77550000 77624000   kernel32 C:Windowssystem32kernel32.dll
    77630000 7776c000   ntdll    C:WindowsSYSTEM32
    tdll.dll
    77770000 7778f000   IMM32    C:Windowssystem32IMM32.DLL
    777b0000 777ba000   LPK      C:Windowssystem32LPK.dll
    777c0000 77860000   ADVAPI32 C:Windowssystem32ADVAPI32.dll
    
    


    And you can get the load address for a specific module using the "lmf m" command:

    0:005> lmf m kernel32
    start    end        module name
    77550000 77624000   kernel32 C:Windowssystem32kernel32.dll
    


    To get the image header information you can use the !dh extension (the exclamation mark denotes an extension) on a particular module.

    0:005> !dh kernel32
    
    File Type: DLL
    FILE HEADER VALUES
         14C machine (i386)
           4 number of sections
    4A5BDAAD time date stamp Mon Jul 13 21:09:01 2009
    
           0 file pointer to symbol table
           0 number of symbols
          E0 size of optional header
        2102 characteristics
                Executable
                32 bit word machine
                DLL
    
    OPTIONAL HEADER VALUES
         10B magic #
        9.00 linker version
       C4600 size of code
        C800 size of initialized data
           0 size of uninitialized data
       510C5 address of entry point
        1000 base of code
             ----- new -----
    77550000 image base
        1000 section alignment
         200 file alignment
           3 subsystem (Windows CUI)
        6.01 operating system version
        6.01 image version
        6.01 subsystem version
       D4000 size of image
         800 size of headers
       D5597 checksum
    00040000 size of stack reserve
    00001000 size of stack commit
    00100000 size of heap reserve
    00001000 size of heap commit
         140  DLL characteristics
                Dynamic base
                NX compatible
       B4DA8 [    A915] address [size] of Export Directory
       BF6C0 [     1F4] address [size] of Import Directory
       C7000 [     520] address [size] of Resource Directory
           0 [       0] address [size] of Exception Directory
           0 [       0] address [size] of Security Directory
       C8000 [    B098] address [size] of Base Relocation Directory
       C5460 [      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
       816B8 [      40] address [size] of Load Configuration Directory
         278 [     408] address [size] of Bound Import Directory
        1000 [     DE8] 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
       C44C1 virtual size
        1000 virtual address
       C4600 size of raw data
         800 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           25       c549c    c4c9c Format: RSDS, guid, 2, kernel32.pdb
     (    10)       4       c5498    c4c98
    
    SECTION HEADER #2
       .data name
         FEC virtual size
       C6000 virtual address
         E00 size of raw data
       C4E00 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
         520 virtual size
       C7000 virtual address
         600 size of raw data
       C5C00 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
        B098 virtual size
       C8000 virtual address
        B200 size of raw data
       C6200 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
    
    
    


    Messages/Exceptions

    When you attach to a process, the modules are displayed first, then WinDBG displays any applicable messages. When we attached to calc.exe, WinDBG automatically sets a breakpoint (which is just a marker that tells the debugger uses to pause the execution of a program). So our message is:

    (da8.b44): Break instruction exception - code 80000003 (first chance)
    


    This particular message is an exception, specifically a first chance exception. An exception is basically some special condition that occurred during the program's operation. The first chance means that the progam was paused right after the exception occurred. A second chance exception is when an exception has occurred, some programming logic to handle exception was executed, and the program has paused.

    Registers

    After the messages/exceptions, the debugger will output the state of the CPU's registers. Registers are basically special variables within the CPU that store a small amount of data or keep track of where something is in memory. The CPU can process the data in these registers very quickly, so its faster for the CPU to perform operations on the values in its registers rather then pulling information all the way down the bus from RAM.

    WinDBG automatically outputted the following registers after we attached to calc.exe:

    eax=7ffd9000 ebx=00000000 ecx=00000000 edx=776cd23d esi=00000000 edi=00000000
    eip=77663540 esp=02affd9c ebp=02affdc8 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    


    Later on down the line, we can reproduce this with the r command:

    0:005> r
    eax=7ffd9000 ebx=00000000 ecx=00000000 edx=776cd23d esi=00000000 edi=00000000
    eip=77663540 esp=02affd9c ebp=02affdc8 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    ntdll!DbgBreakPoint:
    77663540 cc              int     3
    
    


    And if we wanted to just retrieve a value of a specific register, we could by appending the register name:

    0:005> r eax
    eax=7ffd9000
    
    


    and multiple registers:

    0:005> r eax,ebp
    eax=7ffd9000 ebp=02affdc8
    
    


    Instruction Pointer

    The final line is instruction to be executed. This is outputted as part of the r command and is what the EIP register contains. EIP is the instruction pointer, which is the register that contains the location of the next instruction for the CPU to execute. WinDBG's output is equivalent of the u eip L1 command that basically tells WinDBG to go to the memory location pointed to by EIP, treat that memory as assembly, and print out one line.

    ntdll!DbgBreakPoint:
    77663540 cc              int     3
    


    Stay Tuned

    In the next blog post we'll cover actually using WinDBG :) - breakpoints, stepping, and looking at memory - stay tuned!

    2 comments:

    1.  

      Thanks! Love this kind of educational posts!

      ReplyDelete
    2.  

      as @Anonymous said i agree about his statement
      I hope this tut will guide into a deep research

      ReplyDelete
    Add comment
    Load more...
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    Tuesday, December 24, 2013

    Getting Started with WinDBG - Part 2

     
    By Brad Antoniewicz.

     
    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

    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:

     


    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

    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> lmf m notepad
    start    end        module name
    00e60000 00e90000   notepad  notepad.exe 
    0:000> ba e 1 00e63689
            ^ 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 00e63689'
    
    


    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 00e63689
    ModLoad: 76be0000 76bff000   C:Windowssystem32IMM32.DLL
    ModLoad: 76c00000 76ccc000   C:Windowssystem32MSCTF.dll
    eax=77081162 ebx=7ffd7000 ecx=00000000 edx=00e63689 esi=00000000 edi=00000000
    eip=00e63689 esp=0022fbb4 ebp=0022fbbc 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!WinMainCRTStartup:
    00e63689 e8c5f9ffff      call    notepad!__security_init_cookie (00e63053)
    
    


    Now we can set our hardware breakpoint:

     


    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
    
    


     


    You'll notice something doesn't look right here, all of the registers contain 0! This is because WinDBG hasn't actually set them yet. You can single step (discussed below) with the p command. Once we do, the dr0 register will have our breakpoint defined:

     


    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 00523689 e 1 0001 (0001)  0:**** notepad!WinMainCRTStartup
    


    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.
    • 00523689 - 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!WinMainCRTStartup - 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
    00770000 007a0000   notepad  notepad.exe 
    0:000> bp 00770000 + 3689
    0:000> bl
     0 e 00773689     0001 (0001)  0:**** notepad!WinMainCRTStartup
    
    


    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 00770000 + 3689
    notepad!WinMainCRTStartup:
    00773689 e8c5f9ffff      call    notepad!__security_init_cookie (00773053)
    0077368e 6a58            push    58h
    
    


    Now we know that notepad!WinMainCRTStartup is a friendly name for 00770000 + 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 0077368e 
    notepad!_initterm_e+0x61:
    0077368e 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:
    0077368e 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 0077368e     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:
    "; dd esp;"
    0:000> bl 
     0 e 00ae3689     0001 (0001)  0:**** notepad!WinMainCRTStartup ".echo "Here are the values on the stack:
    "; 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!

    2 comments:

    1.  

      Thanks a lot for this meaningful insight. Its money!

      Crisp and very very clear.

      ReplyDelete
    2.  

      i was really waiting for such tutorialas, i find no execuse to not learn WinDBG by your tutorials, the way you explaining it was amazingly helpful

      ReplyDelete
    Add comment
    Load more...
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    Tuesday, December 31, 2013

    Getting Started with WinDBG - Part 3

     
    By Brad Antoniewicz.

     
    In this series of blog posts we've walked you through getting WinDBG installed, setup, and got you started by attaching to a process and setting breakpoints. Our next step is the actual debugging part where we're stepping through a program and looking at memory.

    • 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

    Stepping

    Really the whole reason you're using a debugger is to inspect the state of a process during a specific operation or function. Just about every instruction that gets executed alters the program state in some way which means having the ability to execute an instruction then inspect the state is extremely important. The first part of this is "stepping" - executing instructions then pausing. WinDBG offers a number of different stepping commands depending on where you are in the program and where you want to go.

     
    Most debuggers use the following terms that describe how you can navigate through a program and its functions:

    • Step-Into - When the instruction is a call, follow the call and pause at the first instruction in the called function.
    • Step-Over - When the instruction is a call, execute the function and all subfunctions, pausing at the instruction in the current function after the call.
    • Step-Out - Execute all instructions and pause after the current function is complete (ret at the end of the current function)

    A note to make here is that both Step-Into and Step-Over will execute a single instruction and pause - behavior only changes when a call instruction is reached.

    Go

    The g (Go) command is more of a breakpoint command but its functionality blurs the lines between breakpoints and stepping commands. It's used to resume execution of program but unlike most of the stepping commands, it not really meant to be used on an instruction by instruction basis. g will resume the program until a breakpoint or exception occurs. Really, you would use g to execute all of the instructions up to a breakpoint, whereas with stepping commands you're executing instructions without setting a breakpoint. However, to clarify, debuggers will pause when hitting a breakpoint regardless if you use a stepping command or something like g.

    g is straightforward to use:

    0:001> g
    


    While the program is running, WinDBG will give you a message in the command input box:

     


    If you know the address you'd like to execute until then just provide it as an argument to g:

    0:001> g notepad!WinMainCRTStartup
    


    Single Stepping

    Executing a single instruction, then pausing is called Single Stepping. This can be achieved by either using the "Step-Into" or "Step-Over" commands since both behave the same on non-call instructions. Rather then show them both here, let's look at these commands individually.

    Step-Into

    0:001> t 
    


    To Step-Into with WinDBG, use the t (Trace) command. Each step will show you the state of the registers and the instructions that will be executed. In this example we'll pause at the program's entry point (notepad!WinMainCRTStartup) and look at the first few instructions to be executed (u eip). The first instruction is a call to the notepad!__security_init_cookie function. Let's see how debugger behaves when Stepping-Into with t:

     


    Here we can see that we were running within notepad!WinMainCRTStartup, then on the call we used t to follow the call into the notepad!__security_init_cookie function, where we paused on the first instruction.

    Step-Over

    0:001> p 
    


    WinDBG uses the p command to step over a function call. This means that the call and all subinstructions within the called function will be executed and the program will pause on the next instruction within the current function (e.g. notepad!WinMainCRTStartup). Let's look at the same scenario, but this time we'll use p:

     


    Here we can see that the instruction after the call to notepad!__security_init_cookie is push 58h. When we Step-Over with p we automatically execute everything within the notepad!__security_init_cookie function, then pause at the push after it.

    Step-Out

    0:001> gu 
    


    Stepping-Out with WinDBG can be achieved with the gu (Go Up) command. This command scans the current function for a ret then pauses after it gets executed. This an important behavior, because if, for whatever reason, the function doesn't end in a ret or a code path doesn't lead to one, you could experience unexpected results with gu. Let's see what it looks like:

     


    Here we've paused on notepad!WinMainCRTStartup+0x1d which is a call to notepad!_imp__GetStartupInfoA. We can see (u eip L2) that the instruction after the call is mov dword ptr [ebp-4],0FFFFFFFEh. So we'll single step (t) into the function and pause at the first instruction. Now we use gu to execute all instructions and function calls in our child function then pause on the next instruction in the parent function, which is our mov dword ptr [ebp-4],0FFFFFFFEh

    Executing until Return

    gu is good and all, but sometimes you want to look at the stack right before the function returns, in this scenario, you'll need to use either tt (Trace to Next Return) or pt. Both are easy to call:

    0:001> tt 
    


    0:001> pt 
    


    The important thing here to remember is that tt will stop at the next return, even if its not in the current function. For instance, consider the following pseudocode, our goal is to pause on the ret in func:

    func: 
    call somefunc
    ret
    
    somefunc:
    call someotherfunc
    ret
    
    someotherfunc:
    ret 
    


    In this example, if pause at call somefunc, then use tt, we'll end up pausing at the ret in someotherfunc.

    A better approach for this scenario might be to use pt: Using the same pseudocode, if we pause at call somefunc, then use pt, we'd execute all the code in somefunc (and subsequently someotherfunc), then pause at the ret in func. In all reality for this example we could just use p, but that doesn't illustrate the point :)

    Ultimately it depends on what you, as the person using the debugger, want to do.

    Inspecting Memory

    Now we can finally get into the most important part of debugging: Inspecting Memory. WinDBG provides the d (Display Memory) command for this purpose. In its most simple form you can run it like this:

    0:001> d
    


    But this is more or less useless. Running d by itself for the first time will output the memory where eip points to. This is useless because eip should be pointing to a code segment and, to make sense of that, you'd really need to use the u (Unassemble) command. So a much better starting out command would be:

    0:001> d esp
    


    This will show us the values on the stack. With d, WinDBG will display data using the format specified by the last d command executed. If this is you're first time running d, it doesn't have previous command stored, so WinDBG will give you the output of the db (Display Byte) command.

    Display Bytes

    db will output the data in bytes and provide the corresponding ASCII values:

     


    Display Words

    Words, or 2 byte values, can be shown with dw (Display Word). Alternatively, you can use dW to show Words and ASCII values:

     


    Display DWORDs

    My favorite memory viewing command is dd (Display DWORDs). A DWORD is a double word, so 4 bytes. dd will just show you the DWORDS while dc will show DWORDs and ASCII values:

     


    Display Quadwords

    To display quadwords (4 words/8 bytes) within WinDBG, use dq:

     


    Display Bits

    You can even show binary with dyb:

     


    Displaying Strings

    Strings are displayed with da, essentially WinDBG will print everything as ASCII until it reaches a null byte. So here, even though esp isn't a string, it'll treat everything as a string until it reaches a null. Just to further illustrate this, I've printed out the 5 bytes at esp with db esp L5:

     


    Addressing

    So far we've just been looking at the memory that esp is pointing to by using esp as the parameter to our memory inspection comman however there are a number of different ways to reference memory that can be useful when starting out.

    Registers - As we've seen, you can use any register and WinDBG will use the address in that register as the memory address:

    0:001> dd eax
    


    Memory Address - You can also just use the memory address itself by providing it:

    0:001> db 0020faa0  
    


    Offsets - You can also use offsets with registers or memory addresses by using mathematical expressions:

    0:001> db 0020faa0 + 18
    


    0:001> dd ebp - 18
    


    0:001> dq ebp*eax
    


    These expressions can be used wherever an address can be used. Here's what it looks like in WinDBG:

     


    WinDBG will output question marks (?) for invalid/free memory.

    Pointers

    There are often times where a value on the stack is just a pointer to another location. If you'd like to look at that value you'd need to do two look ups. For instance, say we know that value ebp+4 is a pointer to some assembly code that we want to read. To view that assembly, we'd need two commands. The first command shows us the memory address at ebp+4:



    0:001> dd ebp+4
    


    Then the second requires us to manually copy the value at that address then paste it in as an argument to the u command so we can view that assembly:

    0:001> u 777be2d1
    


     


    This is all fine, but there's an easier way with the poi() function. Using poi() we just provide ebp+4 as its parameter and it will automatically take the value at that address and use it, rather then just using the value of ebp+4:

    0:001> u poi(ebp+4)
    


     


    Limiting output

    By default WinDBG will output a set amount of data, however we can limit how of that data is outputted with the L (Size Range Specifier) attribute. L will work with most commands and just needs to be appended to the end with a value:

    0:001> dd esp L1
    


    The number specified with L is the size, which is related to the command executed. For instance, with db, L will mean the number of bytes to print, while with dd, L will mean the number of DWORDS.

     


    That's really it to get you off the ground inspecting memory - I know, three blog posts building up to this functionality, and its just this tiny little section? Yup - there are some more memory inspection commands, but to get started, d is the core command. Check out the tips below for more info.

    Tips and Tricks

    Now that you're off the ground, lets look at some handy tricks and tips that can make your debugging experience much better.

    Keyboard Shortcuts

    Chances are you'll be starting and stopping an application hundreds of times while you're debugging so any little shortcut can solve you tons of time in the long run. Keyboard shortcuts are huge, here are the four I use the most:

    • F6 - Attach to a process. With the Attach Window open, use the "End" key to drop down to the bottom (where newly launched applications are).
    • CTRL + E - Open Executable
    • CTRL + Break - "Break" into a running application - used to pause a running program
    • F5 - Shortcut for g

    Converting Formats

    If you haven't figured this out already, WinDBG prints numbers in hex by default. That means 12 isn't the same as decimal 12. One quick tip is the .formats command. To use it is straightforward:

    0:001> .formats value
    


    Where value is something you want to convert. So .formats will take whatever you provide and output it in a variety of formats:

     
    Now we know 12 is actually 18 :). However, you can also provide decimal values using the 0n specifier:

     


    Math

    There may be times where you need to calculate an offset or just do some basic math. WinDBG will evaluate expressions with the ? command:

    0:001> ?1+1
    


    These expressions can be as simple or as complicated as you'd like can contain all of the standard addressing that WinDBG uses:

     


    ...who needs calc.exe when you have WinDBG!

    Extensions

    To make life easier, there are a number of extensions that people have created for WinDBG. These are great little tools that can be used within the debugger to provide functionality. Some are even built by Microsoft. The useful ones are !heap, !address, !dh, and !peb. I'll cover these and more in another blog post - So stay tuned!

    Cheat Sheets

    There are a couple of really nice WinDBG command references, cheat sheets, and tutorials out there if you don't like .hh, here are a couple good ones:

    Got any more tips or tricks? Share them in the comments below!

    4 comments:

    1.  

      Typo: you posted the wrong pic for the "u poi(ebp+4)"-example

      ReplyDelete
      Replies
       
       
    2.  
    3.  
      Reply
    4.  

      great series!

      ReplyDelete
    5.  

      Awesome tutorials :)

      ReplyDelete
    Add comment
    Load more...
     
     
     
     
     
     
     
  • 相关阅读:
    我记录网站综合系统 技术原理解析[4:我记录框架 路由系统]
    我记录网站综合系统 技术原理解析[番外篇:自己做Stopwatch]
    我记录网站综合系统 技术原理解析[0:简介(代序) 1.7Beta源代码下载开始]
    我记录网站综合系统 技术原理解析[5:JSON 序列化 反序列化]
    一个网站的诞生 MagicDict未来予想図3 [表格的动态增加行和删除行,高手绕路]
    一个网站的诞生 MagicDict未来予想図2 [单页面多个submit的实现,高手绕路]
    我记录网站综合系统 技术原理解析[2:C# 水印和验证码的制作]
    我记录网站综合系统 技术原理解析[3:我记录框架处理流程]
    一个网站的诞生 MagicDict未来予想図4 [表格的动态增加行和删除行,完整版]
    我记录网站综合系统 技术原理解析[1:我记录的整体框架的简介]
  • 原文地址:https://www.cnblogs.com/jvava/p/4180045.html
Copyright © 2011-2022 走看看