Permalink
Browse files

Added detection and support for ParaVirtualized XEN guests.

Refactored find_dtb plugins to do os-specific checks in the os-specific plugins instead of the base class.

BUG=
R=scudette@gmail.com

Review URL: https://codereview.appspot.com/200130043
  • Loading branch information...
parkisan committed Jan 31, 2015
1 parent 5a58a5e commit 366bc975bdd8e33cf1b152d2241d931c38900f23
Showing with 193 additions and 7 deletions.
  1. +127 −0 rekall/plugins/addrspaces/amd64.py
  2. +0 −4 rekall/plugins/core.py
  3. +49 −3 rekall/plugins/linux/common.py
  4. +17 −0 rekall/plugins/windows/common.py
@@ -4,6 +4,7 @@
# Authors:
# Mike Auty
# Michael Cohen
# Jordi Sanchez
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,9 +25,11 @@
"""
# pylint: disable=protected-access
import logging
import struct
from rekall import config
from rekall import obj
from rekall.plugins.addrspaces import intel
@@ -308,3 +311,127 @@ def get_pml4e(self, vaddr):
def __str__(self):
return "%s@0x%08X" % (self.__class__.__name__, self._ept)
class XenParaVirtAMD64PagedMemory(AMD64PagedMemory):
"""XEN ParaVirtualized guest address space."""
PAGE_SIZE = 0x1000
P2M_PER_PAGE = P2M_TOP_PER_PAGE = P2M_MID_PER_PAGE = PAGE_SIZE / 8
def __init__(self, **kwargs):
super(XenParaVirtAMD64PagedMemory, self).__init__(**kwargs)
self.page_offset = self.session.GetParameter("page_offset")
self.m2p_mapping = {}
if self.page_offset:
self._RebuildM2PMapping()
def _ReadP2M(self, offset, p2m_size):
"""Helper function to return p2m entries at offset.
This function is used to speed up reading the p2m tree, because
traversal via the Array struct is slow.
Yields tuples of (index, p2m) for each p2m, up to a number of p2m_size.
"""
for index, mfn in zip(
xrange(0, p2m_size),
struct.unpack(
"<" + "Q" * p2m_size,
self.read(offset, 0x1000))):
yield (index, mfn)
def _RebuildM2PMapping(self):
"""Rebuilds the machine to physical mapping.
A XEN ParaVirtualized kernel (the guest) maintains a special set of
page tables. Each entry is to machine (host) memory instead of
physical (guest) memory.
XEN maintains a mapping of machine to physical and mapping of physical
to machine mapping in a set of trees. We need to use the former to
translate the machine addresses in the page tables, but only the later
tree is available (mapped in memory) on the guest.
When rekall is run against the memory of a paravirtualized Linux kernel
we traverse the physical to machine mapping and invert it so we can
quickly translate from machine (host) addresses to guest physical
addresses.
See: http://lxr.free-electrons.com/source/arch/x86/xen/p2m.c?v=3.0 for
reference.
"""
logging.debug("Rebuilding the machine to physical mapping...")
p2m_top_location = self.session.profile.get_constant_object(
"p2m_top", "Pointer", vm=self)
end_value = self.session.profile.get_constant("__bss_stop", True)
new_mapping = {}
for p2m_top in self._ReadP2M(p2m_top_location.v(), self.P2M_TOP_PER_PAGE):
p2m_top_idx, p2m_top_entry = p2m_top
self.session.report_progress("Building m2p map %.02f%%" % (
100 * (float(p2m_top_idx) / self.P2M_TOP_PER_PAGE)))
if p2m_top_entry == end_value:
continue
for p2m_mid in self._ReadP2M(p2m_top_entry, self.P2M_MID_PER_PAGE):
p2m_mid_idx, p2m_mid_entry = p2m_mid
if p2m_mid_entry == end_value:
continue
for p2m in self._ReadP2M(p2m_mid_entry, self.P2M_PER_PAGE):
p2m_idx, mfn = p2m
pfn = (p2m_top_idx*self.P2M_MID_PER_PAGE*self.P2M_PER_PAGE
+ p2m_mid_idx*self.P2M_PER_PAGE
+ p2m_idx)
new_mapping[mfn] = pfn
self.m2p_mapping = new_mapping
self.session.SetParameter("mapping", self.m2p_mapping)
def m2p(self, machine_address):
"""Translates from a machine address to a physical address.
This translates host physical addresses to guest physical.
Requires a machine to physical mapping to have been calculated.
"""
machine_address = obj.Pointer.integer_to_address(machine_address)
mfn = machine_address / 0x1000
pfn = self.m2p_mapping.get(mfn)
if pfn is None:
return 0
return (pfn * 0x1000) | (0xFFF & machine_address)
def get_pml4e(self, vaddr):
return self.m2p(
super(XenParaVirtAMD64PagedMemory, self).get_pml4e(vaddr))
def get_pdpte(self, vaddr, pml4e):
return self.m2p(
super(XenParaVirtAMD64PagedMemory, self).get_pdpte(vaddr, pml4e))
def get_pde(self, vaddr, pml4e):
return self.m2p(
super(XenParaVirtAMD64PagedMemory, self).get_pde(vaddr, pml4e))
def get_pte(self, vaddr, pml4e):
return self.m2p(
super(XenParaVirtAMD64PagedMemory, self).get_pte(vaddr, pml4e))
def vtop(self, vaddr):
vaddr = obj.Pointer.integer_to_address(vaddr)
if not self.session.GetParameter("mapping"):
# Simple shortcut for linux. This is required for the first set
# of virtual to physical resolutions while we're building the
# mapping.
if self.page_offset and vaddr > self.page_offset:
return vaddr - self.page_offset
# Try to update the mapping
self._RebuildM2PMapping()
return super(XenParaVirtAMD64PagedMemory, self).vtop(vaddr)
View
@@ -271,10 +271,6 @@ def GetAddressSpaceImplementation(self):
if architecture == "AMD64":
impl = "AMD64PagedMemory"
# Add specific support for windows.
if self.profile.metadata("os") == "windows":
impl = "WindowsAMD64PagedMemory"
# PAE profiles go with the pae address space.
elif architecture == "I386" and self.profile.metadata("pae"):
impl = 'IA32PagedMemoryPae'
@@ -24,7 +24,9 @@
"""
import logging
import re
import struct
from rekall import addrspace
from rekall import kb
from rekall import obj
from rekall import plugin
@@ -135,8 +137,31 @@ def GetPageOffset(cls, profile):
profile.get_constant("phys_startup_32", False))
elif profile.metadata("arch") == "AMD64":
return (profile.get_constant("_text", False) -
profile.get_constant("phys_startup_64", False))
# We can calculate phys_startup_64 when it's not present in the
# profile:
#
# From arch/x86/kernel/vmlinux.lds.S
# 17 #ifdef CONFIG_X86_32
# 18 #define LOAD_OFFSET __PAGE_OFFSET
# 19 #else
# 20 #define LOAD_OFFSET __START_KERNEL_map
# 21 #endif
# [...]
# 84 #ifdef CONFIG_X86_32
# 85 . = LOAD_OFFSET + LOAD_PHYSICAL_ADDR;
# 86 phys_startup_32 = startup_32 - LOAD_OFFSET;
# 87 #else
# 88 . = __START_KERNEL;
# 89 phys_startup_64 = startup_64 - LOAD_OFFSET;
# 90 #endif
#
# From arch/x86/include/asm/page_64_types.h:
# #define __START_KERNEL_map _AC(0xffffffff80000000, UL)
_phys_startup_64 = (profile.get_constant("startup_64", False)
- 0xffffffff80000000)
phys_startup_64 = (profile.get_constant("phys_startup_64", False) or
_phys_startup_64)
return profile.get_constant("_text", False) - phys_startup_64
elif profile.metadata("arch") == "MIPS":
return 0x80000000
@@ -146,6 +171,10 @@ def GetPageOffset(cls, profile):
def VerifyHit(self, dtb):
"""Returns a valid address_space if the dtb is valid."""
self.session.SetParameter("page_offset", obj.Pointer.integer_to_address(
self.GetPageOffset(self.profile)))
address_space = super(LinuxFindDTB, self).VerifyHit(dtb)
if address_space:
# Try to verify the profile by checking the linux_proc_banner.
@@ -158,6 +187,23 @@ def VerifyHit(self, dtb):
logging.debug("Failed to verify dtb @ %#x" % dtb)
def GetAddressSpaceImplementation(self):
"""Returns the correct address space class for this profile."""
# The virtual address space implementation is chosen by the profile.
architecture = self.profile.metadata("arch")
if architecture == "AMD64":
p2m_top_p = (self.profile.get_constant("p2m_top", False)
- self.GetPageOffset(self.profile))
p2m_top = struct.unpack("<Q",
self.physical_address_space.read(
p2m_top_p, 8))[0]
if p2m_top:
logging.debug("Detected paravirtualized XEN guest.")
impl = "XenParaVirtAMD64PagedMemory"
as_class = addrspace.BaseAddressSpace.classes[impl]
return as_class
return super(LinuxFindDTB, self).GetAddressSpaceImplementation()
def dtb_hits(self):
"""Tries to locate the DTB."""
PAGE_OFFSET = self.GetPageOffset(self.profile)
@@ -168,7 +214,7 @@ def dtb_hits(self):
yield self.profile.get_constant("swapper_pg_dir") - PAGE_OFFSET
else:
yield (self.profile.get_constant("init_level4_pgt", True) -
yield (self.profile.get_constant("init_level4_pgt", False) -
PAGE_OFFSET)
def render(self, renderer):
@@ -28,6 +28,7 @@
import logging
import re
from rekall import addrspace
from rekall import scan
from rekall import obj
from rekall import kb
@@ -159,6 +160,22 @@ def VerifyHit(self, hit):
return address_space
def GetAddressSpaceImplementation(self):
"""Returns the correct address space class for this profile."""
# The virtual address space implementation is chosen by the profile.
architecture = self.profile.metadata("arch")
if architecture == "AMD64":
impl = "WindowsAMD64PagedMemory"
# PAE profiles go with the pae address space.
elif architecture == "I386" and self.profile.metadata("pae"):
impl = "WindowsIA32PagedMemoryPae"
else:
return super(WinFindDTB, self).GetAddressSpaceImplementation()
as_class = addrspace.BaseAddressSpace.classes[impl]
return as_class
def render(self, renderer):
renderer.table_header(
[("_EPROCESS (P)", "physical_eprocess", "[addrpad]"),

0 comments on commit 366bc97

Please sign in to comment.