Use cases

esReven integrates WinDbg at multiple stages of the workflow. See the parent page for more information.

This page describes common operations you might need, especially when using WinDbg in the context of a debugger-assisted recording.

Notably, breaking the VM with breakpoints is supported even while recording. Moreover, starting or stopping a recording is possible while the VM is paused.

I want to start and stop recording a scenario at a precise function

  1. Create a new scenario, start your VM & your program.
  2. Connect WinDbg to that VM
  3. The VM pauses in a random process and hands over control to WinDbg. Find the target program as described below.
  4. You can now set breakpoints (using symbols, RVA or absolute addresses) so as to pause the VM right before what you need to record.
  5. Resume the VM.
  6. At some point, a breakpoint hits and control is handed over to WinDbg.
  7. Head over to the Project Manager's record page and click on "Record".
  8. Set new breakpoints to catch the end of what you want to record.
  9. Resume the VM: the recording effectively starts.
  10. Another breakpoint hits: click on "Stop record" in the project manager.

NOTE: you can pause the VM at anytime during the recording, either manually with "Break" or via a breakpoint, and check the state of your VM, without affecting the on-going recording. This also allows for automatic conditional breakpoints, or further scripting.

NOTE: while breaking the VM does not affect the recording, it might still have consequences on the on-going program. Network timeouts are an example of such side effects.

I want to place breakpoints into an on-going program in a VM, or inspect its state

In this basic use case, you have a program currently running on your VM, and you want to inspect its state.

Assuming you connected WinDbg to that VM, first you need to locate your program:

  1. Enter .reload so WinDbg has the information it needs.

  2. Find your target process:

    * 1. Look for the target process. You can find it with for example:
    dx @$targetP = @$cursession.Processes.Where(p => p.Name.ToLower().Contains("<mybinary>")).First()
    *    If not, open the Debugger Data Model, look for its handle, then enter:
    dx @$targetP = @$cursession.Processes[<0xHandle>]
    
  3. Set this as the implicit process & reload user space:

    dx @$targetP.SwitchTo()
    .reload /user
    
  4. From there, you can inspect the process's memory, place breakpoints, etc.

At this point the target program is not effectively running or scheduled, so this might not be enough:

  • Stepping will not get you inside your target process
  • You don't know which thread is currently running The next section will show you how to do this.

I want to break into an on-going program so I can step into it

Assuming you followed the previous section, you may still want to schedule the process. The command .process /i does not work in this context (see why), so you must do it manually. There are two options:

  • You can either explore the stack of your process' threads, and go to the relevant function:

    * 1. Explore the list of threads:
    dx -r2 @$targetP.Threads.Select(t => t.Stack.Frames)
    
    * 2. - Locate the call you want to get back into
         - Navigate to find its Attributes.InstructionOffset, and get there.
    g 0xValue
    
    * When the VM breaks again, reload user land.
    .reload /user
    
  • Alternatively, if you don't know which function or thread to get back to, you can simply wait for the process to be scheduled:

    * Create a one-shot breakpoint into the process
      Note we are assuming all unscheduled threads have the same instruction offset.
    dx Debugger.Utility.Control.ExecuteCommand("bp /1 /p " + ((__int64)&@$targetP.KernelObject).ToDisplayString("x") + " " + @$targetP.Threads.First().Stack.Frames.First().Attributes.InstructionOffset.ToDisplayString())
    
    * Resume the VM
    g
    
    * When the VM breaks again, reload user land
    .reload /user
    
    * At this point, you are in kernel land.
      Use the stack to find a meaningful location to go to.
    

    NOTE: the VM will be quite slow until this breakpoint is reached. This is due to having a conditional breakpoint on a very frequently-hit address.

I want to break at the start of a program

The currently supported perimeter does not include interrupting the VM on events using the sx* commands. However, we can manually break at the start of a program by placing a breakpoint on nt!NtCreateUserProcess.

  1. Place the breakpoint so that it catches the process creation function:
    bp nt!NtCreateUserProcess
    g
    
  2. Head over to the VM & launch your executable.
  3. The VM breaks. Execute the following commands:
    * 1. First, ensure the process is effectively created
    pt
    
    * 2. We must find the newly created process. You can find it with for example:
    dx @$targetP = @$cursession.Processes.Where(p => p.Name.ToLower().Contains("<mybinary>")).First()
    *    If not, open the Debugger Data Model, look for its handle, then enter:
    dx @$targetP = @$cursession.Processes[<0xHandle>]
    
    * 3. Set the new process as the implict process & reload user space
    dx @$targetP.SwitchTo()
    .reload /user
    
    * 4. Find the proper module to break into, usually the first one.
    dx @$targetM = @$curprocess.Modules.First();
    *    If not, explore the modules manually.
    
    * 5. Wait for that entry point to hit
    dx Debugger.Utility.Control.ExecuteCommand("g " + (@$targetM.Contents.Headers.OptionalHeader.AddressOfEntryPoint + @$targetM.BaseAddress).ToDisplayString())
    

You are now at the actual entry point of the program.