Package rekall :: Package plugins :: Package overlays :: Package linux :: Module dwarfparser
[frames] | no frames]

Source Code for Module rekall.plugins.overlays.linux.dwarfparser

  1  # Rekall Memory Forensics 
  2  # Copyright (c) 2012 Michael Cohen <scudette@gmail.com> 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or (at 
  8  # your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  # General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 18  # 
 19   
 20  """A parser for dwarf modules which generates vtypes.""" 
 21  import logging 
 22   
 23  from elftools import construct 
 24  from elftools.dwarf import callframe 
 25  from elftools.dwarf import compileunit 
 26  from elftools.dwarf import descriptions 
 27  from elftools.dwarf import dwarfinfo 
 28  from elftools.dwarf import enums 
 29  from elftools.dwarf import structs as dwarf_structs 
 30  from elftools.elf import elffile 
 31  from elftools.common.py3compat import itervalues 
 32  from elftools.dwarf.descriptions import describe_attr_value 
 33   
 34  from rekall import plugin 
 35  from rekall_lib import utils 
36 37 38 39 -def PatchPyElftools():
40 """Upgrade pyelftools to support DWARF 4. 41 42 Hopefully these fixes will be pushed upstream soon. 43 """ 44 # Check if pyelftools already supports dwarf 4. 45 if "DW_FORM_sec_offset" not in enums.ENUM_DW_FORM: 46 enums.ENUM_DW_FORM.update(dict( 47 DW_FORM_sec_offset=0x17, 48 DW_FORM_exprloc=0x18, 49 DW_FORM_flag_present=0x19, 50 DW_FORM_ref_sig8=0x20, 51 )) 52 53 class Implicit(object): 54 def parse_stream(self, _): 55 """This form consumes no data.""" 56 return True
57 58 class DWARFStructs(dwarf_structs.DWARFStructs): 59 def _create_dw_form(self): 60 super(DWARFStructs, self)._create_dw_form() 61 62 self.Dwarf_dw_form.update(dict( 63 DW_FORM_sec_offset=self.Dwarf_offset(''), 64 DW_FORM_exprloc=construct.PrefixedArray( 65 subcon=self.Dwarf_uint8('elem'), 66 length_field=self.Dwarf_uleb128('')), 67 DW_FORM_flag_present=Implicit(), 68 DW_FORM_ref_sig8=self.Dwarf_uint64(''), 69 )) 70 71 dwarf_structs.DWARFStructs = DWARFStructs 72 dwarfinfo.DWARFStructs = DWARFStructs 73 callframe.DWARFStructs = DWARFStructs 74 75 class DWARFInfo(dwarfinfo.DWARFInfo): 76 def _is_supported_version(self, version): 77 return 2 <= version <= 4 78 79 dwarfinfo.DWARFInfo = DWARFInfo 80 elffile.DWARFInfo = DWARFInfo 81 82 class CompileUnit(compileunit.CompileUnit): 83 def iter_DIEs(self): 84 try: 85 self._parse_DIEs() 86 except IndexError: 87 pass 88 89 return iter(self._dielist) 90 91 compileunit.CompileUnit = CompileUnit 92 dwarfinfo.CompileUnit = CompileUnit 93 94 95 # Ensure Pyelftools is patched to support DWARF 4. 96 PatchPyElftools()
97 98 99 -class DIETag(object):
100 # The parent container for this DIE. 101 parent = None 102
103 - def __init__(self, die, types, parents):
104 self.attributes = {} 105 self.die = die 106 self.types = types 107 if parents: 108 self.parent = parents[-1] 109 110 for attr in die.attributes.values(): 111 self.attributes[attr.name] = attr
112 113 @utils.safe_property
114 - def name(self):
115 # This node is directly named. 116 if "DW_AT_name" in self.attributes: 117 return self.attributes["DW_AT_name"].value 118 119 if "DW_AT_sibling" in self.attributes: 120 sibling = self.types.get(self.attributes["DW_AT_sibling"].value + 121 self.die.cu.cu_offset) 122 if sibling and sibling.die.tag == "DW_TAG_typedef": 123 return sibling.name 124 125 return "__unnamed_%s" % self.die.offset
126 127 @utils.safe_property
128 - def type_id(self):
129 if "DW_AT_type" in self.attributes: 130 return self.attributes["DW_AT_type"].value + self.die.cu.cu_offset
131
132 - def VType(self):
133 """Returns a vtype representation of this DIE.""" 134 return self.name
135
136 - def Definition(self, vtype):
137 """This DW element is given an opportunity to generate a vtype."""
138
139 -class DW_TAG_typedef(DIETag):
140
141 - def VType(self):
142 """For typedefs we just substitute our base type.""" 143 if self.type_id is None: 144 return self.name 145 146 return self.types[self.type_id].VType()
147
148 149 -class DW_TAG_volatile_type(DW_TAG_typedef):
150 pass
151
152 153 -class DW_TAG_base_type(DIETag):
154 """A base type."""
155
156 157 -class DW_TAG_structure_type(DIETag):
158 """A struct definition."""
159 - def __init__(self, die, types, parents):
160 super(DW_TAG_structure_type, self).__init__(die, types, parents) 161 # All the members of this struct. 162 self.members = []
163 164 @utils.safe_property
165 - def name(self):
166 if "DW_AT_name" in self.attributes: 167 return self.attributes["DW_AT_name"].value 168 else: 169 return "__unnamed_%s" % self.die.offset
170 171 @utils.safe_property
172 - def size(self):
173 try: 174 return self.attributes['DW_AT_byte_size'].value 175 except KeyError: 176 pass
177
178 - def Definition(self, vtype):
179 # Forward declerations are not interesting. 180 if "DW_AT_declaration" in self.attributes: 181 return 182 183 if self.name in vtype and vtype[self.name][0] != self.size: 184 self.session.logging.warning( 185 "Structs of different sizes but same name") 186 187 count = 1 188 result = [self.size, {}] 189 190 for member in self.members: 191 if isinstance(member, DW_TAG_member): 192 name = member.name 193 194 # Make nicer names for annonymous things (usually unions). 195 if name.startswith("__unnamed_"): 196 name = "u%s" % count 197 count += 1 198 199 result[1][name] = member.VType() 200 201 # Only emit structs with actual members in them. 202 if result[1]: 203 vtype[self.name] = result
204
205 206 -class DW_TAG_union_type(DW_TAG_structure_type):
207 @utils.safe_property
208 - def name(self):
209 if "DW_AT_name" in self.attributes: 210 return self.attributes["DW_AT_name"].value 211 212 if "DW_AT_sibling" in self.attributes: 213 sibling = self.types.get(self.attributes["DW_AT_sibling"].value + 214 self.die.cu.cu_offset) 215 if sibling and sibling.die.tag == "DW_TAG_typedef": 216 return sibling.name 217 218 return "__unnamed_%s" % self.die.offset
219
220 221 -class DW_TAG_pointer_type(DIETag):
222 - def VType(self):
223 if 'DW_AT_type' in self.attributes: 224 target = self.types[self.type_id] 225 target_type = target.VType() 226 if not isinstance(target_type, list): 227 target_type = [target_type, None] 228 229 return ['Pointer', dict(target=target_type[0], 230 target_args=target_type[1])] 231 232 return ['Pointer', dict(target="Void")]
233
234 235 -class DW_TAG_subroutine_type(DIETag):
236 - def VType(self):
237 return "void"
238
239 240 -class DW_TAG_array_type(DIETag):
241 count = 0 242
243 - def VType(self):
244 if 'DW_AT_type' in self.attributes: 245 target_type = self.types[self.type_id].VType() 246 if not isinstance(target_type, list): 247 target_type = [target_type, None] 248 return ['Array', dict(target=target_type[0], 249 target_args=target_type[1], 250 count=self.count)] 251 252 return ['Array', dict(count=self.count)]
253
254 255 -class DW_TAG_subrange_type(DIETag):
256 """These specify the count of arrays."""
257 - def __init__(self, die, types, parents):
258 super(DW_TAG_subrange_type, self).__init__(die, types, parents) 259 260 if "DW_AT_upper_bound" in self.attributes: 261 self.parent.count = self.attributes['DW_AT_upper_bound'].value + 1
262 263 264 _DWARF_EXPR_DUMPER_CACHE = {}
265 -def describe_DWARF_expr(expr, structs):
266 """ Textual description of a DWARF expression encoded in 'expr'. 267 structs should come from the entity encompassing the expression - it's 268 needed to be able to parse it correctly. 269 """ 270 # Since this function can be called a lot, initializing a fresh new 271 # ExprDumper per call is expensive. So a rudimentary caching scheme is in 272 # place to create only one such dumper per instance of structs. 273 cache_key = id(structs) 274 if cache_key not in _DWARF_EXPR_DUMPER_CACHE: 275 _DWARF_EXPR_DUMPER_CACHE[cache_key] = \ 276 descriptions.ExprDumper(structs) 277 dwarf_expr_dumper = _DWARF_EXPR_DUMPER_CACHE[cache_key] 278 dwarf_expr_dumper.clear() 279 dwarf_expr_dumper.process_expr(expr) 280 return dwarf_expr_dumper.get_str().split(":")
281
282 283 -class DW_TAG_member(DIETag):
284 offset = 0 285 type = None 286
287 - def __init__(self, die, types, parents):
288 super(DW_TAG_member, self).__init__(die, types, parents) 289 290 # Add ourselves to our parent struct. 291 self.parent.members.append(self) 292 293 if 'DW_AT_data_member_location' in self.attributes: 294 value = self.attributes['DW_AT_data_member_location'].value 295 if isinstance(value, (int, long)): 296 self.offset = value 297 else: 298 op_code, value = describe_DWARF_expr(value, die.cu.structs) 299 300 if op_code == "DW_OP_plus_uconst": 301 self.offset = int(value)
302
303 - def delegate(self):
304 """A member is just a place holder for another type in the struct. 305 306 Thie method returns the delegate. 307 """ 308 return self.types[self.type_id]
309
310 - def VType(self):
311 member_type = self.delegate().VType() 312 313 # Does this member represent a bitfield? 314 if "DW_AT_bit_size" in self.attributes: 315 # The dwarf standard says: 316 317 # For a DW_AT_data_bit_offset attribute, the value is an integer 318 # constant (see Section 2.19) that specifies the number of bits from 319 # the beginning of the containing entity to the beginning of the 320 # data member. 321 322 # This means that for little endian we need to swap them with the 323 # size of the integer. 324 full_size = self.attributes['DW_AT_byte_size'].value * 8 325 start_bit = self.attributes['DW_AT_bit_offset'].value 326 end_bit = self.attributes['DW_AT_bit_size'].value + start_bit 327 328 converted_start_bit = full_size - end_bit 329 converted_end_bit = full_size - start_bit 330 331 # Sometimes the member is an Enumeration. In that case we need to 332 # return something slightly different. 333 if member_type[0] == "Enumeration": 334 member_type[1]["target"] = "BitField" 335 member_type[1]["target_args"] = { 336 'start_bit': converted_start_bit, 337 'end_bit': converted_end_bit 338 } 339 return [self.offset, member_type] 340 341 return [self.offset, ['BitField', {'start_bit': converted_start_bit, 342 'target': member_type, 343 'end_bit': converted_end_bit}]] 344 345 if not isinstance(member_type, list): 346 member_type = [member_type] 347 348 return [self.offset, member_type]
349
350 351 -class DW_TAG_enumeration_type(DIETag):
352 """Holds enumerations.""" 353 354 byte_size_lookup = {4: "long", 355 2: "short int", 356 1: "char"} 357
358 - def __init__(self, die, types, parents):
359 super(DW_TAG_enumeration_type, self).__init__(die, types, parents) 360 self.enumerations = {} 361 self.reverse_enumerations = {}
362
363 - def VType(self):
364 byte_size = self.attributes['DW_AT_byte_size'].value 365 return ['Enumeration', {'enum_name': self.name, 366 'target': self.byte_size_lookup[byte_size]}]
367
368 - def Definition(self, vtype):
369 """Enumerations go into the $ENUMS vtype area.""" 370 vtype.setdefault("$ENUMS", {})[self.name] = self.enumerations 371 vtype.setdefault("$REVENUMS", {})[self.name] = self.reverse_enumerations
372
373 374 -class DW_TAG_enumerator(DIETag):
375 """An enumeration.""" 376
377 - def __init__(self, die, types, parents):
378 super(DW_TAG_enumerator, self).__init__(die, types, parents) 379 380 # Add ourselves to our parent container. 381 value = self.attributes['DW_AT_const_value'].value 382 self.parent.enumerations[value] = self.name 383 self.parent.reverse_enumerations[self.name] = value
384 385 386 # A lookup table of the different tag handlers. 387 DIE_LOOKUP = { 388 "DW_TAG_base_type": DW_TAG_base_type, 389 "DW_TAG_structure_type": DW_TAG_structure_type, 390 "DW_TAG_union_type": DW_TAG_union_type, 391 "DW_TAG_member": DW_TAG_member, 392 393 # typedefs and const types are just proxies to native types. 394 "DW_TAG_typedef": DW_TAG_typedef, 395 "DW_TAG_const_type": DW_TAG_typedef, 396 397 # Enumrations. 398 "DW_TAG_enumeration_type": DW_TAG_enumeration_type, 399 "DW_TAG_enumerator": DW_TAG_enumerator, 400 401 "DW_TAG_pointer_type": DW_TAG_pointer_type, 402 "DW_TAG_array_type": DW_TAG_array_type, 403 "DW_TAG_subrange_type": DW_TAG_subrange_type, 404 "DW_TAG_subroutine_type": DW_TAG_subroutine_type, 405 "DW_TAG_volatile_type": DW_TAG_volatile_type, 406 }
407 408 -def DIEFactory(die, types, parents):
409 """Returns an instance of the DIE object.""" 410 if die.tag in DIE_LOOKUP: 411 return DIE_LOOKUP[die.tag](die, types, parents) 412 413 return DIETag(die, types, parents)
414
415 416 -class DWARFParser(object):
417 """A parser for DWARF files.""" 418
419 - def __init__(self, fd, session):
420 self.session = session 421 self.elffile = elffile.ELFFile(fd) 422 self.types = {} 423 424 if self.elffile.has_dwarf_info(): 425 self._dwarfinfo = self.elffile.get_dwarf_info() 426 else: 427 raise RuntimeError("File does not have DWARF information - " 428 "was it compiled with debugging information?") 429 self.logging = session.logging.getChild("linux.dwarf") 430 self.logging.setLevel(logging.ERROR) 431 self.compile()
432
433 - def compile(self):
434 """Compile the vtypes from the dwarf information.""" 435 # We currently dump all compilation units into the same 436 # vtype. Technically the same symbol can be defined differently in 437 # different compilation units, but rekall does not have CU 438 # resolution right now so we assume they are all the same. 439 parents = [] 440 section_offset = self._dwarfinfo.debug_info_sec.global_offset 441 for cu in self._dwarfinfo.iter_CUs(): 442 parents.append(cu) 443 444 die_depth = 0 445 for die in cu.iter_DIEs(): 446 self.logging.debug('%d %s<%x>: %s' % ( 447 die_depth, 448 "\t" * die_depth, 449 die.offset, 450 ('%s' % die.tag) if not die.is_null() else '')) 451 if die.is_null(): 452 die_depth -= 1 453 parents = parents[:-1] 454 continue 455 456 for attr in itervalues(die.attributes): 457 name = attr.name 458 # Unknown attribute values are passed-through as integers 459 if isinstance(name, int): 460 name = 'Unknown AT value: %x' % name 461 462 if self.logging.isEnabledFor(logging.DEBUG): 463 try: 464 self.logging.debug('%d %s <%2x> %-18s: %s' % ( 465 die_depth, 466 "\t" * die_depth, 467 attr.offset, 468 name, 469 describe_attr_value( 470 attr, die, section_offset))) 471 except Exception: 472 pass 473 474 # Record the type in this DIE. 475 t = self.types[die.offset] = DIEFactory( 476 die, self.types, parents) 477 478 if die.has_children: 479 parents.append(t) 480 die_depth += 1
481 482
483 - def VType(self):
484 """Build a vtype for this module's dwarf information.""" 485 result = {} 486 for type in self.types.values(): 487 self.session.report_progress("Extracting type %s", type.name) 488 # Only structs emit definitions basically. 489 type.Definition(result) 490 491 return result
492
493 494 -class DwarfParser(plugin.TypedProfileCommand, plugin.Command):
495 """Parse the dwarf file and dump a vtype structure from it.""" 496 __name = "dwarfparser" 497 498 __args = [ 499 dict(name="dwarf_filename", positional=True, required=True, 500 help="The filename of the PDB file."), 501 502 dict(name="profile_class", default="Linux64", 503 help="The name of the profile implementation. "), 504 ] 505
506 - def __init__(self, *args, **kwargs):
507 super(DwarfParser, self).__init__(*args, **kwargs) 508 509 self.parser = DWARFParser( 510 open(self.plugin_args.dwarf_filename, "rb"), self.session)
511
512 - def render(self, renderer):
513 vtypes = self.parser.VType() 514 result = { 515 "$METADATA": dict( 516 Type="Profile", 517 518 # This should probably be changed for something more specific. 519 ProfileClass=self.plugin_args.profile_class), 520 "$STRUCTS": vtypes, 521 "$ENUMS": vtypes.pop("$ENUMS", {}), 522 "$REVENUMS": vtypes.pop("$REVENUMS", {}), 523 } 524 525 renderer.write(utils.PPrint(result))
526