Skip to content
This repository has been archived by the owner on Oct 18, 2020. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
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 366bc97
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 7 deletions.
127 changes: 127 additions & 0 deletions rekall/plugins/addrspaces/amd64.py
Expand Up @@ -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
Expand All @@ -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


Expand Down Expand Up @@ -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)
4 changes: 0 additions & 4 deletions rekall/plugins/core.py
Expand Up @@ -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'
Expand Down
52 changes: 49 additions & 3 deletions rekall/plugins/linux/common.py
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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)
Expand All @@ -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):
Expand Down
17 changes: 17 additions & 0 deletions rekall/plugins/windows/common.py
Expand Up @@ -28,6 +28,7 @@
import logging
import re

from rekall import addrspace
from rekall import scan
from rekall import obj
from rekall import kb
Expand Down Expand Up @@ -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]"),
Expand Down

0 comments on commit 366bc97

Please sign in to comment.