From b29f0fb5d16fb2c04fe472db142246ec62567771 Mon Sep 17 00:00:00 2001 From: bradley Date: Tue, 23 May 2017 15:28:00 +1000 Subject: [PATCH 1/4] AFF4 Standard v1 image support. --- volatility/plugins/addrspaces/aff4.py | 133 ++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 volatility/plugins/addrspaces/aff4.py diff --git a/volatility/plugins/addrspaces/aff4.py b/volatility/plugins/addrspaces/aff4.py new file mode 100644 index 00000000..e8c7ca11 --- /dev/null +++ b/volatility/plugins/addrspaces/aff4.py @@ -0,0 +1,133 @@ +# Volatility +# AFF4 Standard v1 memory image reader +# Based on WindowsCrashDumpSpace32 +# +# Copyright (C) 2017 Schatz Forensic +# +# Authors: +# bradley@schatzforensic.com (Bradley Schatz) +# +# This file is part of Volatility. +# +# Volatility 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. +# +# Volatility 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 Volatility. If not, see . +# + +""" An AS for processing crash dumps """ +import struct +import volatility.obj as obj +import volatility.addrspace as addrspace +from pyaff4 import data_store +from pyaff4 import lexicon +from pyaff4.container import Container +import volatility.plugins.addrspaces.standard as standard +import logging + + +LOGGER = logging.getLogger("pyaff4") +LOGGER.setLevel(logging.ERROR) + +#pylint: disable-msg=C0111 + +zipFileHeaderMAGIC = "\x50\x4b\x03\x04" + +class AFF4AddressSpace(standard.FileAddressSpace): + """ This AS supports AFF4 Containers """ + order = 31 + + def __init__(self, base, config, **kwargs): + standard.FileAddressSpace.__init__(self, base, config, layered=True) + + # Must be stacked on a Raw file based image + self.as_assert(base, "No base address space provided") + + # Must start with the a Zip File Header + self.as_assert((base.read(0, 4) == zipFileHeaderMAGIC), "Header signature invalid") + + # Cant stack an AFF4 image on another AFF4 images + self.as_assert(type(base) != AFF4AddressSpace, "Cant stack AFF4 addressspace on same") + self.fhandle = Container.open(self.name) + self.fsize = self.fhandle.Size() + self.fhandle.seek(0) + dtb = self.fhandle.parent.getDTB() + if dtb != 0: + self.dtb = dtb + + def write(self, _addr, _buf): + if not self._config.WRITE: + return False + raise NotImplementedError("Write support is not implemented for AFF4 containers") + + def get_header(self): + return self.header + + def fread(self, length): + length = int(length) + return self.fhandle.read(length) + + def read(self, addr, length): + addr, length = int(addr), int(length) + try: + self.fhandle.seek(addr) + except (IOError, OverflowError): + return None + data = self.fhandle.read(length) + if len(data) == 0: + return None + return data + + def zread(self, addr, length): + data = self.read(addr, length) + if data is None: + data = "\x00" * length + elif len(data) != length: + data += "\x00" * (length - len(data)) + + return data + + def read_long(self, addr): + string = self.read(addr, 4) + longval, = self._long_struct.unpack(string) + return longval + + def get_available_addresses(self): + """ This returns the ranges of valid addresses """ + lastOffset = -1 + lastLength = -1 + for run in self.fhandle.GetRanges(): + offset = run.map_offset + length = run.length + if lastOffset == -1: + lastOffset = offset + lastLength = length + else: + if lastOffset + lastLength == offset: + # merge the two + lastLength = lastLength + length + continue + else: + # emit the last + res = (lastOffset, lastLength) + lastOffset = offset + lastLength = length + yield res + yield (lastOffset, lastLength) + + + def is_valid_address(self, addr): + if addr == None: + return False + return self.fhandle.tree.overlaps(addr) + + def close(self): + self.fhandle.close() From b65f177b6db1c4fc0bf78a4a84e31506623fc810 Mon Sep 17 00:00:00 2001 From: bradley Date: Tue, 23 May 2017 15:32:18 +1000 Subject: [PATCH 2/4] Make DTB scanner only scan image regions containing data, rather than relying on those regions being transparently in-filled with zeros. --- volatility/plugins/overlays/basic.py | 38 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/volatility/plugins/overlays/basic.py b/volatility/plugins/overlays/basic.py index cb7f4b76..0fcf794f 100644 --- a/volatility/plugins/overlays/basic.py +++ b/volatility/plugins/overlays/basic.py @@ -211,22 +211,30 @@ class VolatilityDTB(obj.VolatilityMagic): def generate_suggestions(self): offset = 0 - data = self.obj_vm.zread(offset, constants.SCAN_BLOCKSIZE) - last_range_start, last_range_size = sorted(self.obj_vm.get_available_addresses())[-1] - max_offset = last_range_start + last_range_size - while data: - found = data.find(str(self.obj_parent.DTBSignature), 0) - while found >= 0: - proc = obj.Object("_EPROCESS", offset = offset + found, - vm = self.obj_vm) - if 'Idle' in proc.ImageFileName.v(): - yield proc.Pcb.DirectoryTableBase.v() - found = data.find(str(self.obj_parent.DTBSignature), found + 1) + addresslist = sorted(self.obj_vm.get_available_addresses()) + for range in addresslist: + (range_start, range_size) = range + offset = range_start + range_end = range_start + range_size + read_size = min(constants.SCAN_BLOCKSIZE, range_size) - offset += len(data) - if offset >= max_offset: - break - data = self.obj_vm.zread(offset, constants.SCAN_BLOCKSIZE) + data = self.obj_vm.zread(offset, read_size) + + while 1: + found = data.find(str(self.obj_parent.DTBSignature), 0) + while found >= 0: + proc = obj.Object("_EPROCESS", offset = offset + found, + vm = self.obj_vm) + if 'Idle' in proc.ImageFileName.v(): + yield proc.Pcb.DirectoryTableBase.v() + found = data.find(str(self.obj_parent.DTBSignature), found + 1) + + + offset += len(data) + if offset >= range_end: + break + read_size = min(constants.SCAN_BLOCKSIZE, range_end - offset) + data = self.obj_vm.zread(offset, read_size) class UnixTimeStamp(obj.NativeType): """Class for handling Unix Time Stamps""" From 458f3354f0c7465b52f4be4c04e80fb460e547d6 Mon Sep 17 00:00:00 2001 From: bradley Date: Wed, 13 Sep 2017 22:27:20 +1000 Subject: [PATCH 3/4] Shift AFF4 volatility support to contrib --- .../addrspaces => contrib/plugins/aspaces}/aff4.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename {volatility/plugins/addrspaces => contrib/plugins/aspaces}/aff4.py (98%) diff --git a/volatility/plugins/addrspaces/aff4.py b/contrib/plugins/aspaces/aff4.py similarity index 98% rename from volatility/plugins/addrspaces/aff4.py rename to contrib/plugins/aspaces/aff4.py index e8c7ca11..803fe103 100644 --- a/volatility/plugins/addrspaces/aff4.py +++ b/contrib/plugins/aspaces/aff4.py @@ -27,20 +27,21 @@ import struct import volatility.obj as obj import volatility.addrspace as addrspace +import volatility.plugins.addrspaces.standard as standard +import logging +import pyaff4 from pyaff4 import data_store from pyaff4 import lexicon from pyaff4.container import Container -import volatility.plugins.addrspaces.standard as standard -import logging - LOGGER = logging.getLogger("pyaff4") LOGGER.setLevel(logging.ERROR) -#pylint: disable-msg=C0111 +# pylint: disable-msg=C0111 zipFileHeaderMAGIC = "\x50\x4b\x03\x04" + class AFF4AddressSpace(standard.FileAddressSpace): """ This AS supports AFF4 Containers """ order = 31 @@ -123,11 +124,10 @@ class AFF4AddressSpace(standard.FileAddressSpace): yield res yield (lastOffset, lastLength) - def is_valid_address(self, addr): if addr == None: return False return self.fhandle.tree.overlaps(addr) def close(self): - self.fhandle.close() + self.fhandle.close() \ No newline at end of file From d5b8a19ddf9dea68ac4f7d55485d3f1adc26b0a3 Mon Sep 17 00:00:00 2001 From: bradley Date: Wed, 13 Sep 2017 22:57:24 +1000 Subject: [PATCH 4/4] Revert changes to basic.py --- volatility/plugins/overlays/basic.py | 38 +++++++++++----------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/volatility/plugins/overlays/basic.py b/volatility/plugins/overlays/basic.py index 0fcf794f..cb7f4b76 100644 --- a/volatility/plugins/overlays/basic.py +++ b/volatility/plugins/overlays/basic.py @@ -211,30 +211,22 @@ class VolatilityDTB(obj.VolatilityMagic): def generate_suggestions(self): offset = 0 - addresslist = sorted(self.obj_vm.get_available_addresses()) - for range in addresslist: - (range_start, range_size) = range - offset = range_start - range_end = range_start + range_size - read_size = min(constants.SCAN_BLOCKSIZE, range_size) + data = self.obj_vm.zread(offset, constants.SCAN_BLOCKSIZE) + last_range_start, last_range_size = sorted(self.obj_vm.get_available_addresses())[-1] + max_offset = last_range_start + last_range_size + while data: + found = data.find(str(self.obj_parent.DTBSignature), 0) + while found >= 0: + proc = obj.Object("_EPROCESS", offset = offset + found, + vm = self.obj_vm) + if 'Idle' in proc.ImageFileName.v(): + yield proc.Pcb.DirectoryTableBase.v() + found = data.find(str(self.obj_parent.DTBSignature), found + 1) - data = self.obj_vm.zread(offset, read_size) - - while 1: - found = data.find(str(self.obj_parent.DTBSignature), 0) - while found >= 0: - proc = obj.Object("_EPROCESS", offset = offset + found, - vm = self.obj_vm) - if 'Idle' in proc.ImageFileName.v(): - yield proc.Pcb.DirectoryTableBase.v() - found = data.find(str(self.obj_parent.DTBSignature), found + 1) - - - offset += len(data) - if offset >= range_end: - break - read_size = min(constants.SCAN_BLOCKSIZE, range_end - offset) - data = self.obj_vm.zread(offset, read_size) + offset += len(data) + if offset >= max_offset: + break + data = self.obj_vm.zread(offset, constants.SCAN_BLOCKSIZE) class UnixTimeStamp(obj.NativeType): """Class for handling Unix Time Stamps"""