Skip to content

Commit

Permalink
Fixed a linux bug where KASLR was not calculated if the profile was e…
Browse files Browse the repository at this point in the history
…xplicitely given.

Fixed a bug with the file address space which caused physical scan to not complete because the address_space.end() grew when read past the end.

[email protected]

Review URL: https://codereview.appspot.com/307200043 .
  • Loading branch information
scudette committed Sep 3, 2016
1 parent 89b67ae commit e9111eb
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 93 deletions.
3 changes: 1 addition & 2 deletions rekall-core/rekall/plugins/addrspaces/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ def read(self, addr, length):
try:
self.fhandle.seek(addr)
data = self.fhandle.read(length)
if (addr + len(data)) > self.fsize:
self.fsize = addr + len(data)

return data + addrspace.ZEROER.GetZeros(length - len(data))
except IOError:
return addrspace.ZEROER.GetZeros(length)
Expand Down
107 changes: 17 additions & 90 deletions rekall-core/rekall/plugins/guess_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,15 +436,6 @@ class LinuxIndexDetector(DetectionMethod):

name = "linux_index"

# Location of the kallsyms file. We use it to do instant, accurate detection
# of the profile.
KALLSYMS_FILE = "/proc/kallsyms"

# The regular expression to parse the kallsyms file.
KALLSYMS_REGEXP = ("(?P<offset>[0-9a-fA-F]+) "
"(?P<type>[a-zA-Z]) "
"(?P<symbol>[^ ]+)"
"(\t(?P<module>[^ ]+))?$")

find_dtb_impl = linux_common.LinuxFindDTB

Expand All @@ -455,96 +446,23 @@ def __init__(self, **kwargs):
def Offsets(self):
return [0]

def _ParseKallsym(self, line):
"""Parses a single symbol line from a symbols file.
This is the result of obtaining symbols from nm:
0000000000 t linux_proc_banner
0000000010 s other_symbol [module]
Yields:
Tuple of offset, symbol_name, type, module
"""

matches = None
if line:
matches = re.match(self.KALLSYMS_REGEXP, line.rstrip("\n"))

if not matches:
raise ValueError("Invalid line: %s", line)

# Obtain all fields from the kallsyms entry
offset = matches.group("offset")
symbol_type = matches.group("type")
symbol = matches.group("symbol")
try:
module = matches.group("module")
except IndexError:
module = None

try:
offset = obj.Pointer.integer_to_address(int(offset, 16))
except ValueError:
pass

return offset, symbol, symbol_type, module

def ObtainSymbols(self, address_space):
"""Obtain symbol names and values for a live machine.
Yields:
Tuples of offset, symbol_name, type, module
"""

match_failures = 0

kallsyms_as = self._OpenLiveSymbolsFile(address_space)

if not kallsyms_as:
return

data = kallsyms_as.read(0, kallsyms_as.end())
if not data:
# Try to fully read the file
read_length = 1*1024*1024
data = kallsyms_as.read(0, read_length)
while len(data) <= kallsyms_as.end() and read_length < 2**30:
read_length *= 2
data = kallsyms_as.read(0, read_length)
# Truncate data to the actual size
data = data[:kallsyms_as.end()]

self.session.logging.debug(
"Found /proc/kallsyms of size: %d", len(data))

for line in data.split(os.linesep):
if match_failures >= 50:
break

try:
yield self._ParseKallsym(line)
except ValueError:
match_failures += 1
continue

def _OpenLiveSymbolsFile(self, address_space):
"""Opens the live symbols file to parse."""

return address_space.get_file_address_space("/proc/kallsyms")

def DetectFromHit(self, hit, offset, address_space):
if offset != 0:
return

self.session.logging.debug(
"LinuxIndexDetector:DetectFromHit(%x) = %s", offset, hit)

kaslr_reader = linux_common.KAllSyms(self.session)

# We create a dictionary of symbol:offset skipping symbols from
# exported modules.
symbol_dict = dict([(s[1], s[0])
for s in self.ObtainSymbols(address_space)
if not s[3]])
symbol_dict = {}
for offset, symbol, _, module in kaslr_reader.ObtainSymbols():
# Ignore symbols in modules we only care about the kernel.
if not module:
symbol_dict[symbol] = offset

if not symbol_dict:
return

Expand Down Expand Up @@ -699,6 +617,13 @@ class ProfileHook(kb.ParameterHook):
volatile = False

def ScanProfiles(self):
try:
self.session.SetCache("execution_phase", "ProfileAutodetect")
return self._ScanProfiles()
finally:
self.session.SetCache("execution_phase", None)

def _ScanProfiles(self):
address_space = self.session.physical_address_space
best_profile = None
best_match = 0
Expand Down Expand Up @@ -807,6 +732,8 @@ def calculate(self):
# No physical address space - nothing to do here.
return obj.NoneObject("No Physical Address Space.")

