Monday, October 29, 2012

Virtual PC vs. DR7

In this post i will show you another weird behavior of Virtual PC 2007. This time the trick is about how Virtual PC handles the debug register DR7 known as Debug Control register.

For those who don't know, DR7 is used to specify the conditions under which the EXCEPTION_SINGLE_STEP exception is triggered for addresses held in DR0-DR3.
If we want to dissect DR7, it would be as follows:
Bit 0     ---> DR0 is locally enabled.
Bit 1     ---> DR0 is globally enabled.
Bit 2     ---> DR1 is locally enabled.
Bit 3     ---> DR1 is globally enabled.
Bit 4     ---> DR2 is locally enabled.
Bit 5     ---> DR2 is globally enabled.
Bit 6     ---> DR3 is locally enabled.
Bit 7     ---> DR3 is globally enabled.

Bit 8     ---> The "Local Enable Bit". Also for "Last Branch" tracing.
Bit 9     ---> The "Global Enable Bit". Also for "Last Branch" tracing.
Bit 10   ---> Reserved.
Bit 11  ----> Reserved.
Bit 12 -----> IR
Bit 13 -----> GD
Bit 14 -----> TB
Bit 15 -----> TT

Bit 16 -----
                  | ----> When DR0 is triggered.
Bit 17 -----
Bit 18 -----
                  | ----> Size of DR0's trigger condition.
Bit 19 -----
Bit 20 -----
                  | ----> When DR1 is triggered.
Bit 21 -----
Bit 22 -----
                  | ----> Size of DR1's trigger condition.
Bit 23 -----

Bit 24 -----
                  | ----> When DR2 is triggered.
Bit 25 -----
Bit 26 -----
                  | ----> Size of DR2's trigger condition.
Bit 27 -----
Bit 28 -----
                  | ----> When DR3 is triggered.
Bit 29 -----
Bit 30 -----
                  | ----> Size of DR3's trigger condition.
Bit 31 -----

For example:
Imagine we want to place a Hardware-Breakpoint-On-Execution for an instruction at 0x401000. See image below.

What the debugger does in this case is:
1) Sets DR0 to 0x401000.
2) Sets bit 0 of DR7 to 1.
3) Sets bit 8 of DR7 to 1 (for backward compatibility).
4) Sets bits 16 and 17 of DR7 to 00 (00 means On-Execution).

And if we then want to place a Hardware-Breakpoint-On-Write-Four for memory at 0x10000. See image below.
What the debugger does in this case is:
1) Sets DR1 to 0x10000.
2) Sets bit 2 of DR7 to 1.
3) Sets bit 8 of DR7 to 1 (for backward compatibility).
4) Sets bits  20 and 21 of DR7 to 01 (01 means On-Write).
5) Sets bits  22 and 23 of DR7 to 11 (11 for the size of trigger condition means to watch four bytes).

Now let's try to get back to the main topic of this post.

Hereafter, i will call the second byte of DR7 (byte 0xBB of 0xDDCCBBAA) the flags byte, just for brevity.

On Windows XP, if we set the flags byte to any value ranging from 0x00 to 0xFF, the breakpoint is always active and the exception is always raised whenever the trigger condition is met e.g. if we set DR7 to 0x0000FF01 (a hardware breakpoint On-Execution with Local enable, global enable, reserved, reserved, IR, GD, TB, and TT bits set), the exception is raised whenever the address in DR0 executes.
The same applies for Windows 7.

What about Virtual PC 2007? 

In Virtual PC 2007 with Windows XP installed inside, with certain flags set in DR7 e.g. 0x00003F01, the breakpoint is sometimes not activated.

So, i created simple executable that brute-forces the DR7's flag byte and based on the number of times the exception is raised it determines whether it is running inside Virtual PC 2007.

You can download the demo from here and its source code from here.
N.B. It has been tested with Windows XP SP2 and SP3.
N.B. VirtualBox is also affected, but i will leave this for a future post.

Any comments or ideas are very welcome. You can also follow me on Twitter @waleedassar

No comments:

Post a Comment