Package rekall :: Package plugins :: Package windows :: Module vadinfo
[frames] | no frames]

Source Code for Module rekall.plugins.windows.vadinfo

  1  # Rekall Memory Forensics 
  2  # 
  3  # Based on the source code from 
  4  # Volatools Basic 
  5  # Copyright (C) 2007 Komoku, Inc. 
  6  # Copyright 2013 Google Inc. All Rights Reserved. 
  7  # 
  8  # Authors: 
  9  # Michael Cohen <scudette@gmail.com> 
 10  # Brendan Dolan-Gavitt <bdolangavitt@wesleyan.edu> 
 11  # Mike Auty <mike.auty@gmail.com> 
 12  # 
 13  # This program is free software; you can redistribute it and/or modify 
 14  # it under the terms of the GNU General Public License as published by 
 15  # the Free Software Foundation; either version 2 of the License, or (at 
 16  # your option) any later version. 
 17  # 
 18  # This program is distributed in the hope that it will be useful, but 
 19  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 20  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 21  # General Public License for more details. 
 22  # 
 23  # You should have received a copy of the GNU General Public License 
 24  # along with this program; if not, write to the Free Software 
 25  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 26  # 
 27  # The source code in this file was inspired by the excellent work of 
 28  # Brendan Dolan-Gavitt. Background information can be found in 
 29  # the following reference: 
 30  # "The VAD Tree: A Process-Eye View of Physical Memory," Brendan Dolan-Gavitt 
 31   
 32  # pylint: disable=protected-access 
 33   
 34  import re 
 35   
 36  from rekall import scan 
 37  from rekall.plugins import core 
 38  from rekall.plugins.addrspaces import intel 
 39  from rekall.plugins.common import pfn 
 40  from rekall.plugins.windows import address_resolver 
 41  from rekall.plugins.windows import common 
 42  from rekall.plugins.windows import pagefile 
 43  from rekall_lib import utils 
