Package rekall :: Package plugins :: Package addrspaces :: Module elfcore
[frames] | no frames]

Source Code for Module rekall.plugins.addrspaces.elfcore

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2012 Philippe Teuwen, Thorsten Sick, Michael Cohen 
  4  # Copyright 2013 Google Inc. All Rights Reserved. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or (at 
  9  # your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 19  # 
 20   
 21  """An Address Space for processing ELF64 coredumps.""" 
 22  # References: 
 23  # VirtualBox core format: 
 24  # http://www.virtualbox.org/manual/ch12.html#guestcoreformat 
 25  # ELF64 format: http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf 
 26   
 27  # Note that as of version 1.6.0 WinPmem also uses ELF64 as the default imaging 
 28  # format. Except that WinPmem stores image metadata in a YAML file stored in the 
 29  # image. This address space supports both formats. 
 30  import os 
 31  import yaml 
 32   
 33  from rekall import addrspace 
 34  from rekall import constants 
 35  from rekall.plugins.addrspaces import standard 
 36  from rekall.plugins.overlays.linux import elf 
 37   
 38  PT_PMEM_METADATA = 0x6d656d70  # Spells 'pmem' 
 39   
 40   
 41   
42 -class Elf64CoreDump(addrspace.RunBasedAddressSpace):
43 """This AS supports ELF64 coredump format, as used by VirtualBox.""" 44 order = 30 45 46 __name = "elf64" 47 __image = True 48
49 - def __init__(self, **kwargs):
50 super(Elf64CoreDump, self).__init__(**kwargs) 51 52 # Check the file for sanity. 53 self.check_file() 54 55 self.offset = 0 56 self.fname = '' 57 self._metadata = {} 58 59 # Now parse the ELF file. 60 elf_profile = elf.ELFProfile(session=self.session) 61 self.elf64_hdr = elf_profile.elf64_hdr(vm=self.base, offset=0) 62 63 self.as_assert(self.elf64_hdr.e_type == "ET_CORE", 64 "Elf file is not a core file.") 65 self.name = "%s|%s" % (self.__class__.__name__, self.base.name) 66 67 # Iterate over all the program headers and map the runs. 68 for segment in self.elf64_hdr.e_phoff: 69 if segment.p_type == "PT_LOAD": 70 # Some load segments are empty. 71 if (segment.p_filesz == 0 or 72 segment.p_filesz != segment.p_memsz): 73 continue 74 75 # Add the run to the memory map. 76 virtual_address = int(segment.p_paddr) or int(segment.p_vaddr) 77 self.add_run(virtual_address, # Virtual Addr 78 int(segment.p_offset), # File Addr 79 int(segment.p_memsz)) # Run end. 80 81 elif segment.p_type == PT_PMEM_METADATA: 82 self.LoadMetadata(segment.p_offset) 83 84 # NOTE: Deprecated! This will be removed soon. If you want to store 85 # metadata use AFF4 format. 86 87 # Search for the pmem footer signature. 88 footer = self.base.read(self.base.end() - 10000, 10000) 89 if "...\n" in footer[-6:]: 90 header_offset = footer.rfind("# PMEM") 91 if header_offset > 0: 92 self.LoadMetadata(self.base.end() - 10000 + header_offset)
93
94 - def check_file(self):
95 """Checks the base file handle for sanity.""" 96 97 self.as_assert(self.base, 98 "Must stack on another address space") 99 100 ## Must start with the magic for elf 101 self.as_assert((self.base.read(0, 4) == "\177ELF"), 102 "Header signature invalid")
103
104 - def LoadMetadata(self, offset):
105 """Load the WinPmem metadata from the elf file.""" 106 self.session.logging.error( 107 "DEPRECATED Elf metadata found! " 108 "This will not be supported in the next release.") 109 try: 110 data = self.base.read(offset, 1024*1024) 111 yaml_file = data.split('...\n')[0] 112 113 metadata = yaml.safe_load(yaml_file) 114 except (yaml.YAMLError, TypeError) as e: 115 self.session.logging.error( 116 "Invalid file metadata, skipping: %s" % e) 117 return 118 119 for session_param, metadata_key in (("dtb", "CR3"), 120 ("kernel_base", "KernBase")): 121 if metadata_key in metadata: 122 self.session.SetParameter( 123 session_param, metadata[metadata_key]) 124 125 previous_section = metadata.pop("PreviousHeader", None) 126 if previous_section is not None: 127 self.LoadMetadata(previous_section) 128 129 pagefile_offset = metadata.get("PagefileOffset", None) 130 pagefile_size = metadata.get("PagefileSize", None) 131 132 if pagefile_offset is not None and pagefile_size is not None: 133 self.LoadPageFile(pagefile_offset, pagefile_size) 134 135 self._metadata.update(metadata)
136 137 pagefile_offset = 0 138 pagefile_end = 0 139
140 - def LoadPageFile(self, pagefile_offset, pagefile_size):
141 """We map the page file into the physical address space. 142 143 This allows us to treat all physical addresses equally - regardless if 144 they come from the memory or the page file. 145 """ 146 # Map the pagefile after the end of the physical address space. 147 vaddr = self.end() + 0x10000 148 149 self.session.logging.info( 150 "Loading pagefile into physical offset %#08x", vaddr) 151 152 # Map the pagefile into the 153 self.add_run(vaddr, pagefile_offset, pagefile_size) 154 155 # Remember the region for the pagefile. 156 self.pagefile_offset = vaddr 157 self.pagefile_end = vaddr + pagefile_size
158
159 - def describe(self, addr):
160 if self.pagefile_offset <= addr <= self.pagefile_end: 161 return "%#x@Pagefile" % ( 162 addr - self.pagefile_offset) 163 164 return "%#x" % addr
165 166
167 -class KCoreAddressSpace(Elf64CoreDump):
168 """A Linux kernel's /proc/kcore file also maps the entire physical ram. 169 170 http://lxr.free-electrons.com/source/Documentation/x86/x86_64/mm.txt 171 172 ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all 173 physical memory. 174 """ 175 # We must run before the regular Elf64CoreDump address space in the voting 176 # order. 177 order = Elf64CoreDump.order - 1 178 179 __name = "elf64" 180 __image = True 181
182 - def __init__(self, **kwargs):
183 super(KCoreAddressSpace, self).__init__(**kwargs) 184 185 # This is a live address space. 186 self.volatile = True 187 self.mapped_files = {} 188 189 # Collect all ranges between 0xffff880000000000 - 0xffffc7ffffffffff 190 runs = [] 191 192 for start, _, run in self.runs: 193 if 0xffff880000000000 < run.start < 0xffffc7ffffffffff: 194 runs.append((start - 0xffff880000000000, 195 run.file_offset, run.length)) 196 197 self.as_assert(runs, "No kcore compatible virtual ranges.") 198 self.runs.clear() 199 200 # At this point, we think this is a valid, usable kcore file. 201 # RHEL, however, disabled read access to /proc/kcore past the ELF 202 # headers and the file size reflects this. /proc/kcore usually has a 203 # size of at least 64TB (46bits of physical address space in x64). 204 # We use the file size to detect cases where kcore will be unusable. 205 206 if getattr(self.base, "fhandle", None): 207 try: 208 statinfo = os.fstat(self.base.fhandle.fileno()) 209 if statinfo.st_size < 2**46: 210 # We raise a TypeError and not an ASAssertionError because 211 # we need it to be catchable when rekall is used as a 212 # library (i.e GRR). ASAssertionErrors are swallowed by 213 # the address space selection algorithm. 214 raise RuntimeError( 215 "This kcore file is too small (%d bytes) and likely " 216 "invalid for memory analysis. You may want to use pmem " 217 "instead." % statinfo.st_size) 218 except(IOError, AttributeError): 219 pass 220 221 for x in runs: 222 self.add_run(*x)
223
224 - def get_file_address_space(self, filename):
225 try: 226 # Try to read the file with OS APIs. 227 return standard.FileAddressSpace(filename=filename, 228 session=self.session) 229 except IOError: 230 return
231 232
233 -def WriteElfFile(address_space, outfd, session=None):
234 """Convert the address_space to an ELF Core dump file. 235 236 The Core dump will be written to outfd which is expected to have a .write() 237 method. 238 """ 239 runs = list(address_space.get_mappings()) 240 241 elf_profile = elf.ELFProfile(session=session) 242 elf64_pheader = elf_profile.elf64_phdr() 243 elf64_pheader.p_type = "PT_LOAD" 244 elf64_pheader.p_align = 0x1000 245 elf64_pheader.p_flags = "PF_R" 246 247 elf64_header = elf_profile.elf64_hdr() 248 elf64_header.e_ident = elf64_header.e_ident.signature 249 elf64_header.e_type = 'ET_CORE' 250 elf64_header.e_phoff = elf64_header.obj_end 251 elf64_header.e_ehsize = elf64_header.obj_size 252 elf64_header.e_phentsize = elf64_pheader.obj_size 253 elf64_header.e_phnum = len(runs) 254 elf64_header.e_shnum = 0 # We don't have any sections. 255 256 # Where we start writing data. 257 file_offset = (elf64_header.obj_size + 258 # One Phdr for each run. 259 len(runs) * elf64_pheader.obj_size) 260 261 outfd.write(elf64_header.GetData()) 262 for run in runs: 263 elf64_pheader.p_paddr = run.start 264 elf64_pheader.p_memsz = run.length 265 elf64_pheader.p_offset = file_offset 266 elf64_pheader.p_filesz = run.length 267 268 outfd.write(elf64_pheader.GetData()) 269 270 file_offset += run.length 271 272 # Now just copy all the runs 273 total_data = 0 274 for run in runs: 275 offset = run.start 276 length = run.length 277 while length > 0: 278 data = address_space.read(offset, min(10000000, length)) 279 session.report_progress("Writing %sMb", total_data/1024/1024) 280 outfd.write(data) 281 length -= len(data) 282 offset += len(data) 283 total_data += len(data)
284