Security-X

Forum Security-X => News => Discussion démarrée par: igor51 le août 09, 2019, 01:00:47

Titre: [FireEye]Finding Evil in Windows 10 Compressed Memory, Part Two: Virtual Store Deep Dive
Posté par: igor51 le août 09, 2019, 01:00:47
Finding Evil in Windows 10 Compressed Memory, Part Two: Virtual Store
Deep Dive


Introduction


 

This blog post is the second in a three-part series covering our
  Windows 10 memory forensics research and it coincides with our     href="https://www.blackhat.com/us-19/briefings/schedule/index.html#paging-all-windows-geeks--finding-evil-in-windows--compressed-memory-15582">BlackHat
    USA 2019 presentation. In     href="/content/fireeye-www/en_US/blog/threat-research/2019/07/finding-evil-in-windows-ten-compressed-memory-part-one.html">Part
    One of the series, we covered the integration of the research in
  both Volatily and   class="code">Rekall memory forensics tools. We demonstrated
  that forensic artifacts (including reflectively loaded malware) could
  remain undiscovered without the FLARE research integration on Windows
  10 (available on GitHub at   href="https://github.com/fireeye/win10_volatility/">win10_volatility
  and win10_rekall).


 

In this post, we demonstrate how to retrieve a compressed page using
  the structures and algorithms described in our     href="/content/dam/fireeye-www/blog/pdfs/finding-evil-in-windows-10-compressed-memory-wp.pdf">white
  paper. We track down a compressed page in memory, beginning at its
  virtual address within a known process. A   class="code">WinDbg kernel debugger setup is used in this
  walkthrough, but a similar process could be followed from within a
  memory snapshot or extraction using   class="code">Volatility or Rekall.


 

Finding a Compressed Page


 

The operating system used in this demo is Windows
    10.0.15063.0 (x64)
and the structure definitions shown will
  be applicable across any 1703 build. Note
  that the two global offsets nt!SmGlobals and
    nt!MmPagingFile will need to be located
  for each revision. The process of retrieving these global offsets is
  described further in our     href="/content/dam/fireeye-www/blog/pdfs/finding-evil-in-windows-10-compressed-memory-wp.pdf">white paper.


 

To begin analysis, we create a marker page and flush it to the
  Virtual Store. This can be done in several ways, the easiest of which
  is allocating memory in a memory constrained virtual machine.  A
  simple utility (ram_eater.exe) was created
  to perform this task. The ram_eater utility
  allocates and writes a marker page, and then repeatedly allocates more
  memory in user-specified page amounts. In a memory constrained virtual
  machine (1 GB RAM), the marker page will become stale shortly and be
  evicted to the virtual store. In Figure 1,   class="code">ram_eater reports that it has allocated the marker
  page at address 0x2a368480000. The marker
  page we used (see Figure 2) was a string beginning with “    class="code">CC WAS HERE!”.


 


 
 
 Figure 1: Allocating a marker page using ram_eater_x64.exe


 

We can verify the contents of our marker page by locating it in the
  kernel debugger, viewing its Page Table Entry (PTE) and dumping its
  corresponding physical memory (see Figure 2). We use the   class="code">!process extension to locate   class="code">ram_eater’s EPROCESS
  structure and switch into the context of the   class="code">ram_eater process. This ensures that we traverse
  the correct process-specific page tables for the   class="code">ram_eater process. Using the page frame number
    (pfn) described by the hardware PTE, we
  dump the physical memory to validate the contents of our marker page.
  Page frame numbers do not include the low-order bits used to specify
  an offset into a page, therefore they must be multiplied by   class="code">PAGE_SIZE (0x1000) to
  identify the actual address of the data.


 


 
 
 Figure 2: Locating and viewing the marker
    page from the kernel debugger


 

After allocating additional memory using   class="code">ram_eater, we check to see if the marker page has
  been sent to the virtual store. Each entry in the output of the   class="code">!vm extension can be treated as an index in to
    nt!MmPagingFile (see Figure 3).


 


 
 
 Figure 3: PTE of a compressed page in the
    virtual store an confirmation of virtual store’s PageFile index


 

In the PTE displayed in Figure 3, the PageFile index (  class="code">MMPTE_SOFTWARE.PageFileLow) is 2 and corresponds
  to the “No Name for Paging File” entry in the   class="code">!vm extension’s output. From general observation,
  we know that on a default Windows configuration, the last entry
  corresponds to the virtual store. It is possible to configure systems
  with more than a single PageFile on disk, so do not assume that
  PageFile index 2 will always correlate to the virtual store.


 

A more thorough option to validate page file indices is to
  disassemble nt!MmStoreCheckPagefiles. This
  function contains references to two global variables, the number of
  active PageFiles, as well as an array of pointers to each   class="code">nt!_MMPAGING_FILE structure (see Figure 4). We use
  the PageFile structure’s newly introduced   class="code">VirtualStorePagefile field to confirm if the
  PageFile represents a virtual store.


 


 
 
 Figure 4: Locating nt!MmPagingFile in
    WinDbg and dumping system’s nt!_MMPAGING_FILE structures


 

