Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WinDbg not showing register values

Basically, this is the same question that was asked here.

When performing kernel debugging of a machine running Windows 7 or older, with WinDbg version 6.2 and up, the debugger doesn't show anything in the registers window. Pressing the Customize... button results in a message box that reads Registers are not yet known.

At the same time, issuing the r command results in perfectly valid register values being printed out.

What is the reason for this behaviour, and can it be fixed?

like image 246
Michael Bikovitsky Avatar asked Mar 12 '16 17:03

Michael Bikovitsky


People also ask

How to view Registers in WinDbg?

You can view and edit registers by entering the r (Registers) command in the Debugger Command window. You can customize the display by using several options or by using the rm (Register Mask) command.

How to see Registers in visual studio?

To open the Registers window Enable address-level debugging, by selecting Enable address-level debugging in Tools (or Debug) > Options > Debugging.

How do I get call stack in WinDbg?

As an alternative to the k command, you can view the call stack in the Calls window. To open the Calls window, choose Call Stack from the View menu. The following screen shot shows an example of a Calls window. Buttons in the Calls window enable you to customize the view of the call stack.

How to connect WinDbg?

On the remote computer, open WinDbg, and choose Connect to Remote Session from the File menu. Under Connection String, enter the following string. where YourHostComputer is the name of your host computer, which is running the debugging server. Select OK.


1 Answers

TL;DR: I wrote an extension DLL that fixes the bug. Available here.

The Problem

To understand the problem, we first need to understand that WinDbg is basically just a frontend to Microsoft's Windows Symbolic Debugger Engine, implemented inside dbgeng.dll. Other frontends include the command-line kd.exe (kernel debugger) and cdb.exe (user-mode debugger).

The engine implements everything we expect from a debugger: working with symbol files, read and writing memory and registers, setting breakpoitns, etc. The engine then exposes all of this functionality through COM-like interfaces (they implement IUnknown but are not registered components). This allows us, for instance, to write our own debugger (like this person did).

Armed with this knowledge, we can now make an educated guess as to how WinDbg obtains the values of the registers on the target machine.

The engine exposes the IDebugRegisters interface for manipulating registers. This interface declares the GetValues method for retrieving the values of multiple registers in one go. But how does WinDbg know how many registers are there? That why we have the GetNumberRegisters method.

So, to retrieve the values of all registers on the target, we'll have to do something like this:

  1. Call IDebugRegisters::GetNumberRegisters to get the total number of registers.
  2. Call IDebugRegisters::GetValues with the Count parameter set to the total number of registers, the Indices parameter set to NULL, and the Start parameter set to 0.

One tiny problem, though: the second call fails with E_INVALIDARG.

Ehm, excuse me? How can it fail? Especially puzzling is the documentation for this return value:

The value of the index of one of the registers is greater than the number of registers on the target machine.

But I just asked you how many registers there are, so how can that value be out of range? Okay, let's continue reading the docs anyway, maybe something will become clear:

If the return value is not S_OK, some of the registers still might have been read. If the target was not accessible, the return type is E_UNEXPECTED and Values is unchanged; otherwise, Values will contain partial results and the registers that could not be read will have type DEBUG_VALUE_INVALID.

(Emphasis mine.)

Aha! So maybe the engine just couldn't read one of the registers! But which one? Turns out that the engine chokes on the xcr0 register. From the Intel 64 and IA-32 Architectures Software Developer’s Manual:

Extended control register XCR0 contains a state-component bitmap that specifies the user state components that software has enabled the XSAVE feature set to manage. If the bit corresponding to a state component is clear in XCR0, instructions in the XSAVE feature set will not operate on that state component, regardless of the value of the instruction mask.

Okay, so the register controls the operation of the XSAVE instruction, which saves the state of the CPU's extended features (like XMM and AVX). According to the last comment on this page, this instruction requires some support from the operating system. Although the comment states that Windows 7 (that's what the VM I was testing on was running) does support this instruction, it seems that the issue at hand is related to the OS anyway, as when the target is Windows 8 everything works fine.

Really, it's unclear whether the bug is within the debugger engine, which reports more registers than it can retrieve values for, or within WinDbg, which refuses to show any values at all if the engine fails to produce all of them.

The Solution

We could, of course, bite the bullet and just use an older version of WinDbg for debugging older Windows versions. But where's the challenge in that?

Instead, I present to you a debugger extension that solves this problem. It does so by hooking (with the help of this library) the relevant debugger engine methods and returning S_OK if the only register that failed was xcr0. Otherwise, it propagates the failure. The extension supports runtime unload, so if you experience problems you can always disable the hooks.

That's it, have fun!

like image 156
Michael Bikovitsky Avatar answered Sep 17 '22 17:09

Michael Bikovitsky