Log in

View Full Version : dr7.gd - dr6 saving


deroko
January 3rd, 2008, 11:41
Recenly I wrote a little driver to test this feature with my softice, I didn't need it realy, but who know when it will become usefull.

Whole concept consist of setting dr7.GD to 1, and waiting for mov drX/reg or mov reg/drX to occur. Here is a little quote from IA32 manual:

Quote:
GD (general detect enable) flag (bit 13)
Enables (when set) debug-register protection, which causes a debug exception
to be generated prior to any MOV instruction that accesses a debug register.
When such a condition is detected, the BD flag in debug status register DR6 is
set prior to generating the exception. This condition is provided to support incircuit
emulators. (When the emulator needs to access the debug registers,
emulator software can set the GD flag to prevent interference from the program
currently executing on the processor.) The processor clears the GD flag upon
entering to the debug exception handler, to allow the handler access to the
debug registers.

Basically what this means is that GD is cleared each time int 1 exception occurs. This is done in the way so handler can examine dr6 for BS (indicates single steping) or other flags like BT, BD and B0-B1. If GD wouldn't be cleared in dr7 on int1 exception then handler would fall into infinite loop trying to access dr6 no matter what caused int1 as GD would constantly cause int1 to be called. This simply tells us that we have to set dr7.GD everytime when int 1 is generated no matter if we handle exception or pass it lower in the chain. Neet

Now we come to dr6 issue when softice is active(well when mov reg,dr6 occurs, but without sice I have no idea why this would be usefull - syser maybe???), if we watch and log activity of softice when stepover occurs we may see this:


Code:
0xB43517FE : mov dr3, esi
dr updated : 0xBA642A93 <------ step over instruction
0xB4351801 : mov dr7, ebx
dr updated : 0x00FF07C0 <------ set dr7 and wait
0xBA328683 : mov eax, dr6
dr value moved : 0xFFFF0FF8 <------ dr6 properly updated
0xB42BE589 : mov eax, dr6
dr value moved : 0xFFFF0FF8
0xB42BE596 : mov dr6, eax
dr updated : 0xFFFF0FF0

Now you may see that dr6.B3 is set, but this is done because I save dr6, and update it properly. Well still it's a walkaround for saving as I don't know any other better way to save it. Now let's descibe this problem, and why and how it happens:

When we enter int 1h (dr3 was hit), dr6 will have value as shown above: 0xFFFF0FF8, as we don't handle this exception, we have to set dr7.GD again, and pass it to the lower handler. Lower handle in the chain will access dr6, and cause dr6 to look like this : 0xFFFF2FF0 (BD set). Now if we emulate mov reg, dr6 with value 0xFFFF0FF0 (we clear BD flag from dr6), handler will not know that actually dr3 was hit which will lead to unhandled exception in kernel mode:

Quote:
BugCheck 1000008E, {80000004, ba5e6c7b, b5cb0194, 0}


Followup: MachineOwner
---------

kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

KERNEL_MODE_EXCEPTION_NOT_HANDLED_M (1000008e)
This is a very common bugcheck. Usually the exception address pinpoints
the driver/function that caused the problem. Always note this address
as well as the link date of the driver/image that contains this address.
Some common problems are exception code 0x80000003. This means a hard
coded breakpoint or assertion was hit, but this system was booted
/NODEBUG. This is not supposed to happen as developers should never have
hardcoded breakpoints in retail code, but ...
If this happens, make sure a debugger gets connected, and the
system is booted /DEBUG. This will let us see why this breakpoint is
happening.
Arguments:
Arg1: 80000004, The exception code that was not handled
Arg2: ba5e6c7b, The address that the exception occurred at
Arg3: b5cb0194, Trap Frame
Arg4: 00000000

Now let's take a little look into IA32 manual to see what really happens (older one):

Quote:
Note that the contents of the DR6 register are never cleared by the processor. To avoid any
confusion in identifying debug exceptions, the debug handler should clear the register before
returning to the interrupted program or task.

but is this really a true? now if we take a look into newer one:

Quote:
Certain debug exceptions may clear bits 0-3. The remaining contents of the DR6
register are never cleared by the processor. To avoid confusion in identifying debug exceptions,
debug handlers should clear the register before returning to the interrupted
task.

We may see that certain debug exception may clear B0-B3 but they don't say which ones. Obviously BD debug exception is the one. So may walkaround was to follow certain logic, of int1 handler:

1. when int1 occurs I save dr6 always
2. when dr6 update occurs I save it (mov dr6, reg)
3. when I detect mov reg, dr6, I update dr6 with proper value which was saved.

Hope this will be usefull to someone Actually our friendly neighbour yates wrote similar code long time ago : http://www.woodmann.com/collaborative/tools/Category:Technical_PoC_Tools but it has this little flaw in it.

ps. I really like how ms describes unhandled_exception :

Quote:
This is not supposed to happen as developers should never have
hardcoded breakpoints in retail code, but ...

but ... shit happens


dELTA
January 3rd, 2008, 14:37
Your high-quality low level excavations are always very welcome, keep 'em coming.

JMI
January 3rd, 2008, 14:42
Or, in more simple language, keep digging up "the good stuff" and sharing it here.

Regards,

deroko
January 3rd, 2008, 17:07
will do so

doug
January 3rd, 2008, 21:13
Always interesting contributions, deroko. Thanks.

For those interested, there are a few more hits on this board about this topic. E.g.: if you search for 'gd bit', or dr7.

http://www.woodmann.com/forum/showthread.php?t=6286
http://www.woodmann.com/forum/showthread.php?t=5595
http://www.woodmann.com/forum/showthread.php?t=5022