44 45 46 -class VAD(common.WinProcessFilter):
47 """Concise dump of the VAD. 48 49 Similar to windbg's !vad. 50 """ 51 52 __name = "vad" 53 54 __args = [ 55 dict(name="regex", type="RegEx", 56 help="A regular expression to filter VAD filenames."), 57 58 dict(name="offset", type="IntParser", 59 help="Only print the vad corresponding to this offset.") 60 ] 61 62 table_header = [ 63 dict(name='_EPROCESS', type="_EPROCESS", hidden=True), 64 dict(name="divider", type="Divider"), 65 dict(name='VAD', style="address"), 66 dict(name='lev', width=3, align="r"), 67 dict(name='start', style="address"), 68 dict(name='end', style="address"), 69 dict(name='com', width=6, align="r"), 70 dict(name='type', width=7), 71 dict(name='exe', width=6), 72 dict(name='protect', width=20), 73 dict(name='filename') 74 ] 75
76 - def __init__(self, *args, **kwargs):
77 super(VAD, self).__init__(*args, **kwargs) 78 self._cache = {}
79
80 - def column_types(self):
81 return dict( 82 _EPROCESS=self.session.profile._EPROCESS(), 83 divider=self.session.profile._EPROCESS(), 84 VAD=self.session.profile._MMVAD(), 85 lev=int, 86 start=self.session.profile.Pointer(), 87 end=self.session.profile.Pointer(), 88 com=int, 89 type=str, 90 exe=str, 91 protect=self.session.profile.Enumeration(), 92 filename=str)
93
94 - def find_file(self, addr):
95 """Finds the file mapped at this address.""" 96 for task in self.filter_processes(): 97 yield self.find_file_in_task(addr, task)
98
99 - def find_file_in_task(self, addr, task):
100 resolver = self.GetVadsForProcess(task) 101 if resolver: 102 _, _, data = resolver.get_containing_range(addr) 103 return data
104
105 - def _make_cache(self, task):
106 result = utils.RangedCollection() 107 self.session.report_progress( 108 " Enumerating VADs in %s (%s)", task.name, task.pid) 109 110 for vad in task.RealVadRoot.traverse(): 111 result.insert(vad.Start, vad.End, (self._get_filename(vad), vad)) 112 113 return result
114
115 - def GetVadsForProcess(self, task):
116 try: 117 resolver = self._cache[task] 118 except KeyError: 119 # Break the recursion by placing a None for the resolver. The 120 # self._make_cache() call will run the vad plugin which might 121 # resolve a VAD PTE, calling this code. This means that we can only 122 # use non-VAD PTEs to read the VAD itself. 123 self._cache[task] = None 124 resolver = self._cache[task] = self._make_cache(task) 125 126 return resolver
127
128 - def _get_filename(self, vad):
129 filename = "" 130 try: 131 file_obj = vad.ControlArea.FilePointer 132 if file_obj: 133 filename = file_obj.FileName or "Pagefile-backed section" 134 except AttributeError: 135 pass 136 137 return unicode(filename)
138
139 - def collect_vadroot(self, vad_root, task):
140 task_as = task.get_process_address_space() 141 result = [] 142 for vad in vad_root.traverse(): 143 # Apply filters if needed. 144 if self.plugin_args.regex and not re.search( 145 self.plugin_args.regex, self._get_filename(vad)): 146 continue 147 148 if (self.plugin_args.offset is not None and 149 not vad.Start <= self.plugin_args.offset <= vad.End): 150 continue 151 152 exe = "" 153 if "EXECUTE" in str(vad.u.VadFlags.ProtectionEnum): 154 exe = "Exe" 155 156 result.append(dict( 157 VAD=vad, 158 lev=vad.obj_context.get('depth', 0), 159 160 # The vad region itself exists in the process address space. 161 start=self.profile.Pointer(value=vad.Start, vm=task_as), 162 end=self.profile.Pointer(value=vad.End, vm=task_as), 163 com=vad.CommitCharge if vad.CommitCharge < 0x7fffffff else -1, 164 type="Private" if vad.u.VadFlags.PrivateMemory > 0 else "Mapped", 165 exe=exe, 166 protect=vad.u.VadFlags.ProtectionEnum, 167 filename=self._get_filename(vad))) 168 169 # Sort by start range. 170 result.sort(key=lambda x: x["start"].v()) 171 return result
172
173 - def collect(self):
174 for task in self.filter_processes(): 175 yield dict(_EPROCESS=task, divider=task) 176 177 for row in self.collect_vadroot(task.RealVadRoot, task): 178 row["_EPROCESS"] = task 179 yield row
180
181 182 -class VADMap(pfn.VADMapMixin, common.WinProcessFilter):
183 """Inspect each page in the VAD and report its status. 184 185 This allows us to see the address translation status of each page in the 186 VAD. 187 """ 188
189 - def _CreateMetadata(self, collection):
190 metadata = {} 191 for descriptor_cls, _, _ in collection.descriptors: 192 # Any of the following kinds of PTE are considered prototype. 193 if descriptor_cls in (pagefile.WindowsProtoTypePTEDescriptor, 194 pagefile.VadPteDescriptor): 195 metadata["ProtoType"] = True 196 break 197 198 for descriptor_cls, args, kwargs in reversed(collection.descriptors): 199 if issubclass(descriptor_cls, 200 pagefile.WindowsSubsectionPTEDescriptor): 201 metadata.update(descriptor_cls( 202 session=self.session, *args, **kwargs).metadata()) 203 204 return metadata 205 206 elif issubclass(descriptor_cls, pagefile.WindowsPTEDescriptor): 207 type = kwargs.get("pte_type") 208 if type == "Proto": 209 metadata["ProtoType"] = True 210 211 pte_value = kwargs.get("pte_value") 212 if pte_value & 1: 213 metadata["type"] = "Valid" 214 else: 215 metadata["type"] = "Transition" 216 217 return metadata 218 219 elif issubclass(descriptor_cls, pagefile.WindowsPagefileDescriptor): 220 metadata["number"] = kwargs.get("pagefile_number", 0) 221 metadata["offset"] = kwargs.get("address", 0) 222 metadata["type"] = "Pagefile" 223 224 return metadata 225 226 elif issubclass(descriptor_cls, pagefile.DemandZeroDescriptor): 227 metadata["type"] = "Demand Zero" 228 return metadata 229 230 elif issubclass(descriptor_cls, intel.PhysicalAddressDescriptor): 231 metadata["offset"] = kwargs["address"] 232 metadata.setdefault("type", "Valid") 233 234 elif issubclass(descriptor_cls, intel.InvalidAddress): 235 metadata["type"] = "Invalid" 236 237 return metadata
238
239 - def _GenerateVADRuns(self, vad):
240 address_space = self.session.GetParameter("default_address_space") 241 242 offset = vad.Start 243 end = min(vad.End, self.plugin_args.end) 244 245 while offset < end: 246 if self.plugin_args.start <= offset <= self.plugin_args.end: 247 yield offset, self._CreateMetadata( 248 address_space.describe_vtop(offset)) 249 250 self.session.report_progress("Inspecting 0x%08X", offset) 251 252 offset += 0x1000
253
254 - def GeneratePageMetatadata(self, task):
255 for module in self.session.address_resolver.modules(): 256 if isinstance(module, address_resolver.VadModule): 257 # Skip guard regions for Wow64. 258 if module.vad.CommitCharge >= 0x7fffffff: 259 continue 260 261 for vaddr, metadata in self._GenerateVADRuns(module.vad): 262 yield vaddr, metadata
263
264 265 -class VADDump(core.DirectoryDumperMixin, VAD):
266 """Dumps out the vad sections to a file""" 267 268 __name = "vaddump" 269 270 @classmethod
271 - def args(cls, parser):
272 super(VADDump, cls).args(parser) 273 parser.add_argument("--max_size", type="IntParser", 274 default=100*1024*1024, 275 help="Maximum file size to dump.")
276
277 - def __init__(self, *args, **kwargs):
278 self.max_size = kwargs.pop("max_size", 100*1024*1024) 279 super(VADDump, self).__init__(*args, **kwargs)
280
281 - def render(self, renderer):
282 for task in self.filter_processes(): 283 renderer.section("{0:6} ({1:2})".format( 284 task.name, task.UniqueProcessId)) 285 286 renderer.table_header([ 287 ("Start", "start", "[addrpad]"), 288 ("End", "end", "[addrpad]"), 289 ("Length", "length", "[addr]"), 290 ("Filename", "filename", "60s"), 291 ("Comment", "comment", "")]) 292 293 # Get the task and all process specific information 294 task_space = task.get_process_address_space() 295 296 name = task.ImageFileName 297 offset = task_space.vtop(task.obj_offset) 298 if offset is None: 299 renderer.format( 300 "Process does not have a valid address space.\n") 301 continue 302 303 with self.session.plugins.cc() as cc: 304 cc.SwitchProcessContext(task) 305 306 for vad in task.RealVadRoot.traverse(): 307 # Find the start and end range 308 start = vad.Start 309 end = vad.End 310 311 filename = "{0}.{1:x}.{2:08x}-{3:08x}.dmp".format( 312 name, offset, start, end) 313 314 with renderer.open(directory=self.dump_dir, 315 filename=filename, 316 mode='wb') as fd: 317 318 if end - start > self.max_size: 319 renderer.table_row(start, end, end-start, 320 "Skipped - Region too large") 321 continue 322 323 self.session.report_progress("Dumping %s" % filename) 324 325 self.CopyToFile(task_space, start, end + 1, fd) 326 renderer.table_row( 327 start, end, end-start, filename, 328 self._get_filename(vad))
329
330 331 -class VadScanner(scan.BaseScanner):
332 """A scanner over all memory regions of a process.""" 333
334 - def __init__(self, task=None, process_profile=None, **kwargs):
335 """Scan the process address space through the Vads. 336 337 Args: 338 task: The _EPROCESS object for this task. 339 340 process_profile: The specialized profile for this process. In practice 341 this is always different from task.obj_profile (which belongs to the 342 kernel). If not provided we default to the kernel profile. 343 """ 344 self.task = task 345 super(VadScanner, self).__init__( 346 profile=process_profile or task.obj_profile, 347 address_space=task.get_process_address_space(), 348 **kwargs)
349
350 - def scan(self, offset=0, maxlen=None):
351 maxlen = maxlen or self.profile.get_constant("MaxPointer") 352 353 for vad in self.task.RealVadRoot.traverse(): 354 # Only scan the VAD region. 355 for match in super(VadScanner, self).scan(vad.Start, vad.Length): 356 yield match
357