# If the global cache is persistent we try to detect this image by
# fingerprint if we have seen it before.
if self.session.cache.__class__ == cache.FileCache:
name = self.session.cache.DetectImage(
self.session.physical_address_space)
Expand Down
134 changes: 134 additions & 0 deletions rekall-core/rekall/plugins/linux/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,117 @@
@contact: [email protected]
@organization: Digital Forensics Solutions
"""
import os
import re

from rekall import addrspace
from rekall import kb
from rekall import plugin
from rekall import obj
from rekall import utils

from rekall.plugins import core


class KAllSyms(object):
"""A parser for KAllSyms files."""
# Location of the kallsyms file. We use it to do instant, accurate detection
# of the profile.
KALLSYMS_FILE = "/proc/kallsyms"

# The regular expression to parse the kallsyms file.
KALLSYMS_REGEXP = ("(?P<offset>[0-9a-fA-F]+) "
"(?P<type>[a-zA-Z]) "
"(?P<symbol>[^ ]+)"
"(\t(?P<module>[^ ]+))?$")

def __init__(self, session):
self.session = session
self.physical_address_space = session.physical_address_space

def _ParseKallsym(self, line):
"""Parses a single symbol line from a symbols file.
This is the result of obtaining symbols from nm:
0000000000 t linux_proc_banner
0000000010 s other_symbol [module]
Yields:
Tuple of offset, symbol_name, type, module
"""

matches = None
if line:
matches = re.match(self.KALLSYMS_REGEXP, line.rstrip("\n"))

if not matches:
raise ValueError("Invalid line: %s", line)

# Obtain all fields from the kallsyms entry
offset = matches.group("offset")
symbol_type = matches.group("type")
symbol = matches.group("symbol")
try:
module = matches.group("module")
except IndexError:
module = None

try:
offset = obj.Pointer.integer_to_address(int(offset, 16))
except ValueError:
pass

return offset, symbol, symbol_type, module

def ObtainSymbols(self):
"""Obtain symbol names and values for a live machine.
Yields:
Tuples of offset, symbol_name, type, module
"""
kallsyms_as = self._OpenLiveSymbolsFile(
self.physical_address_space)

if not kallsyms_as:
return []

# Proc files do not have a stat and the address space shows it as being
# of zero length.
if kallsyms_as.end() != 0:
data = kallsyms_as.read(0, kallsyms_as.end())
else:
# Try to fully read the file
read_length = 1*1024*1024
data = kallsyms_as.read(0, read_length).strip("\x00")
while len(data) == read_length and read_length < 2**30:
read_length *= 2
data = kallsyms_as.read(0, read_length).strip("\x00")

return self.parse_data(data)

def parse_data(self, data):
self.session.logging.debug(
"Found %s of size: %d", self.KALLSYMS_FILE, len(data))

match_failures = 0
for line in data.split(os.linesep):
if match_failures >= 50:
break

try:
yield self._ParseKallsym(line)
except ValueError:
match_failures += 1
continue

def _OpenLiveSymbolsFile(self, physical_address_space):
"""Opens the live symbols file to parse."""

return physical_address_space.get_file_address_space(
self.KALLSYMS_FILE)


class AbstractLinuxCommandPlugin(plugin.PhysicalASMixin,
plugin.TypedProfileCommand,
plugin.ProfileCommand):
Expand Down Expand Up @@ -310,6 +412,38 @@ def calculate(self):
return self.session.profile.GetPageOffset()


class LinuxKASLR(AbstractLinuxParameterHook):
"""The Kernel Address Space Randomization constant.
Note that this function assumes the profile is already correct. It is
not called during the profile guessing phase. So in reality this will
only come into play when the user provided the profile specifically.
"""
name = "kernel_slide"

def calculate(self):
if self.session.GetCache("execution_phase") == "ProfileAutodetect":
raise RuntimeError(
"Attempting to get KASLR slide during profile "
"autodetection.")

# Try to get the kernel slide if kallsyms is available.
for offset, symbol, type, module in KAllSyms(
self.session).ObtainSymbols():
if not module and type in ["r", "R"]:
offset_from_profile = self.session.profile.get_constant(
symbol)

# The KASLR is the difference between the profile and the
# kallsyms.
return offset - offset_from_profile

# We might want to scan for the kernel slide but the search space is
# really large, so right now we just do nothing.
return 0



class LinuxInitTaskHook(AbstractLinuxParameterHook):
name = "pslist_InitTask"

Expand Down
2 changes: 1 addition & 1 deletion rekall-core/rekall/plugins/overlays/windows/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@

"_LDR_DATA_TABLE_ENTRY": [None, {
"TimeDateStamp": [None, ["WinFileTime"]],
"LoadReason": lambda x: x.m("LoadReason") or x.m("LoadCount")
"LoadReason": lambda x: x.multi_m("LoadReason", "LoadCount")
}],

'_PHYSICAL_MEMORY_DESCRIPTOR' : [None, {
Expand Down

0 comments on commit e9111eb

Please sign in to comment.