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

Source Code for Module rekall.plugins.addrspaces.win32

  1  # Rekall Memory Forensics 
  2  # Copyright (C) 2012 Michael Cohen 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # Authors: 
  6  # Michael Cohen <scudette@gmail.com> 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or (at 
 11  # your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, but 
 14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 16  # General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 21  # 
 22  """This is a windows specific address space.""" 
 23  import os 
 24  import struct 
 25  import weakref 
 26   
 27  import pywintypes 
 28  import win32file 
 29   
 30  from rekall import addrspace 
 31  from rekall.plugins.addrspaces import standard 
 32   
 33   
 34  PMEM_MODE_IOSPACE = 0 
 35  PMEM_MODE_PHYSICAL = 1 
 36  PMEM_MODE_PTE = 2 
 37  PMEM_MODE_PTE_PCI = 3 
 38   
 39   
40 -def CTL_CODE(DeviceType, Function, Method, Access):
41 return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method
42 43 44 # IOCTLS for interacting with the driver. 45 INFO_IOCTRL = CTL_CODE(0x22, 0x103, 0, 3) 46 CTRL_IOCTRL = CTL_CODE(0x22, 0x101, 0, 3) 47 48 PAGE_SHIFT = 12 49 50
51 -class Win32FileWrapper(object):
52 """A simple wrapper that makes a win32 file handle look like an AS.""" 53
54 - def __init__(self, fhandle, size=None):
55 self.fhandle = fhandle 56 self.size = size
57
58 - def read(self, offset, length):
59 try: 60 win32file.SetFilePointer(self.fhandle, offset, 0) 61 _, data = win32file.ReadFile(self.fhandle, length) 62 except Exception: 63 return addrspace.ZEROER.GetZeros(length) 64 65 return data
66
67 - def write(self, offset, data):
68 win32file.SetFilePointer(self.fhandle, offset, 0) 69 # The WinPmem driver returns bytes_written == 0 always. This is probably 70 # a bug in its write routine, so we ignore it here. If the operation was 71 # successful we assume all bytes were written. 72 err, _bytes_written = win32file.WriteFile(self.fhandle, data) 73 if err == 0: 74 return len(data) 75 return 0
76
77 - def end(self):
78 return self.size
79
80 - def close(self):
81 win32file.CloseHandle(self.fhandle)
82 83
84 -class Win32AddressSpace(addrspace.CachingAddressSpaceMixIn, 85 addrspace.RunBasedAddressSpace):
86 """ This is a direct file AS for use in windows. 87 88 In windows, in order to open raw devices we need to use the win32 apis. This 89 address space allows us to open the raw device as exported by e.g. the 90 winpmem driver. 91 """ 92 93 CHUNK_SIZE = 0x1000 94
95 - def _OpenFileForRead(self, path):
96 try: 97 fhandle = self.fhandle = win32file.CreateFile( 98 path, 99 win32file.GENERIC_READ, 100 win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, 101 None, 102 win32file.OPEN_EXISTING, 103 win32file.FILE_ATTRIBUTE_NORMAL, 104 None) 105 106 self._closer = weakref.ref( 107 self, lambda x: win32file.CloseHandle(fhandle)) 108 109 self.write_enabled = False 110 return fhandle 111 112 except pywintypes.error as e: 113 raise IOError("Unable to open %s: %s" % (path, e))
114
115 - def close(self):
116 for run in self.get_mappings(): 117 run.address_space.close()
118 119
120 -class Win32FileAddressSpace(Win32AddressSpace):
121 __name = "win32file" 122 123 # We should be the AS of last resort but in front of the non win32 version. 124 order = standard.FileAddressSpace.order - 5 125 __image = True 126
127 - def __init__(self, base=None, filename=None, **kwargs):
128 self.as_assert(base == None, 'Must be first Address Space') 129 super(Win32FileAddressSpace, self).__init__(**kwargs) 130 131 path = filename or self.session.GetParameter("filename") 132 133 self.as_assert(path, "Filename must be specified in session (e.g. " 134 "session.SetParameter('filename', 'MyFile.raw').") 135 136 self.fname = path 137 138 # The file is just a regular file, we open for reading. 139 fhandle = self._OpenFileForRead(path) 140 141 # If we can not get the file size it means this is not a regular file - 142 # maybe a device. 143 self.fhandle_as = Win32FileWrapper(fhandle) 144 try: 145 file_size = win32file.GetFileSize(fhandle) 146 self.add_run(0, 0, file_size, self.fhandle_as) 147 except pywintypes.error: 148 # This may be a device, we just read the whole space. 149 self.add_run(0, 0, 2**63, self.fhandle_as) 150 self.volatile = True
151 152
153 -class WinPmemAddressSpace(Win32AddressSpace):
154 """An address space specifically designed for communicating with WinPmem.""" 155 156 __name = "winpmem" 157 __image = True 158 159 # This is a live address space. 160 volatile = True 161 162 # We must be in front of the regular file based AS. 163 order = Win32FileAddressSpace.order - 5 164 165 # This AS can map files into itself. 166 __can_map_files = True 167
168 - def __init__(self, base=None, filename=None, session=None, **kwargs):
169 self.as_assert(base == None, 'Must be first Address Space') 170 path = filename or session.GetParameter("filename") 171 self.as_assert(path.startswith("\\\\"), 172 "Filename does not look like a device.") 173 174 super(WinPmemAddressSpace, self).__init__( 175 filename=filename, session=session, **kwargs) 176 177 try: 178 # First open for write in case the driver is in write mode. 179 fhandle = self._OpenFileForWrite(path) 180 except IOError: 181 fhandle = self._OpenFileForRead(path) 182 183 self.fhandle_as = Win32FileWrapper(fhandle) 184 185 try: 186 self.ParseMemoryRuns(fhandle) 187 except Exception: 188 raise addrspace.ASAssertionError( 189 "This is not a WinPmem based driver.") 190 191 # Key: lower cased filename, value: offset where it is mapped. 192 self.mapped_files = {} 193 self.filesystems = {}
194
195 - def _OpenFileForWrite(self, path):
196 try: 197 fhandle = self.fhandle = win32file.CreateFile( 198 path, 199 win32file.GENERIC_READ | win32file.GENERIC_WRITE, 200 win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, 201 None, 202 win32file.OPEN_EXISTING, 203 win32file.FILE_ATTRIBUTE_NORMAL, 204 None) 205 self.write_enabled = True 206 self._closer = weakref.ref( 207 self, lambda x: win32file.CloseHandle(fhandle)) 208 209 return fhandle 210 211 except pywintypes.error as e: 212 raise IOError("Unable to open %s: %s" % (path, e))
213 214 FIELDS = (["CR3", "NtBuildNumber", "KernBase", "KDBG"] + 215 ["KPCR%02d" % i for i in xrange(32)] + 216 ["PfnDataBase", "PsLoadedModuleList", "PsActiveProcessHead"] + 217 ["Padding%s" % i for i in xrange(0xff)] + 218 ["NumberOfRuns"]) 219
220 - def ParseMemoryRuns(self, fhandle):
221 # Set acquisition mode. If the driver does not support this mode it will 222 # just fall back to the default. 223 win32file.DeviceIoControl( 224 fhandle, CTRL_IOCTRL, 225 struct.pack("I", PMEM_MODE_PTE), 4, None) 226 227 result = win32file.DeviceIoControl( 228 fhandle, INFO_IOCTRL, "", 102400, None) 229 230 fmt_string = "Q" * len(self.FIELDS) 231 self.memory_parameters = dict(zip(self.FIELDS, struct.unpack_from( 232 fmt_string, result))) 233 234 offset = struct.calcsize(fmt_string) 235 for x in xrange(self.memory_parameters["NumberOfRuns"]): 236 start, length = struct.unpack_from("QQ", result, x * 16 + offset) 237 self.add_run(start, start, length, self.fhandle_as)
238
239 - def ConfigureSession(self, session):
240 dtb = self.memory_parameters["CR3"] 241 session.SetCache("dtb", int(dtb), volatile=False) 242 243 # Get the kernel base directly from the winpmem driver if that is 244 # available. 245 kernel_base = self.memory_parameters["KernBase"] 246 if kernel_base > 0: 247 self.session.SetCache("kernel_base", kernel_base, volatile=False)
248
249 - def _map_raw_filename(self, filename):
250 # Parsing the NTFS can be expensive so we only do it when the user 251 # specifically wanted to be thorough. 252 if self.session.GetParameter("performance") != "thorough": 253 return 254 255 drive, base_filename = os.path.splitdrive(filename) 256 if not drive: 257 return 258 259 try: 260 ntfs_session = self.filesystems[drive] 261 except KeyError: 262 ntfs_session = self.filesystems[drive] = self.session.add_session( 263 filename=r"\\.\%s" % drive, verbose=True, autodetect=[], 264 profile="ntfs") 265 266 # Stat the MFT inode (MFT 2). 267 mft_stat = ntfs_session.plugins.istat(2) 268 269 # Lookup the mft entry by filename. 270 mft_entry = mft_stat.ntfs.MFTEntryByName(base_filename) 271 272 # Open the $DATA stream 273 return mft_entry.open_file()
274
275 - def get_file_address_space(self, filename):
276 """Return an address space for filename.""" 277 try: 278 # Try to read the file with OS APIs. 279 file_as = Win32FileAddressSpace(filename=filename, 280 session=self.session) 281 return file_as 282 except IOError: 283 try: 284 # Try to read the file with raw access. 285 file_as = self._map_raw_filename(filename) 286 return file_as 287 except IOError: 288 # Cant read this file - no mapping available. 289 return
290
291 - def get_mapped_offset(self, filename, file_offset, length=None):
292 # Normalize filename for case insenstive comparisons. 293 filename = filename.lower() 294 mapped_offset = self.mapped_files.get(filename) 295 if mapped_offset is None: 296 file_as = self.get_file_address_space(filename) 297 if not file_as: 298 return 299 300 # Add a guard page and align. 301 mapped_offset = self.mapped_files[filename] = ( 302 (length or self.end()) + 0x10000) & 0xFFFFFFFFFFFFF000 303 304 self.add_run(mapped_offset, 0, file_as.end(), file_as) 305 306 if mapped_offset is not None: 307 return mapped_offset + file_offset
308