Package rekall :: Package plugins :: Package response :: Module linux
[frames] | no frames]

Source Code for Module rekall.plugins.response.linux

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