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

Source Code for Module rekall.plugins.windows.address_resolver

  1  # Rekall Memory Forensics 
  2  # Copyright 2014 Google Inc. All Rights Reserved. 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published by 
  6  # the Free Software Foundation; either version 2 of the License, or (at 
  7  # your option) any later version. 
  8  # 
  9  # This program is distributed in the hope that it will be useful, but 
 10  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 12  # General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with this program; if not, write to the Free Software 
 16  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 17  # 
 18   
 19  """The module implements the windows specific address resolution plugin.""" 
 20   
 21  __author__ = "Michael Cohen <scudette@gmail.com>" 
 22  import re 
 23   
 24  from rekall import addrspace 
 25  from rekall import config 
 26  from rekall import obj 
 27  from rekall import plugin 
 28  from rekall import testlib 
 29  from rekall.plugins.common import address_resolver 
 30  from rekall.plugins.windows import common 
 31  from rekall.plugins.overlays.windows import pe_vtypes 
 32  from rekall_lib import utils 
 33   
 34   
 35  config.DeclareOption( 
 36      "autodetect_build_local_tracked", 
 37      group="Autodetection Overrides", 
 38      default=["nt", "win32k", "tcpip", "ntdll"], 
 39      type="ArrayStringParser", 
 40      help="When autodetect_build_local is set to 'basic' we fetch these " 
 41      "modules directly from the symbol server.") 