Having confirmed that the marker page is in the virtual store, the
  next step is to calculate the Store Manager Page Key (  class="code">SM_PAGE_KEY), as it serves as a pseudo-handle to
  locate the decompressed page. Our     href="/content/dam/fireeye-www/blog/pdfs/finding-evil-in-windows-10-compressed-memory-wp.pdf">white
  paper details the process used to calculate the   class="code">SM_PAGE_KEY, which turns out to be   class="code">0x201a3061 for this example. Note, that we will
  not use the PTE’s swizzle bit in the page key calculations, since the
  OS build is below 1803. To begin page retrieval, the pointer to the
  Store Manager’s global structure or   class="code">nt!SmGlobals needs to be located. This is a
  straightforward process if symbols are available (see Figure 5).


 


 
 
 Figure 5: Dumping nt!SmGlobals


 

The first thing to observe is that both   class="code">SMKM_STORE_MGR and SMKM
  are located at offset 0x0, or directly at
    nt!SmGlobals. Viewed as a memory dump,
    nt!SmGlobals appears as an array of
  pointers. Viewed as a two-dimensional array (32x32) of   class="code">SMKM_STORE_METADATA elements, each element in the
  array of pointers points to an array of 32   class="code">SMKM_STORE_METADATA structures. Each   class="code">SMKM_STORE_METADATA structure represents a store.
  To locate our SM_PAGE_KEY’s corresponding
  store, we need to find the store index associated with the page key
  inside the SMKM_STORE_MGR.sGlobalTree B+tree
  container. The store index is a compound value that yields both
  indices needed to select the particular   class="code">SMKM_STORE_METADATA element. Let’s traverse the
    SMKM_STORE_MGR’s global B+tree (Figure 6).
  Recall that we are interested in a store manager page key value of
    0x201a3061.


 


 
 
 Figure 6: Traversing the global B+tree


 

Now that we have the store index (obtained from the   class="code">SMKM_FRONTEND_ENTRY structure) we calculate both
  indices to select the correct   class="code">SMKM_STORE_METADATA structure for our   class="code">SM_PAGE_KEY. The index in to the pointer array is
  the result of dividing the retrieved store index by 32, while the
  second one is the remainder of the division operation. In our case
  both indices are 0 and they select the first of the 1024 stores on the
  system, which is reserved for legacy applications. Universal Windows
  Platform (UWP) applications, on the other hand, will be placed in
  stores from 1 to 1023. Now, with the   class="code">SMKM_STORE_METADATA known, we examine the store’s
    SMKM_STORE structure, as shown in Figure 7.


 


 
 
 Figure 7: Dumping the SMKM_STORE structure


 

Once we have our SMKM_STORE structure we
  traverse another B+tree that associates our   class="code">SM_PAGE_KEY (0x201a3061)
  with a chunk key. The chunk key is a compound value and once decoded
  points to a specific page record inside   class="code">SMHP_CHUNK_METADATA's two-dimensional   class="code">aChunkPointer array. The B+tree traversal is shown
  in Figure 8.


 


 
 
 Figure 8: Traversing the local B+tree to
    find the chunk key associated with the SM_PAGE_KEY


 

After the B+tree traversal is complete we found that our chunk key
  is 4b02d. Since it’s a compound value we
  need to decode it in order to retrieve the two indices into   class="code">SMHP_CHUNK_METADATA’s chunk pointer array, and the
  offset within the located chunk. The decoding involves four additional
    SHMP_CHUNK_METADATA fields –   class="code">dwVectorSize,   class="code">dwPageRecordsPerChunk,   class="code">dwPageRecordSize, and   class="code">dwChunkPageHeaderSize. The process is shown in
  Figure 9.


 


 
 
 Figure 9: Retrieving the page record
    associated with the chunk key


 

The decoding of the chunk key in Figure 9 allowed us to find all the
  information to derive the virtual address of our compressed page. The
  retrieved REGION_KEY (  class="code">0xf72397, in our case) is also a compound value
  that encodes the index within the   class="code">SMKM_STORE’s region pointer array, as well as the
  offset within the region of pages. To calculate this data, we parse
  the region key with the help of two fields inside the   class="code">ST_DATA_MGR structure –   class="code">dwRegionIndexMask and   class="code">dwRegionSizeMask. The calculations are shown in
  Figure 10.


 


 
 
 Figure 10: Calculating the compressed
    page’s virtual address


 

The virtual address 0x12f3970 calculated
  in Figure 10 contains the compressed page of interest. We can retrieve
  it from the MemCompression process space, as
  shown in Figure 11. To confirm that the compressed memory is located
  within MemCompression, check the   class="code">SMKM_STORE structure’s   class="code">StoreOwnerProcess field.


 


 
 
 Figure 11: Retrieving the compressed page
    from within MemCompression process space


 

The compressed page can be decompressed with a call to the   class="code">RtlDecompressBufferEx API or any other
  implementation that supports the XPRESS
  compression algorithm.


 

Conclusion


 

In this blog post, we shared a walkthrough in which we forced a
  known marker page into the compression store and manually retrieved it
  by walking through memory dumps using known structure offsets from
  Windows 10 1709 x64. The same techniques used here can be applied to
    Windows 10 1607 and onwards assuming
  correct structure offsets are known. In Part 3 of the series,
          href="/content/fireeye-www/en_US/blog/threat-research/2019/08/finding-evil-in-windows-ten-compressed-memory-part-three.html">Automating
      Undocumented Structure Extraction
, we will look at how the
  FLARE team leveraged emulation via flare-emu to automate the
  extraction of the structures used in this walkthrough.


 

Resources


 
Source: Finding Evil in Windows 10 Compressed Memory, Part Two: Virtual Store
Deep Dive (http://)