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

Source Code for Module rekall.plugins.windows.procdump

  1  # Rekall Memory Forensics 
  2  # Copyright (C) 2007,2008 Volatile Systems 
  3  # Copyright (c) 2008 Brendan Dolan-Gavitt <bdolangavitt@wesleyan.edu> 
  4  # Copyright 2013 Google Inc. All Rights Reserved. 
  5  # 
  6  # Additional Authors: 
  7  # Mike Auty <mike.auty@gmail.com> 
  8  # 
  9  # This program is free software; you can redistribute it and/or modify 
 10  # it under the terms of the GNU General Public License as published by 
 11  # the Free Software Foundation; either version 2 of the License, or (at 
 12  # your option) any later version. 
 13  # 
 14  # This program is distributed in the hope that it will be useful, but 
 15  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 16  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 17  # General Public License for more details. 
 18  # 
 19  # You should have received a copy of the GNU General Public License 
 20  # along with this program; if not, write to the Free Software 
 21  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 22  # 
 23   
 24  # pylint: disable=protected-access 
 25   
 26  import os 
 27   
 28  from rekall.plugins.windows import common 
 29  from rekall.plugins import core 
 30  from rekall import plugin 
 31  from rekall_lib import utils 
 32   
 33   
34 -class PEDump(common.WindowsCommandPlugin):
35 """Dump a PE binary from memory.""" 36 37 __name = "pedump" 38 39 __args = [ 40 dict(name="image_base", type="SymbolAddress", required=False, 41 positional=True, 42 help="The address of the image base (dos header)."), 43 44 dict(name="out_file", 45 help="The file name to write."), 46 47 dict(name="address_space", type="AddressSpace", 48 help="The address space to use."), 49 50 dict(name="out_fd", 51 help="A file like object to write the output.") 52 ] 53
54 - def __init__(self, *args, **kwargs):
55 super(PEDump, self).__init__(*args, **kwargs) 56 self.pe_profile = self.session.LoadProfile("pe")
57
58 - def WritePEFile(self, fd=None, address_space=None, image_base=None):
59 """Dumps the PE file found into the filelike object. 60 61 Note that this function can be used for any PE file (e.g. executable, 62 dll, driver etc). Only a base address need be specified. This makes this 63 plugin useful as a routine in other plugins. 64 65 Args: 66 fd: A writable filelike object which must support seeking. 67 address_space: The address_space to read from. 68 image_base: The offset of the dos file header. 69 """ 70 dos_header = self.pe_profile._IMAGE_DOS_HEADER( 71 offset=image_base, vm=address_space) 72 73 image_base = dos_header.obj_offset 74 nt_header = dos_header.NTHeader 75 76 # First copy the PE file header, then copy the sections. 77 data = dos_header.obj_vm.read( 78 image_base, min(1000000, nt_header.OptionalHeader.SizeOfHeaders)) 79 80 if not data: 81 return 82 83 fd.seek(0) 84 fd.write(data) 85 86 for section in nt_header.Sections: 87 # Force some sensible maximum values here. 88 size_of_section = min(10000000, section.SizeOfRawData) 89 physical_offset = min(100000000, int(section.PointerToRawData)) 90 91 data = section.obj_vm.read( 92 section.VirtualAddress + image_base, size_of_section) 93 94 fd.seek(physical_offset, 0) 95 fd.write(data)
96
97 - def collect(self):
98 renderer = self.session.GetRenderer() 99 if self.plugin_args.out_file: 100 out_fd = renderer.open( 101 filename=self.plugin_args.out_file, mode="wb") 102 else: 103 out_fd = self.plugin_args.out_fd 104 105 if not out_fd: 106 self.session.logging.error( 107 "No output filename or file handle specified.") 108 return [] 109 110 with out_fd: 111 self.session.logging.info( 112 "Dumping PE File at image_base %#x to %s", 113 self.plugin_args.image_base, out_fd.name) 114 115 self.WritePEFile(out_fd, self.plugin_args.address_space, 116 self.plugin_args.image_base) 117 118 return []
119 120
121 -class ProcExeDump(core.DirectoryDumperMixin, common.WinProcessFilter):
122 """Dump a process to an executable file sample""" 123 124 __name = "procdump" 125 126 dump_dir_optional = True 127 128 __args = [ 129 dict(name="out_fd", 130 help="A file like object to write the output.") 131 ] 132 133 table_header = [ 134 dict(name="_EPROCESS", width=50), 135 dict(name="Filename"), 136 ] 137
138 - def __init__(self, *args, **kwargs):
139 """Dump a process from memory into an executable. 140 141 In windows PE files are mapped into memory in sections. Each section is 142 mapped into a region within the process virtual memory from a region in 143 the executable file: 144 145 File on Disk Memory Image 146 0-> ------------ image base-> ------------ 147 Header Header 148 ------------ ------------ 149 Section 1 150 ------------ ------------ 151 Section 2 Section 1 152 ------------ ------------ 153 154 ------------ 155 Section 2 156 ------------ 157 158 This plugin simply copies the sections from memory back into the file on 159 disk. Its likely that some of the pages in memory are not actually 160 memory resident, so we might get invalid page reads. In this case the 161 region on disk is null padded. If that happens it will not be possible 162 to run the executable, but the executable can still be disassembled and 163 analysed statically. 164 165 References: 166 http://code.google.com/p/corkami/downloads/detail?name=pe-20110117.pdf 167 168 NOTE: Malware can mess with the headers after loading. The remap option 169 allows to remap the sections on the disk file so they do not collide. 170 171 Args: 172 remap: If set, allows to remap the sections on disk so they do not 173 overlap. 174 175 out_fd: Alternatively, a filelike object can be provided directly. 176 """ 177 super(ProcExeDump, self).__init__(*args, **kwargs) 178 self.pedump = PEDump(session=self.session) 179 if self.dump_dir is None and not self.plugin_args.out_fd: 180 raise plugin.PluginError("Dump dir must be specified.")
181
182 - def collect(self):
183 """Renders the tasks to disk images, outputting progress as they go""" 184 for task in self.filter_processes(): 185 pid = task.UniqueProcessId 186 187 task_address_space = task.get_process_address_space() 188 if not task_address_space: 189 self.session.logging.info( 190 "Can not get task address space - skipping.") 191 continue 192 193 if self.plugin_args.out_fd: 194 self.pedump.WritePEFile( 195 self.plugin_args.out_fd, 196 task_address_space, task.Peb.ImageBaseAddress) 197 yield task, "User FD" 198 199 # Create a new file. 200 else: 201 filename = u"executable.%s_%s.exe" % ( 202 utils.EscapeForFilesystem(task.name), pid) 203 204 yield task, filename 205 206 with self.session.GetRenderer().open( 207 directory=self.dump_dir, 208 filename=filename, 209 mode="wb") as fd: 210 # The Process Environment Block contains the dos header: 211 self.pedump.WritePEFile( 212 fd, task_address_space, task.Peb.ImageBaseAddress)
213 214
215 -class DLLDump(ProcExeDump):
216 """Dump DLLs from a process address space""" 217 218 __name = "dlldump" 219 220 __args = [ 221 dict(name="regex", default=".", type="RegEx", 222 help="A Regular expression for selecting the dlls to dump.") 223 ] 224 225 table_header = [ 226 dict(name="_EPROCESS"), 227 dict(name="base", style="address"), 228 dict(name="module", width=20), 229 dict(name="filename") 230 ] 231
232 - def collect(self):
233 for task in self.filter_processes(): 234 task_as = task.get_process_address_space() 235 236 # Skip kernel and invalid processes. 237 for module in task.get_load_modules(): 238 process_offset = task_as.vtop(task.obj_offset) 239 if process_offset: 240 241 # Skip the modules which do not match the regex. 242 if not self.plugin_args.regex.search( 243 utils.SmartUnicode(module.BaseDllName)): 244 continue 245 246 base_name = os.path.basename( 247 utils.SmartUnicode(module.BaseDllName)) 248 249 dump_file = "module.{0}.{1:x}.{2:x}.{3}".format( 250 task.UniqueProcessId, process_offset, module.DllBase, 251 utils.EscapeForFilesystem(base_name)) 252 253 yield dict(_EPROCESS=task, 254 base=module.DllBase, 255 module=module.BaseDllName, 256 filename=dump_file) 257 258 # Use the procdump module to dump out the binary: 259 with self.session.GetRenderer().open( 260 filename=dump_file, 261 directory=self.dump_dir, 262 mode="wb") as fd: 263 self.pedump.WritePEFile(fd, task_as, module.DllBase) 264 265 else: 266 self.session.logging.error( 267 "Cannot dump %s@%s at %#x\n", 268 task.ImageFileName, module.BaseDllName, 269 int(module.DllBase))
270 271
272 -class ModDump(DLLDump):
273 """Dump kernel drivers from kernel space.""" 274 275 __name = "moddump" 276 277 address_spaces = None 278
279 - def find_space(self, image_base):
280 """Search through all process address spaces for a PE file.""" 281 if self.address_spaces is None: 282 self.address_spaces = [self.kernel_address_space] 283 for task in self.filter_processes(): 284 self.address_spaces.append(task.get_process_address_space()) 285 286 for address_space in self.address_spaces: 287 if address_space.is_valid_address(image_base): 288 return address_space
289 290 table_header = [ 291 dict(name="Name", width=30), 292 dict(name="Base", style="address"), 293 dict(name="Filename") 294 ] 295
296 - def collect(self):
297 modules_plugin = self.session.plugins.modules(session=self.session) 298 299 for module in modules_plugin.lsmod(): 300 if self.plugin_args.regex.search( 301 utils.SmartUnicode(module.BaseDllName)): 302 address_space = self.find_space(module.DllBase) 303 if address_space: 304 dump_file = "driver.{0:x}.{1}".format( 305 module.DllBase, utils.EscapeForFilesystem( 306 module.BaseDllName)) 307 yield (module.BaseDllName, module.DllBase, dump_file) 308 309 with self.session.GetRenderer().open( 310 filename=dump_file, 311 directory=self.dump_dir, 312 mode="wb") as fd: 313 self.pedump.WritePEFile( 314 fd, address_space, module.DllBase)
315