/usr/lib/python2.7/dist-packages/rekall/plugins/response/linux.py is in python-rekall-core 1.6.0+dfsg-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """Linux specific response plugins."""
import os
import platform
from rekall import addrspace
from rekall import utils
from rekall.plugins.common import address_resolver
from rekall.plugins.response import common
from rekall.plugins.response import processes
from rekall.plugins.overlays import basic
class LiveMap(utils.SlottedObject):
__slots__ = ("start", "end", "perms", "file_offset", "dev", "inode",
"filename")
@utils.safe_property
def length(self):
return self.end - self.start
class IRMaps(processes.APIProcessFilter):
"""Examine the process memory maps."""
name = "maps"
__args = [
dict(name="regex", type="RegEx",
help="A regular expression to filter VAD filenames."),
dict(name="offset", type="SymbolAddress",
help="Only print the vad corresponding to this offset."),
dict(name="verbosity", type="IntParser", default=1,
help="With high verbosity print more information on each region."),
]
table_header = [
dict(name='proc', type="proc", hidden=True),
dict(name="divider", type="Divider"),
dict(name='Map', hidden=True),
dict(name='start', style="address"),
dict(name='end', style="address"),
dict(name='perms', width=4),
dict(name='filename')
]
def generate_maps(self, pid):
# Details of this are here: http://goo.gl/fmebo
# On linux its easy - just parse /proc/ filesystem.
try:
maps_data = open("/proc/%s/maps" % pid).read()
except (OSError, IOError):
return
for line in maps_data.splitlines():
result = LiveMap()
parts = line.split()
start, end = parts[0].split("-")
result.start = int(start, 16)
result.end = int(end, 16)
result.perms = parts[1]
result.file_offset = parts[2]
result.dev = parts[3]
result.inode = int(parts[4])
try:
result.filename = parts[5]
except IndexError:
pass
yield result
def merge_ranges(self, pid):
"""Generate merged ranges."""
old_maps = None
for maps in self.generate_maps(pid):
# Try to merge this range with the previous range.
if (old_maps and
old_maps.end == maps.start and
old_maps.filename == maps.filename):
old_maps.end = maps.end
continue
# Yield the old range:
if old_maps:
yield old_maps
old_maps = maps
# Emit the last range.
if old_maps:
yield old_maps
def collect(self):
generator = self.generate_maps
if self.plugin_args.verbosity <= 1:
generator = self.merge_ranges
for proc in self.filter_processes():
divider = "{0} pid: {1:6}\n".format(proc.name, proc.pid)
yield dict(divider=divider)
for maps in generator(proc.pid):
if (self.plugin_args.regex and not
self.plugin_args.regex.search(maps.filename or "")):
continue
if (self.plugin_args.offset is not None and
not maps.start <= self.plugin_args.offset <= maps.end):
continue
yield dict(proc=proc,
Map=maps,
start=maps.start,
end=maps.end,
perms=maps.perms,
filename=maps.filename)
class LinuxAPIProfile(common.APIBaseProfile):
"""Profile for Linux live analysis."""
def __init__(self, proc=None, **kwargs):
super(LinuxAPIProfile, self).__init__(**kwargs)
# TODO: Although it is possible to run 32 bit processes on 64 bit
# systems we dont detect this case. We set the profile architecture
# based on the operating system's platform.
arch, _ = platform.architecture()
if arch == "64bit":
basic.ProfileLP64.Initialize(self)
else:
basic.Profile32Bits.Initialize(self)
# Register the profile for Linux.
common.APIProfile = LinuxAPIProfile
class LinuxAPIProcessAddressSpace(addrspace.RunBasedAddressSpace):
"""An address space which read processes using ReadProcessMemory()."""
def __init__(self, pid=None, **kwargs):
super(LinuxAPIProcessAddressSpace, self).__init__(**kwargs)
self.pid = pid
try:
self.process_handle = open("/proc/%s/mem" % pid, "rb")
for maps in self.session.plugins.maps().merge_ranges(pid):
self.add_run(maps.start, maps.start, maps.length,
address_space=self, data=dict(
pid=pid, vad=maps))
except (IOError, OSError):
# We cant open the memory, just return an empty address space.
pass
def read(self, addr, length):
if length > self.session.GetParameter("buffer_size"):
raise IOError("Too much data to read.")
self.process_handle.seek(addr)
try:
return self.process_handle.read(length)
except IOError:
return addrspace.ZEROER.GetZeros(length)
def __str__(self):
return "%s(%s)" % (self.__class__.__name__, self.pid)
# Register the process AS as a Linux one.
common.IRProcessAddressSpace = LinuxAPIProcessAddressSpace
class MapModule(address_resolver.Module):
"""A module representing a memory mapping."""
class LinuxAPIAddressResolver(address_resolver.AddressResolverMixin,
common.AbstractAPICommandPlugin):
"""A Linux specific address resolver plugin."""
@staticmethod
def NormalizeModuleName(module_name):
if not module_name:
return ""
return os.path.basename(module_name)
def _EnsureInitialized(self):
if self._initialized:
return
task = self.session.GetParameter("process_context")
for row in self.session.plugins.maps(pids=task.pid):
maps = row.get("Map")
if not maps:
continue
self.AddModule(MapModule(
name=(self.NormalizeModuleName(maps.filename) or
"map_%#x" % maps.start),
start=maps.start, end=maps.end, session=self.session))
self._initialized = True
|