Closed GoogleCodeExporter closed 8 years ago
We use the KPCR as a fallback for finding the KDBG structure, so we'll want to
decide which one we're intending to rely on and then make sure we stick to
that. Going around in circles trying to find one our the other would be very
bad...
Original comment by mike.auty@gmail.com
on 1 Jun 2012 at 5:57
Ah you're right, I forgot about the backup. So the backup is good because
someone could modify the "KDBG" signature that our kdbgscan relies on, without
causing instability to the system. I have a gut feeling if someone changed the
self-referencing members of KPCR (which is what kpcrscan relies on) then the
system would quickly become instable. It would be good to keep the backup
around for those cases, although the backup method was broken from Jan 2011 to
April 2012 (see r1612) and no one noticed (meaning it was probably never
used/needed in over a year).
I still think we can improve in this area. We definitely want to maintain
robustness, but I'm not sure the frequency that its useful (how often do
attackers modify the "KDBG" header) is worth the time it takes up for normal
analysis. Consider BDG's robust process scanner for instance, its an awesome
option to have, but we still just use psscan (pool tag scanner) for everyday
usage and only the robust scanner if there's reason to believe pool tags were
modified. That reminds me, anyone want to convert the robust scanner to 2.1? I
thought AW had tried to port it to 2.0 but found it was crazy slow.
So anyway, it seems like if KPCR can be found from KDBG, then we should
leverage that in the imageinfo plugin. Currently imageinfo scans for KDBG, then
scans again for KPCR, and only reports the first KPCR found, whether its for
CPU #0 or CPU #8. It can be misleading when the output of imageinfo says there
are multiple processors but only lists one KPCR. Hmm more thoughts welcome...
Original comment by michael.hale@gmail.com
on 1 Jun 2012 at 7:10
Oh another thing about the backup method...it won't always work on x64.
Although we do have a working kpcrscan for x64 (see Issue #184), the backup
method of finding KDBG via KPCR is the part that may not always work. This is
because the _KPCR.KdVersionBlock pointer is often NULL in x64 systems (its
always like this, not just the result of a bad acquisition or something). For
example, you see when attaching with a kernel debugger:
kd> !pcr 0
KPCR for Processor 0 at fffff800019c3500:
kd> dt _KPCR fffff800019c3500 KdVersionBlock
nt!_KPCR
+0x108 KdVersionBlock : (null)
So if KdVersionBlock is NULL, we cannot find KDBG from KPCR. However, if
KdVersionBlock is NULL, we can fallback to a different way of finding
KdVersionBlock, which is what I'm coding up right now.
Original comment by michael.hale@gmail.com
on 1 Jun 2012 at 8:55
We're not going to hold 2.1.x back on this, given it's how it's always worked
(so people aren't getting *worse* performance).
Original comment by mike.auty@gmail.com
on 6 Jun 2012 at 8:31
OK, I wanted to update with some findings and conclusions (I think we can close
the issue after this, and the next small patch which I'm about to apply). Here
are the conclusions up front if you don't want to read the details:
* kpcrscan is not unnecessary, but the cases in which it can serve as a backup
for finding KDBG are limited
* if the primary way of finding KDBG fails because someone overwrites the
"KDBG<header>", then the backup way will fail also, because even when found via
KPCR, the KDBG header string "KDBG" must be present to be considered valid
* only the KPCR for the first CPU has a valid KdVersionBlock, and sometimes,
especially on x64, even the KPCR for the first (and only) CPU has a NULL
KdVersionBlock (which prevents us from finding KDBG). the VolMagic returns the
first KPCR found, but not necessarily the KPCR for the first CPU. therefore, to
make the backup method as robust as possible, we should iterate though
VolMagic.generate_suggestions() until we find a KPCR with a valid
KdVersionBlock (if any exist).
So for the details, here's the output of kpcrscan (modified to show different
fields) on a Win7 x86 system with 2 CPUs.
$ python vol.py -f b0f427db.vmem --profile=Win7SP0x86 kpcrscan
Volatile Systems Volatility Framework 2.1_alpha
Offset(V) KdVersionBlock IDT GDT CurrentThread Number VendorString Cr3
---------- -------------- ---------- ---------- ------------- ----------
--------------------
0x807c4000 0x000000000000 0x807cd020 0x807ccc20 0x00086898290 1 GenuineIntel
0x3e553780
0x82969c00 0x000082968c00 0x80b95400 0x80b95000 0x00085719d48 0 GenuineIntel
0x3e553780
As you can see, the first KPCR found at offset 0x807c4000 is for CPU #1 and its
KdVersionBlock is NULL. In this case, even our current backup method would
fail. It needs to go on and find the second KPCR at offset 0x82969c00 which is
for CPU #0.
The next output is from a 2008 x86 with 4 CPUs.
$ python vol.py -f 045712.raw --profile=Win2008SP2x86 kpcrscan
Volatile Systems Volatility Framework 2.1_alpha
Offset(V) KdVersionBlock IDT GDT CurrentThread Number VendorString Cr3
---------- -------------- ---------- ---------- ------------- ----------
--------------------
0x806d1000 0x000000000000 0x806d8950 0x806d8550 0x000806d51e0 1 GenuineIntel
0x122000
0x80705000 0x000000000000 0x8070c950 0x8070c550 0x000807091e0 2 GenuineIntel
0x122000
0x80739000 0x000000000000 0x80740950 0x80740550 0x0008073d1e0 3 GenuineIntel
0x122000
0x81925800 0x000081924c70 0x814fe400 0x814fe000 0x0008402ad78 0 GenuineIntel
0x122000
In this case, the KPCR for CPU #0 (the only one with a valid KdVersionBlock) is
the last one found by the scanner...so taking the first/best suggestion from
the VolMagic would not work.
Next output is from 2003 x64 with a single CPU:
$ python vol.py -f 6f1bedec.vmem kpcrscan --profile=Win2003SP2x64
Volatile Systems Volatility Framework 2.1_alpha
Offset(V) KdVersionBlock IDT GDT CurrentThread Number VendorString Cr3
------------------ ------------------ ------------------ ------------------
------------------ ---------- --------------------
0x0000f80001177000 0xfffff80001175cb0 0xfffff800004f7070 0xfffff800004f7000
0xfffff8000117b300 0 GenuineIntel 0x1a293000
Last but not least, here's the output from a Vista x64 with a single CPU:
$ python vol.py -f 9fe7abd5.vmem --profile=VistaSP2x64 kpcrscan
Volatile Systems Volatility Framework 2.1_alpha
Offset(V) KdVersionBlock IDT GDT
CurrentThread Number VendorString Cr3
------------------ ------------------ ------------------ ------------------
------------------ ---------- -------------------- ------------------
0x0000f800019c3500 0x0000000000000000 0xfffff80002b67070 0xfffff80002b67000
0xfffffa80049926c0 0 GenuineIntel 0x299a1000
So of the two systems tested with 1 CPU, the (one and only) KPCR does not have
a valid KdVersionBlock on one, but does have a valid value on the other.
Original comment by michael.hale@gmail.com
on 12 Jun 2012 at 3:45
This issue was closed by revision r1865.
Original comment by michael.hale@gmail.com
on 12 Jun 2012 at 3:47
Original issue reported on code.google.com by
michael.hale@gmail.com
on 1 Jun 2012 at 1:57