Let's take a look at the Intel manual (volume 3B):
B0 through B3 (breakpoint condition detected) flags (bits 0 through 3) — Indicates (when set) that its associated breakpoint condition was met when a debug exception was generated. These flags are set if the condition described for each breakpoint by the LENn, and R/Wn flags in debug control register DR7 is true. They are set even if the breakpoint is not enabled by the Ln and Gn flags in register DR7.
So, this has to be interpreted the following way:
1. A debugger exception (int 1) is generated
2. For each hardware breakpoint set (0-3) a check is made, and a flag is, or is not set
Now, please note, that nothing here is said about checking if a hardware breakpoint set is enabled (register DR7, bits Ln and Gn) before setting the flags (exception generation is another thing).
OK, so how many of you guys suspect that some vm/emu authors might interpret it another way: "when a hardware breakpoint is hit, set it's flag in the DR6 register, and generate int1"? Let's check it! :)
I'll start with bochs. First of all, as far as I know, bochs default binary distro has the debugging register functionality turned off, so if you would like to test it, a recompilation (detection by DRn not working at all anyone?) will be needed (--enable-x86-debugger option at ./configure).
Let's look at the following code:
#if BX_X86_DEBUGGER
else {
// only bother comparing if any breakpoints enabled and
// debug events are not inhibited on this boundary.
if (! (BX_CPU_THIS_PTR inhibit_mask & BX_INHIBIT_DEBUG_SHADOW) && ! BX_CPU_THIS_PTR in_repeat) {
if (BX_CPU_THIS_PTR dr7 & 0x000000ff) {
bx_address iaddr = get_laddr(BX_SEG_REG_CS, BX_CPU_THIS_PTR prev_rip);
Bit32u dr6_bits = hwdebug_compare(iaddr, 1, BX_HWDebugInstruction, BX_HWDebugInstruction);
if (dr6_bits) {
// Add to the list of debug events thus far.
BX_CPU_THIS_PTR debug_trap |= dr6_bits;
BX_ERROR(("#DB: x86 code breakpoint catched"));
exception(BX_DB_EXCEPTION, 0, 0); // no error, not interrupt
}
}
}
}
#endif
Let's look into the hwdebug_compare funciton:
for (unsigned n=0;n<4;n++) {
bx_address dr_start = BX_CPU_THIS_PTR dr[n] & ~alignment_mask[dr_len[n]];
bx_address dr_end = dr_start + alignment_mask[dr_len[n]];
ibpoint_found_n[n] = 0;
// See if this instruction address matches any breakpoints
if (dr7 & (3 << n*2)) {
if ((dr_op[n]==opa || dr_op[n]==opb) &&
(laddr_0 <= dr_end) &&
(laddr_n >= dr_start)) {
ibpoint_found_n[n] = 1;
ibpoint_found = 1;
}
}
}
// If *any* enabled breakpoints matched, then we need to
// set status bits for *all* breakpoints, even disabled ones,
// as long as they meet the other breakpoint criteria.
// dr6_mask is the return value. These bits represent the bits
// to be OR'd into DR6 as a result of the debug event.
Bit32u dr6_mask = 0;
if (ibpoint_found) {
if (ibpoint_found_n[0]) dr6_mask |= 0x1;
if (ibpoint_found_n[1]) dr6_mask |= 0x2;
if (ibpoint_found_n[2]) dr6_mask |= 0x4;
if (ibpoint_found_n[3]) dr6_mask |= 0x8;
}
As you can see in the comment, the authors were aware of this detail. However, the code above is invalid - they check at first if the exception is enabled, before checking if the condition was met. So, the comment is OK, but the code is not.
Below, you can see a screenshot (click to enlarge) from a proof of concept code:
So, seem that bochs can be detected. Now, for some screenshots from other vm/emu's (ignore the interrupt 8 message):
And now, so we can compare the output, two photos of real hardware (ASUS laptop with Core 2 Duo, and ASUS EeePC netbook with Intel Atom cpu):
Yaay, so we can you this method to detect Bochs and VirtualPC!!!
By the way...
On 22nd Nov'24 we're running a webinar called "CVEs of SSH" – it's free, but requires sign up: https://hexarcana.ch/workshops/cves-of-ssh (Dan from HexArcana is the speaker).
Well... not exactly. The manual I quoted at the beginning of this post is from October 2006. A few days ago a new set of manuals were released (December 2009), and seems something changed:
B0 through B3 (breakpoint condition detected) flags (bits 0 through 3) — Indicates (when set) that its associated breakpoint condition was met when a debug exception was generated. These flags are set if the condition described for each breakpoint by the LENn, and R/Wn flags in debug control register DR7 is true. They may or may not be set if the breakpoint is not enabled by the Ln or the Gn flags in register DR7. Therefore on a #DB, a debug handler should check only those B0-B3 bits which correspond to an enabled breakpoint.
Well, this makes this post useful or not useful. However, as far as I'm concerned, this change is made in the new processors, which are handled by this version of the manual, and are not handled by the previous. These new CPUs are (it's not stated in which of them the change was made):
Intel® Core™2 Quad processor Q6000 series
Intel® Xeon® processor 3000, 3200 series
Intel® Xeon® processor 5000 series
Intel® Xeon® processor 5300 series
Intel® Core™2 Extreme processor X7000 and X6800 series
Intel® Core™2 Extreme QX6000 series
Intel® Xeon® processor 7100 series
Intel® Pentium® Dual-Core processor
Intel® Xeon® processor 7200, 7300 series
Intel® Core™2 Extreme QX9000 series
Intel® Xeon® processor 5200, 5400, 7400 series
Intel® CoreTM2 Extreme processor QX9000 and X9000 series
Intel® CoreTM2 Quad processor Q9000 series
Intel® CoreTM2 Duo processor E8000, T9000 series
Intel® AtomTM processor family
Intel® CoreTM i7 processor
Intel® CoreTM i5 processor
So I guess, the detection needs additional check, to know which CPU it's dealing with.
All in all, it was quite fun to play with it ;>
Comments:
A komentarz...:D
Ahahaha ;>>> Thanks for your comment, it's very insightful ;>>>
@wheelq
Glad to hear you enjoyed the post!
Yeah, the comment is great;>
@MazeGen
Thanks for your comment!
Yeah, I know, but my paper edition (which I read when traveling to/from work) is from 2006, hence this post.
Guess it's time to order a new one :)
http://kosiara87.blogspot.com/2011/01/virtualbox-40-machine-cloner.html
Add a comment: