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