From d8f497eb71698a493aa0d83a17c2248caa48312c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 8 Apr 2018 22:25:46 +0100 Subject: [PATCH] Allow unpickling of PrimitiveObjects. --- volatility/framework/interfaces/layers.py | 3 +- volatility/framework/objects/__init__.py | 36 ++++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/volatility/framework/interfaces/layers.py b/volatility/framework/interfaces/layers.py index ae0e9880c..5c419405c 100644 --- a/volatility/framework/interfaces/layers.py +++ b/volatility/framework/interfaces/layers.py @@ -1,6 +1,5 @@ """Defines layers for containing data. One layer may combine other layers, map data based on the data itself, or map a procedure (such as decryption) across another layer of data.""" -import collections.abc import functools import logging import math @@ -9,6 +8,8 @@ import traceback import typing from abc import ABCMeta, abstractmethod +import collections.abc + from volatility.framework import constants, exceptions, validity, interfaces from volatility.framework.interfaces import configuration, context diff --git a/volatility/framework/objects/__init__.py b/volatility/framework/objects/__init__.py index 537a31d3e..52b962078 100644 --- a/volatility/framework/objects/__init__.py +++ b/volatility/framework/objects/__init__.py @@ -48,16 +48,38 @@ class PrimitiveObject(interfaces.objects.ObjectInterface): type_name: str, object_info: interfaces.objects.ObjectInformation, struct_format: str, + new_value = None, **kwargs) -> typing.Type['PrimitiveObject']: - """Creates the appropriate class and returns it so that the native type is inherritted + """Creates the appropriate class and returns it so that the native type is inherited The only reason the **kwargs is added, is so that the inherriting types can override __init__ - without needing to override __new__""" - return cls._struct_type.__new__(cls, - cls._struct_value(context, - struct_format, - object_info.layer_name, - object_info.offset)) + without needing to override __new__ + + We also sneak in new_value, so that we don't have to do expensive (read: impossible) context reads + when unpickling.""" + if new_value is None: + value = cls._struct_value(context, + struct_format, + object_info.layer_name, + object_info.offset) + else: + value = new_value + result = super().__new__(cls, value) + # This prevents us having to go read a context layer when recreating after unpickling + result.__new_value = value + return result + + def __getnewargs_ex__(self): + """Make sure that when pickling, all appropiate parameters for new are provided""" + kwargs = {} + for k, v in self._vol.maps[-1].items(): + if k not in ["context", "struct_format", "object_info", "type_name"]: + kwargs[k] = v + kwargs['new_value'] = self.__new_value + return (self._context, + self._vol.maps[-2]['type_name'], + self._vol.maps[-3], + self._struct_format), kwargs @classmethod def _struct_value(cls,