42 43 44 # In windows there are two types of mapped PE files - kernel modules and Dlls. 45 46 -class PEModule(address_resolver.Module):
47 """Windows overlays PE files in memory.""" 48 49 _profile = None 50
52 """Get the module guid from the session cache. 53 54 This allows the user to override the GUID detection with their own. 55 """ 56 return self.session.GetParameter("%s_profile" % self.name)
57
59 """Guess the guid for the PE file.""" 60 # Try to load the file from the physical address space. 61 if self.session.physical_address_space.metadata("can_map_files"): 62 phys_as = self.session.physical_address_space 63 if self.filename: 64 image_offset = phys_as.get_mapped_offset(self.filename, 0) 65 if image_offset: 66 try: 67 file_as = addrspace.RunBasedAddressSpace( 68 base=phys_as, session=self.session) 69 70 file_as.add_run(0, image_offset, 2**63) 71 72 pe_file_as = pe_vtypes.PEFileAddressSpace( 73 base=file_as, session=self.session) 74 75 pe_helper = pe_vtypes.PE( 76 address_space=pe_file_as, 77 image_base=pe_file_as.image_base, 78 session=self.session) 79 80 return pe_helper.RSDS.GUID_AGE 81 except IOError: 82 pass
83
84 - def detect_guid_pe_header(self):
85 # Overlay on the virtual AS. 86 pe_helper = pe_vtypes.PE( 87 address_space=self.session.GetParameter("default_address_space"), 88 image_base=self.start, session=self.session) 89 90 return pe_helper.RSDS.GUID_AGE
91
93 index = self.session.LoadProfile("%s/index" % self.name) 94 for profile_name, _ in index.LookupIndex(self.start): 95 return profile_name
96
97 - def detect_profile_name(self):
98 """Try to figure out the profile name for this module. 99 100 We have a number of methods as we need to call these in the most 101 appropriate order. 102 """ 103 # Firt check the session - this allows the user to specify the profile 104 # directly by storing e.g. win32_profile in the session. 105 profile = self.detect_profile_from_session() 106 if profile: 107 return profile 108 109 # We might have the original file, e.g. in the AFF4 image. 110 guid = (self.detect_guid_from_mapped_file() or 111 112 # Maybe we can just read the RSDS value from the mapped pe 113 # header. 114 self.detect_guid_pe_header()) 115 116 if guid: 117 return "%s/GUID/%s" % (self.name, guid) 118 119 # Finally try to apply the index if available. 120 return self.detect_profile_from_index()
121
122 - def build_local_profile(self, profile_name=None, force=False):
123 """Fetch and build a local profile from the symbol server.""" 124 if profile_name is None: 125 profile_name = self.detect_profile_name() 126 127 mode = self.session.GetParameter("autodetect_build_local") 128 if force or mode == "full" or ( 129 mode == "basic" and 130 self.name in self.session.GetParameter( 131 "autodetect_build_local_tracked")): 132 build_local_profile = self.session.plugins.build_local_profile() 133 try: 134 self.session.logging.debug("Will build local profile %s", 135 profile_name) 136 build_local_profile.fetch_and_parse(profile_name) 137 return self.session.LoadProfile(profile_name, use_cache=False) 138 except IOError: 139 pass 140 141 return obj.NoneObject()
142
144 """Create a dummy profile from PE exports.""" 145 # Building from export table is slow and might not be needed if the user 146 # wants speed. 147 if self.session.GetParameter("performance") == "fast": 148 return obj.NoneObject() 149 150 self.session.logging.debug("Building profile from PE Exports for %s", 151 self.name) 152 result = obj.Profile.classes["BasicPEProfile"]( 153 name=self.name, 154 session=self.session) 155 156 result.image_base = self.start 157 158 peinfo = self.session.plugins.peinfo( 159 image_base=self.start, address_space=self.session.GetParameter( 160 "default_address_space")) 161 162 constants = {} 163 if "Export" in self.session.GetParameter("name_resolution_strategies"): 164 for _, func, name, _ in peinfo.pe_helper.ExportDirectory(): 165 self.session.report_progress("Merging export table: %s", name) 166 func_offset = func.v() 167 if not result.get_constant_by_address(func_offset): 168 constants[str(name or "")] = func_offset - self.start 169 170 result.add_constants(constants_are_addresses=True, constants=constants) 171 return result
172
173 - def reset(self):
174 self._profile = None
175 176 @utils.safe_property
177 - def profile(self):
178 if self._profile: 179 return self._profile 180 181 return self.load_profile(force=False)
182
183 - def load_profile(self, force=True):
184 profile_name = self.detect_profile_name() 185 if profile_name: 186 self._profile = ( 187 self.session.LoadProfile(profile_name) or 188 self.build_local_profile(profile_name, force=force)) 189 190 if not self._profile: 191 # Profile is not available, should we build it? 192 self._profile = self.build_profile_from_exports() 193 194 if not self._profile: 195 return obj.NoneObject("Unable to detect GUID") 196 197 self._profile.image_base = self.start 198 return self._profile
199 200 @profile.setter
201 - def profile(self, value):
202 """Allow the profile for this module to be overridden.""" 203 self._profile = value 204 if value: 205 self._profile.image_base = self.start
206
207 208 -class VadModule(PEModule):
209 """A Module corresponding to a VAD entry.""" 210 filename = None 211
212 - def __init__(self, vad=None, session=None):
213 name = "vad_%#x" % vad.Start 214 try: 215 # The filename of the _MMVAD 216 self.file_obj = vad.ControlArea.FilePointer 217 if self.file_obj.v(): 218 self.filename = self.file_obj.file_name_with_drive() 219 name = WindowsAddressResolver.NormalizeModuleName(self.filename) 220 except AttributeError: 221 # Anonymous module has no name but can be enumerated via 222 # address_resolver.modules(). 223 self.file_obj = self.filename = None 224 225 self.vad = vad 226 super(VadModule, self).__init__( 227 name=name, 228 start=vad.Start, 229 end=vad.End, 230 session=session)
231
232 233 -class KernelModule(PEModule):
234 """A Windows kernel module."""
235 - def __init__(self, ldr_module=None, session=None):
236 self.ldr_module = ldr_module 237 self.filename = ldr_module.filename or ldr_module.name 238 name = WindowsAddressResolver.NormalizeModuleName(self.filename) 239 super(KernelModule, self).__init__( 240 name=name, 241 start=ldr_module.base, 242 end=ldr_module.end, 243 session=session)
244
245 246 -class WindowsAddressResolver(address_resolver.AddressResolverMixin, 247 common.WindowsCommandPlugin):
248 """A windows specific address resolver plugin.""" 249 250 @classmethod
251 - def args(cls, parser):
252 parser.add_argument( 253 "download_profile", default=False, 254 help="Try to download the profile for this module from the " 255 "symbol server.")
256
257 - def __init__(self, download_profile=None, **kwargs):
258 super(WindowsAddressResolver, self).__init__(**kwargs) 259 self.download_profile = download_profile
260
261 - def render(self, _):
262 if self.download_profile: 263 self.session.address_resolver.GetModuleByName( 264 self.download_profile).load_profile(force=True)
265 266 @staticmethod
267 - def NormalizeModuleName(module_name):
268 result = unicode(module_name) 269 result = re.split(r"[/\\]", result)[-1] 270 271 # Drop the file extension. 272 result = result.split(".")[0] 273 274 # The kernel is treated specially - just like windbg. 275 if result in ["ntoskrnl", "ntkrnlpa", "ntkrnlmp"]: 276 result = u"nt" 277 278 return result.lower()
279
280 - def track_modules(self, *modules):
281 """Add module names to the tracked list.""" 282 already_tracked = self.session.GetParameter( 283 'autodetect_build_local_tracked') or [] 284 285 needed = set(modules) 286 if not needed.issubset(already_tracked): 287 needed.update(already_tracked) 288 with self.session as session: 289 session.SetParameter("autodetect_build_local_tracked", needed) 290 for module_name in modules: 291 module_obj = self.GetModuleByName(module_name) 292 if module_obj: 293 # Clear the module's profile. This will force it to 294 # reload a new profile. 295 module_obj.profile = None
296 297
298 - def _EnsureInitialized(self):
299 """Initialize the address resolver. 300 301 In windows we populate the virtual address space map from kernel modules 302 and VAD mapped files (dlls). 303 """ 304 if self._initialized: 305 return 306 307 try: 308 # First populate with kernel modules. 309 for ldr_entry in self.session.plugins.modules().lsmod(): 310 self.AddModule( 311 KernelModule(ldr_module=ldr_entry, session=self.session)) 312 313 # Windows 10 does not have the kernel in the modules list. 314 if not self._modules_by_name.get("nt"): 315 start = obj.Pointer.integer_to_address( 316 self.session.GetParameter("kernel_base")) 317 pe = pe_vtypes.PE(image_base=start, session=self.session) 318 end = start + pe.nt_header.OptionalHeader.SizeOfImage 319 self.AddModule( 320 PEModule( 321 session=self.session, 322 name="nt", 323 start=start, 324 end=end, 325 profile=self.session.profile) 326 ) 327 328 # Now use the vad. 329 process_context = self.session.GetParameter("process_context") 330 if process_context != None: 331 for vad in process_context.RealVadRoot.traverse(): 332 self.AddModule(VadModule(vad=vad, session=self.session)) 333 334 finally: 335 self._initialized = True
336
337 338 -class PECommandPlugin(plugin.KernelASMixin, plugin.PhysicalASMixin, 339 plugin.TypedProfileCommand, 340 plugin.ProfileCommand):
341 """A command that is active when inspecting a PE file.""" 342 __abstract = True 343 344 @classmethod
345 - def is_active(cls, session):
346 """We are only active if the profile is windows.""" 347 return (super(PECommandPlugin, cls).is_active(session) and 348 session.profile.name == 'pe')
349
350 351 -class PESectionModule(address_resolver.Module):
352 """A section in a PE file."""
353
354 355 -class PEAddressResolver(address_resolver.AddressResolverMixin, 356 PECommandPlugin):
357 """A simple address resolver for PE files.""" 358
359 - def __init__(self, *args, **kwargs):
360 super(PEAddressResolver, self).__init__(*args, **kwargs) 361 self.image_base = self.kernel_address_space.image_base 362 self.pe_helper = pe_vtypes.PE( 363 address_space=self.session.kernel_address_space, 364 image_base=self.image_base, 365 session=self.session)
366 367 @staticmethod
368 - def NormalizeModuleName(module_name):
369 result = unicode(module_name) 370 result = re.split(r"[/\\]", result)[-1] 371 372 # The kernel is treated specially - just like windbg. 373 if result in ["ntoskrnl.pdb", "ntkrnlpa.pdb", "ntkrnlmp.pdb"]: 374 result = u"nt" 375 376 return result
377
378 - def _ParseAddress(self, name):
379 capture = super(PEAddressResolver, self)._ParseAddress(name) 380 if capture["module"] != "header" and not capture["symbol"]: 381 capture["symbol"] = capture["module"] 382 capture["module"] = "header" 383 384 return capture
385
386 - def _EnsureInitialized(self):
387 if self._initialized: 388 return 389 390 symbols = {} 391 self.pe_profile = None 392 393 # Get a usable profile. 394 if "Symbol" in self.session.GetParameter("name_resolution_strategies"): 395 # Load the profile for this binary. 396 self.pe_profile = self.session.LoadProfile("%s/GUID/%s" % ( 397 self.NormalizeModuleName(self.pe_helper.RSDS.Filename), 398 self.pe_helper.RSDS.GUID_AGE)) 399 400 if self.pe_profile == None: 401 self.pe_profile = pe_vtypes.BasicPEProfile(session=self.session) 402 403 if "Export" in self.session.GetParameter("name_resolution_strategies"): 404 # Extract all exported symbols into the profile's symbol table. 405 for _, func, name, _ in self.pe_helper.ExportDirectory(): 406 func_address = func.v() 407 name = utils.SmartUnicode(name) 408 symbols[name] = func_address - self.image_base 409 410 self.pe_profile.image_base = self.image_base 411 self.pe_profile.add_constants(constants_are_addresses=True, 412 constants=symbols) 413 414 # A section for the header. 415 self.AddModule( 416 PESectionModule(start=self.image_base, 417 end=self.image_base+0x1000, 418 name="pe", 419 profile=self.pe_profile, 420 session=self.session)) 421 422 # Find the highest address covered in this executable image. 423 for _, name, virtual_address, length in self.pe_helper.Sections(): 424 if length > 0: 425 virtual_address += self.image_base 426 self.AddModule( 427 PESectionModule(start=virtual_address, 428 end=virtual_address + length, 429 name=self.NormalizeModuleName(name), 430 profile=self.pe_profile, 431 session=self.session)) 432 433 self._initialized = True
434
435 - def search_symbol(self, pattern):
436 if "!" not in pattern: 437 pattern = "pe!" + pattern 438 return super(PEAddressResolver, self).search_symbol(pattern)
439
440 - def __str__(self):
441 self._EnsureInitialized() 442 return "<%s: %s@%#x>" % (self.__class__.__name__, self.pe_profile.name, 443 self.image_base)
444
445 446 -class TestWindowsAddressResolver(testlib.DisabledTest):
447 PLUGIN = "address_resolver"
448