64bit Big Sized RAM Image Acquisition Problem

The Problem

One day, I acquired a 16GB raw memory image on Windows7 x64 machine using MoonSols DumpIt for malware investigation. Then I tried to analyze it using Volatility Framework and Redline, but they returned no output like this:

vol_no_result

I guessed the acquired image was corrupted, so I took an image again, but the result was the same. I googled about the problem and found a similar issue. According to the report, I tested several memory acquisition tools such as FTK Imager and Windows Memory Reader, but they didn’t work.

I changed an image format from raw to crashdump. Then Volatility Framework could parse it and display the result. Strangely, Volatility also could analyze the raw image converted from the crashdump. Where does this difference come from?

The Cause

I debugged Volatility and noticed it could not find _KDDEBUGGER_DATA64 structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def get_kdbg(addr_space):
    """A function designed to return the KDBG structure from 
    an address space. First we try scanning for KDBG and if 
    that fails, we try scanning for KPCR and bouncing back to
    KDBG from there. 

    Also note, both the primary and backup methods rely on the 
    4-byte KDBG.Header.OwnerTag. If someone overwrites this 
    value, then neither method will succeed. The same is true 
    even if a user specifies --kdbg, because we check for the 
    OwnerTag even in that case. 
    """

    kdbgo = obj.VolMagic(addr_space).KDBG.v()

    kdbg = obj.Object("\_KDDEBUGGER_DATA64", offset = kdbgo, vm = addr_space) 

    if kdbg.is_valid(): # <- failed
        return kdbg

    # Fall back to finding it via the KPCR. We cannot
    # accept the first/best suggestion, because only 
    # the KPCR for the first CPU allows us to find KDBG. 
    for kpcr_off in obj.VolMagic(addr_space).KPCR.generate_suggestions():
        
        kpcr = obj.Object("_KPCR", offset = kpcr_off, vm = addr_space)

        kdbg = kpcr.get_kdbg() 
    
        if kdbg.is_valid(): # <- failed
            return kdbg

    return obj.NoneObject("KDDEBUGGER structure not found using either KDBG signature or KPCR pointer")

_KDDEBUGGER_DATA64 includes various debug information like PsLoadedModuleList. Matthieu Suiche at MoonSols told me Vista or later Windows OSes on x64 machines encrypt the structure. So I compared a simply-dumped raw image with a raw image converted from crashdump.

kdbg_encrypted

DBGKD_DEBUG_DATA_HEADER64 in _KDDEBUGGER_DATA64 is highlighted. Surely, it seems to be encoded by a specific algorithm. I think there is a few people knowing the fact because the encryption is only applied to big size RAM (if not correct, please let me know). For instance, I checked _KDDEBUGGER_DATA64 in 1GB raw image was not encrypted and analysis tools worked fine.

Which acquisition tools should we use?

Matthieu said DumpIt implemented the decryption of _KDDEBUGGER_DATA64 structure for crashdump format only. I investigated whether acquisition tools including DumpIt can decrypt the structure. I showed the result in the folowing table.

acquisition_tools_result

The result means the only option is to take crashdump using DumpIt. Any other tools don’t decrypt. So, why DumpIt can decrypt the structure? I reverse-engineered the driver code and found it used KdSystemDebugControl to get _DBGKD_GET_VERSION64 and _KDDEBUGGER_DATA64, then made its crashdump header (1st page in .dmp) manually by inserting _KDDEBUGGER_DATA64 member values, but I couldn’t find the decryption routine by static code analysis. The local debugging API is related to the decryption? not sure.

KdSystemDebugControl

Just for the record,

64-bit crashdump images generated by Windows Memory Reader and winpmem couldn’t be analyzed by my EnScript, CrashDumpAnalyzer. I cannot understand the error because my EnScript reads debug information from the 1st header, so the EnScript is due to parse it even if _KDDEBUGGER_DATA64 structure is encrypted. I checked the 1st page.

1st_header

In a crashdump acquired by winpmem, NumberOfRuns of _PHYSICAL_MEMORY_DESCRIPTOR64 includes “PAGE” (this value should be defined as qword, correct?). That’s why the script fails in parsing the structure. I modified NumberOfRuns then could get the analysis result.