mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-30 05:24:52 +02:00
Add in object type-annotations.
This commit is contained in:
@@ -15,11 +15,11 @@ class Void(interfaces.objects.ObjectInterface):
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
"""Dummy size for Void objects"""
|
||||
raise TypeError("Void types are incomplete, cannot contain data and do not have a size")
|
||||
|
||||
def write(self, value):
|
||||
def write(self, value: typing.Any) -> None:
|
||||
"""Dummy method that does nothing for Void objects"""
|
||||
raise TypeError("Cannot write data to a void, recast as another object")
|
||||
|
||||
@@ -30,16 +30,25 @@ class Function(interfaces.objects.ObjectInterface):
|
||||
|
||||
class PrimitiveObject(interfaces.objects.ObjectInterface):
|
||||
"""PrimitiveObject is an interface for any objects that should simulate a Python primitive"""
|
||||
_struct_type = int # type: typing.Type
|
||||
_struct_type = int # type: typing.ClassVar[typing.Type]
|
||||
|
||||
def __init__(self, context, type_name, object_info, struct_format):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
struct_format: str) -> None:
|
||||
super().__init__(context = context,
|
||||
type_name = type_name,
|
||||
object_info = object_info,
|
||||
struct_format = struct_format)
|
||||
self._struct_format = struct_format
|
||||
|
||||
def __new__(cls, context, type_name, object_info, struct_format, **kwargs):
|
||||
def __new__(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
struct_format: str,
|
||||
**kwargs) -> typing.Type['PrimitiveObject']:
|
||||
"""Creates the appropriate class and returns it so that the native type is inherritted
|
||||
|
||||
The only reason the **kwargs is added, is so that the inherriting types can override __init__
|
||||
@@ -51,7 +60,11 @@ class PrimitiveObject(interfaces.objects.ObjectInterface):
|
||||
object_info.offset))
|
||||
|
||||
@classmethod
|
||||
def _struct_value(cls, context, struct_format, layer_name, offset):
|
||||
def _struct_value(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
struct_format: str,
|
||||
layer_name: str,
|
||||
offset: int) -> typing.Union[int, float, bool, bytes, str]:
|
||||
length = struct.calcsize(struct_format)
|
||||
data = context.memory.read(layer_name, offset, length)
|
||||
(value,) = struct.unpack(struct_format, data)
|
||||
@@ -59,11 +72,11 @@ class PrimitiveObject(interfaces.objects.ObjectInterface):
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
"""Returns the size of the templated object"""
|
||||
return struct.calcsize(template.vol.struct_format)
|
||||
|
||||
def write(self, value):
|
||||
def write(self, value: bytes) -> None:
|
||||
"""Writes the object into the layer of the context at the current offset"""
|
||||
if isinstance(value, self._struct_type):
|
||||
data = struct.pack(self.vol.struct_format, value)
|
||||
@@ -79,26 +92,35 @@ class Integer(PrimitiveObject, int):
|
||||
|
||||
class Float(PrimitiveObject, float):
|
||||
"""Primitive Object that handles double or floating point numbers"""
|
||||
_struct_type = float
|
||||
_struct_type = float # type: typing.ClassVar[typing.Type]
|
||||
|
||||
|
||||
class Char(PrimitiveObject, bytes):
|
||||
"""Primitive Object that handles characters"""
|
||||
_struct_type = bytes
|
||||
_struct_type = bytes # type: typing.ClassVar[typing.Type]
|
||||
|
||||
|
||||
class Bytes(PrimitiveObject, bytes):
|
||||
"""Primitive Object that handles specific series of bytes"""
|
||||
_struct_type = bytes
|
||||
_struct_type = bytes # type: typing.ClassVar[typing.Type]
|
||||
|
||||
def __init__(self, context, type_name, object_info, length = 1):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
length: int = 1) -> None:
|
||||
super().__init__(context = context,
|
||||
type_name = type_name,
|
||||
object_info = object_info,
|
||||
struct_format = str(length) + "s")
|
||||
self._vol['length'] = length
|
||||
|
||||
def __new__(cls, context, type_name, object_info, length = 1, **kwargs):
|
||||
def __new__(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
length: int = 1,
|
||||
**kwargs) -> typing.Type['Bytes']:
|
||||
"""Creates the appropriate class and returns it so that the native type is inherritted
|
||||
|
||||
The only reason the **kwargs is added, is so that the inherriting types can override __init__
|
||||
@@ -118,9 +140,15 @@ class String(PrimitiveObject, str):
|
||||
:type max_length: int
|
||||
|
||||
"""
|
||||
_struct_type = str
|
||||
_struct_type = str # type: typing.ClassVar[typing.Type]
|
||||
|
||||
def __init__(self, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = "strict"):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
max_length: int = 1,
|
||||
encoding: str = "utf-8",
|
||||
errors: str = "strict") -> None:
|
||||
super().__init__(context = context,
|
||||
type_name = type_name,
|
||||
object_info = object_info,
|
||||
@@ -129,7 +157,14 @@ class String(PrimitiveObject, str):
|
||||
self._vol['encoding'] = encoding
|
||||
self._vol['errors'] = errors
|
||||
|
||||
def __new__(cls, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = "strict", **kwargs):
|
||||
def __new__(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
max_length: int = 1,
|
||||
encoding: str = "utf-8",
|
||||
errors: str = "strict",
|
||||
**kwargs) -> typing.Type['String']:
|
||||
"""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__
|
||||
@@ -140,7 +175,7 @@ class String(PrimitiveObject, str):
|
||||
if errors:
|
||||
params['errors'] = errors
|
||||
# Pass the encoding and error parameters to the string constructor to appropriately encode the string
|
||||
value = cls._struct_type.__new__(cls,
|
||||
value = cls._struct_type.__new__(cls, # type: ignore
|
||||
cls._struct_value(context,
|
||||
struct_format = str(max_length) + "s",
|
||||
layer_name = object_info.layer_name,
|
||||
@@ -154,7 +189,12 @@ class String(PrimitiveObject, str):
|
||||
class Pointer(Integer):
|
||||
"""Pointer which points to another object"""
|
||||
|
||||
def __init__(self, context, type_name, object_info, struct_format, subtype = None):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
struct_format: str,
|
||||
subtype: typing.Optional[templates.ObjectTemplate] = None) -> None:
|
||||
self._check_type(subtype, templates.ObjectTemplate)
|
||||
super().__init__(context = context,
|
||||
object_info = object_info,
|
||||
@@ -163,7 +203,11 @@ class Pointer(Integer):
|
||||
self._vol['subtype'] = subtype
|
||||
|
||||
@classmethod
|
||||
def _struct_value(cls, context, struct_format, layer_name, offset):
|
||||
def _struct_value(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
struct_format: str,
|
||||
layer_name: str,
|
||||
offset: int) -> typing.Any:
|
||||
"""Ensure that pointer values always fall within the address space of the layer they're constructed on
|
||||
|
||||
If there's a need for all the data within the address, the pointer should be recast. The "pointer"
|
||||
@@ -175,7 +219,7 @@ class Pointer(Integer):
|
||||
(value,) = struct.unpack(struct_format, data)
|
||||
return value & mask
|
||||
|
||||
def dereference(self, layer_name = None):
|
||||
def dereference(self, layer_name: typing.Optional[str] = None) -> interfaces.objects.ObjectInterface:
|
||||
"""Dereferences the pointer
|
||||
|
||||
Layer_name is identifies the appropriate layer within the context that the pointer points to.
|
||||
@@ -191,24 +235,27 @@ class Pointer(Integer):
|
||||
offset = offset,
|
||||
parent = self))
|
||||
|
||||
def __getattr__(self, attr):
|
||||
def __getattr__(self, attr: str) -> typing.Any:
|
||||
"""Convenience function to access unknown attributes by getting them from the subtype object"""
|
||||
return getattr(self.dereference(), attr)
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
return Integer.VolTemplateProxy.size(template)
|
||||
|
||||
@classmethod
|
||||
def children(cls, template):
|
||||
def children(cls, template: interfaces.objects.Template) -> typing.List[interfaces.objects.Template]:
|
||||
"""Returns the children of the template"""
|
||||
if 'subtype' in template.vol:
|
||||
return [template.vol.subtype]
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def replace_child(cls, template, old_child, new_child):
|
||||
def replace_child(cls,
|
||||
template: interfaces.objects.Template,
|
||||
old_child: interfaces.objects.Template,
|
||||
new_child: interfaces.objects.Template) -> None:
|
||||
"""Substitutes the old_child for the new_child"""
|
||||
if 'subtype' in template.vol:
|
||||
if template.vol.subtype == old_child:
|
||||
@@ -218,36 +265,51 @@ class Pointer(Integer):
|
||||
class BitField(interfaces.objects.ObjectInterface, int):
|
||||
"""Object containing a field which is made up of bits rather than whole bytes"""
|
||||
|
||||
def __new__(cls, context, type_name, object_info, base_type = None, start_bit = 0, end_bit = 0,
|
||||
**kwargs):
|
||||
cls._check_class(base_type.vol.object_class, Integer)
|
||||
value = base_type(context = context,
|
||||
object_info = object_info)
|
||||
return int.__new__(cls, (value >> start_bit) & ((1 << end_bit) - 1))
|
||||
|
||||
def __init__(self, context, type_name, object_info, base_type = None, start_bit = 0, end_bit = 0):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
base_type: typing.Type = int,
|
||||
start_bit: int = 0,
|
||||
end_bit: int = 0) -> None:
|
||||
super().__init__(context, type_name, object_info)
|
||||
self._vol['base_type'] = base_type
|
||||
self._vol['start_bit'] = start_bit
|
||||
self._vol['end_bit'] = end_bit
|
||||
|
||||
def __new__(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
base_type: typing.Type = int,
|
||||
start_bit: int = 0,
|
||||
end_bit: int = 0,
|
||||
**kwargs) -> typing.Type:
|
||||
cls._check_class(base_type.vol.object_class, Integer)
|
||||
value = base_type(context = context,
|
||||
object_info = object_info)
|
||||
return int.__new__(cls, (value >> start_bit) & ((1 << end_bit) - 1)) # type: ignore
|
||||
|
||||
def write(self, value):
|
||||
raise NotImplementedError("Writing to BitFields is not yet implemented")
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
return Integer.VolTemplateProxy.size(template)
|
||||
|
||||
@classmethod
|
||||
def children(cls, template):
|
||||
def children(cls, template: interfaces.objects.Template) -> typing.List[interfaces.objects.Template]:
|
||||
"""Returns the children of the template"""
|
||||
if 'base_type' in template.vol:
|
||||
return [template.vol.base_type]
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def replace_child(cls, template, old_child, new_child):
|
||||
def replace_child(cls,
|
||||
template: interfaces.objects.Template,
|
||||
old_child: interfaces.objects.Template,
|
||||
new_child: interfaces.objects.Template) -> None:
|
||||
"""Substitutes the old_child for the new_child"""
|
||||
if 'base_type' in template.vol:
|
||||
if template.vol.base_type == old_child:
|
||||
@@ -257,16 +319,27 @@ class BitField(interfaces.objects.ObjectInterface, int):
|
||||
class Enumeration(interfaces.objects.ObjectInterface, int):
|
||||
"""Returns an object made up of choices"""
|
||||
|
||||
def __new__(cls, context, type_name, object_info, base_type = None, choices = None, **kwargs):
|
||||
def __new__(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
base_type: interfaces.objects.Template = None,
|
||||
choices: typing.Dict[str, int] = None,
|
||||
**kwargs) -> typing.Type:
|
||||
cls._check_class(base_type.vol.object_class, Integer)
|
||||
value = base_type(context = context,
|
||||
object_info = object_info)
|
||||
return int.__new__(cls, value)
|
||||
return int.__new__(cls, value) # type: ignore
|
||||
|
||||
def __init__(self, context, type_name, object_info, base_type = None, choices = None):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
base_type: typing.Optional[Integer] = None,
|
||||
choices: typing.Optional[typing.Dict[str, int]] = None) -> None:
|
||||
super().__init__(context, type_name, object_info)
|
||||
|
||||
self._inverse_choices = {}
|
||||
self._inverse_choices = {} # type: typing.Dict[int, str]
|
||||
for k, v in self._check_type(choices, dict).items():
|
||||
self._check_type(k, str)
|
||||
self._check_type(v, int)
|
||||
@@ -280,44 +353,47 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
|
||||
|
||||
self._vol['base_type'] = base_type
|
||||
|
||||
def lookup(self, value):
|
||||
def lookup(self, value: int) -> str:
|
||||
"""Looks up an individual value and returns the associated name"""
|
||||
if value in self._inverse_choices:
|
||||
return self._inverse_choices[value]
|
||||
raise ValueError("The value of the enumeration is outside the possible choices")
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
def description(self) -> str:
|
||||
"""Returns the chosen name for the value this object contains"""
|
||||
return self.lookup(self)
|
||||
|
||||
@property
|
||||
def choices(self):
|
||||
def choices(self) -> typing.Dict[str, int]:
|
||||
return self._vol['choices']
|
||||
|
||||
def __getattr__(self, attr):
|
||||
def __getattr__(self, attr: str) -> str:
|
||||
"""Returns the value for a specific name"""
|
||||
if attr in self._vol['choices']:
|
||||
return self._vol['choices'][attr]
|
||||
raise AttributeError("Unknown attribute {} for Enumeration {}".format(attr, self._vol['type_name']))
|
||||
|
||||
def write(self, value):
|
||||
def write(self, value: bytes):
|
||||
raise NotImplementedError("Writing to Enumerations is not yet implemented")
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
return template._vol['base_type'].size
|
||||
|
||||
@classmethod
|
||||
def children(cls, template):
|
||||
def children(cls, template: interfaces.objects.Template) -> typing.List[interfaces.objects.Template]:
|
||||
"""Returns the children of the template"""
|
||||
if 'base_type' in template.vol:
|
||||
return [template.vol.base_type]
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def replace_child(cls, template, old_child, new_child):
|
||||
def replace_child(cls,
|
||||
template: interfaces.objects.Template,
|
||||
old_child: interfaces.objects.Template,
|
||||
new_child: interfaces.objects.Template) -> None:
|
||||
"""Substitutes the old_child for the new_child"""
|
||||
if 'base_type' in template.vol:
|
||||
if template.vol.base_type == old_child:
|
||||
@@ -327,7 +403,12 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
|
||||
class Array(interfaces.objects.ObjectInterface, abc.Sequence):
|
||||
"""Object which can contain a fixed number of an object type"""
|
||||
|
||||
def __init__(self, context, type_name, object_info, count = 0, subtype = None):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
count: int = 0,
|
||||
subtype: templates.ObjectTemplate = None) -> None:
|
||||
self._check_type(subtype, templates.ObjectTemplate)
|
||||
super().__init__(context = context,
|
||||
type_name = type_name,
|
||||
@@ -338,47 +419,53 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
|
||||
# This overrides the little known Sequence.count(val) that returns the number of items in the list that match val
|
||||
# Changing the name would be confusing (since we use count of an array everywhere else), so this is more important
|
||||
@property
|
||||
def count(self):
|
||||
def count(self) -> int:
|
||||
"""Returns the count dynamically"""
|
||||
return self.vol.count
|
||||
|
||||
@count.setter
|
||||
def count(self, value):
|
||||
def count(self, value: int) -> None:
|
||||
"""Sets the count to a specific value"""
|
||||
self._vol['count'] = self._check_type(value, int)
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
"""Returns the size of the array, based on the count and the subtype"""
|
||||
if 'subtype' not in template.vol and 'count' not in template.vol:
|
||||
raise TypeError("Array ObjectTemplate must be provided a count and subtype")
|
||||
return template.vol.get('subtype', None).size * template.vol.get('count', 0)
|
||||
|
||||
@classmethod
|
||||
def children(cls, template):
|
||||
def children(cls, template: interfaces.objects.Template) -> typing.List[interfaces.objects.Template]:
|
||||
"""Returns the children of the template"""
|
||||
if 'subtype' in template.vol:
|
||||
return [template.vol.subtype]
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def replace_child(cls, template, old_child, new_child):
|
||||
def replace_child(cls,
|
||||
template: interfaces.objects.Template,
|
||||
old_child: interfaces.objects.Template,
|
||||
new_child: interfaces.objects.Template) -> None:
|
||||
"""Substitutes the old_child for the new_child"""
|
||||
if 'subtype' in template.vol:
|
||||
if template.vol['subtype'] == old_child:
|
||||
template.update_vol(subtype = new_child)
|
||||
|
||||
@classmethod
|
||||
def relative_child_offset(cls, template, child):
|
||||
def relative_child_offset(cls,
|
||||
template: interfaces.objects.Template,
|
||||
child: interfaces.objects.Template) -> int:
|
||||
"""Returns the relative offset from the head of the parent data to the child member"""
|
||||
if 'subtype' in template and child == 'subtype':
|
||||
return 0
|
||||
raise IndexError("Member not present in array template: {}".format(child))
|
||||
|
||||
def __getitem__(self, i):
|
||||
def __getitem__(self, i: typing.Union[int, slice]) \
|
||||
-> typing.Union[typing.List[interfaces.objects.Template], interfaces.objects.Template]:
|
||||
"""Returns the i-th item from the array"""
|
||||
result = []
|
||||
result = [] # type: typing.List[interfaces.objects.Template]
|
||||
mask = self._context.memory[self.vol.layer_name].address_mask
|
||||
# We use the range function to deal with slices for us
|
||||
series = range(self.vol.count)[i]
|
||||
@@ -395,11 +482,11 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
|
||||
return result[0]
|
||||
return result
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
"""Returns the length of the array"""
|
||||
return self.vol.count
|
||||
|
||||
def write(self, value):
|
||||
def write(self, value) -> None:
|
||||
raise NotImplementedError("Writing to Arrays is not yet implemented")
|
||||
|
||||
|
||||
@@ -409,30 +496,38 @@ class Struct(interfaces.objects.ObjectInterface):
|
||||
Keep the number of methods in this class low or very specific, since each one could overload a valid member.
|
||||
"""
|
||||
|
||||
def __init__(self, context, type_name, object_info, size, members):
|
||||
def __init__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
size: int,
|
||||
members: typing.Dict[str, typing.Tuple[int, interfaces.objects.Template]]) -> None:
|
||||
super().__init__(context = context,
|
||||
type_name = type_name,
|
||||
object_info = object_info,
|
||||
size = size,
|
||||
members = members)
|
||||
self._check_members(members)
|
||||
self._concrete_members = {}
|
||||
self._concrete_members = {} # type: typing.Dict[str, typing.Dict]
|
||||
|
||||
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
||||
@classmethod
|
||||
def size(cls, template):
|
||||
def size(cls, template: interfaces.objects.Template) -> int:
|
||||
"""Method to return the size of this type"""
|
||||
if template.vol.get('size', None) is None:
|
||||
raise TypeError("Struct ObjectTemplate not provided with a size")
|
||||
return template.vol.size
|
||||
|
||||
@classmethod
|
||||
def children(cls, template):
|
||||
def children(cls, template: interfaces.objects.Template) -> typing.List[interfaces.objects.Template]:
|
||||
"""Method to list children of a template"""
|
||||
return [member for _, member in template.vol.members.values()]
|
||||
|
||||
@classmethod
|
||||
def replace_child(cls, template, old_child, new_child):
|
||||
def replace_child(cls,
|
||||
template: interfaces.objects.Template,
|
||||
old_child: interfaces.objects.Template,
|
||||
new_child: interfaces.objects.Template) -> None:
|
||||
"""Replace a child elements within the arguments handed to the template"""
|
||||
for member in template.vol.members.get('members', {}):
|
||||
relative_offset, member_template = template.vol.members[member]
|
||||
@@ -447,7 +542,9 @@ class Struct(interfaces.objects.ObjectInterface):
|
||||
template.update_vol(members = tmp_list)
|
||||
|
||||
@classmethod
|
||||
def relative_child_offset(cls, template, child):
|
||||
def relative_child_offset(cls,
|
||||
template: interfaces.objects.Template,
|
||||
child: interfaces.objects.Template) -> int:
|
||||
"""Returns the relative offset of a child to its parent"""
|
||||
retlist = template.vol.members.get(child, None)
|
||||
if retlist is None:
|
||||
@@ -455,7 +552,8 @@ class Struct(interfaces.objects.ObjectInterface):
|
||||
return retlist[0]
|
||||
|
||||
@classmethod
|
||||
def _check_members(cls, members):
|
||||
def _check_members(cls,
|
||||
members: typing.Dict[str, typing.Tuple[int, interfaces.objects.Template]]) -> None:
|
||||
# Members should be an iterable mapping of symbol names to tuples of (relative_offset, ObjectTemplate)
|
||||
# An object template is a callable that when called with a context, offset, layer_name and type_name
|
||||
if not isinstance(members, abc.Mapping):
|
||||
@@ -463,11 +561,11 @@ class Struct(interfaces.objects.ObjectInterface):
|
||||
if not all([(isinstance(member, tuple) and len(member) == 2) for member in members.values()]):
|
||||
raise TypeError("Struct members must be a tuple of relative_offsets and templates")
|
||||
|
||||
def member(self, attr = 'member'):
|
||||
def member(self, attr: str = 'member') -> object:
|
||||
"""Specifically named method for retrieving members."""
|
||||
return self.__getattr__(attr)
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
def __getattribute__(self, attr: str) -> typing.Any:
|
||||
"""Make sure that class overrides all start with helper_"""
|
||||
if attr == '__dict__' or attr == '__class__' or attr in self.__dict__:
|
||||
return object.__getattribute__(self, attr)
|
||||
@@ -484,7 +582,7 @@ class Struct(interfaces.objects.ObjectInterface):
|
||||
# Change this to an attribute error if we want to prohibit rather than deprecate member collisisons
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
def __getattr__(self, attr: str) -> typing.Any:
|
||||
"""Method for accessing members of the type"""
|
||||
if attr in self._concrete_members:
|
||||
return self._concrete_members[attr]
|
||||
|
||||
@@ -17,38 +17,45 @@ class ObjectTemplate(interfaces.objects.Template, validity.ValidityRoutines):
|
||||
* etc
|
||||
"""
|
||||
|
||||
def __init__(self, object_class = None, type_name = None, **arguments):
|
||||
def __init__(self,
|
||||
object_class: typing.Optional[typing.Type[interfaces.objects.ObjectInterface]] = None,
|
||||
type_name: str = None,
|
||||
**arguments) -> None:
|
||||
super().__init__(type_name = type_name, **arguments)
|
||||
self._check_class(object_class, interfaces.objects.ObjectInterface)
|
||||
self._arguments['object_class'] = object_class
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
def size(self) -> int:
|
||||
"""Returns the children of the templated object (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)"""
|
||||
return self.vol.object_class.VolTemplateProxy.size(self)
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
def children(self) -> typing.List[interfaces.objects.Template]:
|
||||
"""Returns the children of the templated object (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)
|
||||
"""
|
||||
return self.vol.object_class.VolTemplateProxy.children(self)
|
||||
|
||||
def relative_child_offset(self, child):
|
||||
def relative_child_offset(self, child: interfaces.objects.Template) -> int:
|
||||
"""Returns the relative offset of a child of the templated object (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)
|
||||
"""
|
||||
return self.vol.object_class.VolTemplateProxy.relative_child_offset(self, child)
|
||||
|
||||
def replace_child(self, old_child, new_child):
|
||||
def replace_child(self,
|
||||
old_child: interfaces.objects.Template,
|
||||
new_child: interfaces.objects.Template) -> None:
|
||||
"""Replaces `old_child` for `new_child` in the templated object's child list (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)
|
||||
"""
|
||||
return self.vol.object_class.VolTemplateProxy.replace_child(self, old_child, new_child)
|
||||
|
||||
def __call__(self, context, object_info):
|
||||
def __call__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
object_info: interfaces.objects.ObjectInformation) -> interfaces.objects.ObjectInterface:
|
||||
"""Constructs the object
|
||||
|
||||
Returns: an object adhereing to the :class:`~volatility.framework.interfaces.objects.ObjectInterface`
|
||||
"""
|
||||
arguments = {}
|
||||
arguments = {} # type: typing.Dict[str, typing.Any]
|
||||
arguments.update(self.vol)
|
||||
del arguments['object_class']
|
||||
return self.vol.object_class(context = context,
|
||||
@@ -64,7 +71,7 @@ class ReferenceTemplate(interfaces.objects.Template):
|
||||
"""
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
def children(self) -> typing.List[interfaces.objects.Template]:
|
||||
return []
|
||||
|
||||
def _unresolved(self, *args, **kwargs) -> typing.Any:
|
||||
@@ -78,6 +85,8 @@ class ReferenceTemplate(interfaces.objects.Template):
|
||||
replace_child = _unresolved # type: typing.ClassVar[typing.Any]
|
||||
relative_child_offset = _unresolved # type: typing.ClassVar[typing.Any]
|
||||
|
||||
def __call__(self, context, object_info):
|
||||
def __call__(self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
object_info: interfaces.objects.ObjectInformation):
|
||||
template = context.symbol_space.get_type(self.vol.type_name)
|
||||
return template(context = context, object_info = object_info)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from volatility.framework import objects
|
||||
import typing
|
||||
|
||||
from volatility.framework import objects, interfaces
|
||||
from volatility.framework.objects import templates
|
||||
|
||||
|
||||
def array_to_string(array, count = None, errors = 'replace'):
|
||||
def array_to_string(array: objects.Array,
|
||||
count: typing.Optional[int] = None,
|
||||
errors: str = 'replace') -> interfaces.objects.ObjectInterface:
|
||||
"""Takes a volatility Array of characters and returns a string"""
|
||||
# TODO: Consider checking the Array's target is a native char
|
||||
if count is None:
|
||||
@@ -12,7 +16,9 @@ def array_to_string(array, count = None, errors = 'replace'):
|
||||
return array.cast("string", max_length = count, errors = errors)
|
||||
|
||||
|
||||
def pointer_to_string(pointer, count, errors = 'replace'):
|
||||
def pointer_to_string(pointer: objects.Pointer,
|
||||
count: int,
|
||||
errors: str = 'replace'):
|
||||
"""Takes a volatility Pointer to characters and returns a string"""
|
||||
if not isinstance(pointer, objects.Pointer):
|
||||
raise TypeError("pointer_to_string takes a Pointer")
|
||||
@@ -22,7 +28,10 @@ def pointer_to_string(pointer, count, errors = 'replace'):
|
||||
return char.cast("string", max_length = count, errors = errors)
|
||||
|
||||
|
||||
def array_of_pointers(array, count, subtype = None, context = None):
|
||||
def array_of_pointers(array: objects.Array,
|
||||
count: int,
|
||||
subtype: templates.ObjectTemplate = None,
|
||||
context: interfaces.context.ContextInterface = None) -> interfaces.objects.ObjectInterface:
|
||||
"""Takes an object, and recasts it as an array of pointers to subtype"""
|
||||
if isinstance(subtype, str) and context is not None:
|
||||
subtype = context.symbol_space.get_type(subtype)
|
||||
|
||||
Reference in New Issue
Block a user