Files
volatility3/volatility/framework/symbols/mac/extensions/__init__.py
T
2019-01-03 01:13:53 +00:00

280 lines
9.2 KiB
Python

# This file was contributed to the Volatility Framework Version 3.
# Copyright (C) 2018 Volatility Foundation.
#
# THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE Volatility Contributors
# Public License V1.0("LICENSE") AS FIRST COMPLETED BY: Volatility Foundation,
# Inc. ANY USE, PUBLIC DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION
# OF, OR PREPARATION OF SUBSEQUENT WORKS, DERIVATIVE WORKS OR DERIVED WORKS BASED
# ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS
# TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THE LICENSE. "LICENSED
# WORK,” “RECIPIENT" AND “DISTRIBUTOR" ARE DEFINED IN THE LICENSE. A COPY OF THE
# LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.txt" ACCOMPANYING THE
# CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS FILE, A
# COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE:
# https://www.volatilityfoundation.org/license/vcpl_v1.0
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
# specific language governing rights and limitations under the License.
#
from typing import Generator, Iterable, Optional, Set, Tuple
from volatility.framework import constants
from volatility.framework import exceptions, interfaces
from volatility.framework.symbols import generic
from volatility.framework.objects import utility
class proc(generic.GenericIntelProcess):
def get_task(self):
return self.task.dereference().cast("task")
def add_process_layer(self, config_prefix: str = None, preferred_name: str = None) -> Optional[str]:
"""Constructs a new layer based on the process's DTB.
Returns the name of the Layer or None.
"""
parent_layer = self._context.memory[self.vol.layer_name]
if not isinstance(parent_layer, interfaces.layers.TranslationLayerInterface):
raise TypeError("Parent layer is not a translation layer, unable to construct process layer")
try:
dtb = self.get_task().map.pmap.pm_cr3
except exceptions.PagedInvalidAddressException:
return None
# Add the constructed layer and return the name
return self._add_process_layer(self._context, dtb, config_prefix, preferred_name)
def get_map_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
try:
task = self.get_task()
except exceptions.PagedInvalidAddressException:
return
try:
current_map = task.map.hdr.links.next
except exceptions.PagedInvalidAddressException:
return
seen = set() # type: Set[int]
for i in range(task.map.hdr.nentries):
if not current_map or current_map.vol.offset in seen:
break
yield current_map
seen.add(current_map.vol.offset)
current_map = current_map.links.next
######
# ikelos: this breaks with multi threading on, but works with it disabled
# with multi threading on, it throws that same error about v4 pickle stuff that linux originally did
# the fix for linux was to call int() so that we were not returning vol objects.
# I call int() on these and the code works nearly 1-1 with the linux one so I am very confused
######
def get_process_memory_sections(self,
context: interfaces.context.ContextInterface,
config_prefix: str,
rw_no_file: bool = False) -> \
Generator[Tuple[int, int], None, None]:
"""Returns a list of sections based on the memory manager's view of this task's virtual memory"""
for vma in self.get_map_iter():
start = int(vma.links.start)
end = int(vma.links.end)
if rw_no_file:
if vma.get_perms() != "rw" or vma.get_path(context, config_prefix) != "":
if vma.get_special_path() != "[heap]":
continue
yield (start, end - start)
class fileglob(generic.GenericIntelProcess):
def get_fg_type(self):
ret = "INVALID"
if self.has_member("fg_type"):
ret = self.member(attr = 'fg_type')
elif self.fg_ops != 0:
try:
ret = self.fg_ops.fo_type
except exceptions.PagedInvalidAddressException:
pass
return ret.description
class vm_map_object(generic.GenericIntelProcess):
def get_map_object(self):
if self.has_member("vm_object"):
return self.vm_object
elif self.has_member("vmo_object"):
return self.vmo_object
raise AttributeError("vm_map_object -> get_object")
class vnode(generic.GenericIntelProcess):
def _do_calc_path(self, ret, vnodeobj, vname):
if vnodeobj is None:
return
if vname:
ret.append(utility.pointer_to_string(vname))
if int(vnodeobj.v_flag) & 0x000001 != 0 and int(vnodeobj.v_mount) != 0:
if int(vnodeobj.v_mount.mnt_vnodecovered) != 0:
self._do_calc_path(ret, vnodeobj.v_mount.mnt_vnodecovered, vnodeobj.v_mount.mnt_vnodecovered.v_name)
else:
self._do_calc_path(ret, vnodeobj.v_parent, vnodeobj.v_parent.v_name)
def full_path(self):
if self.v_flag & 0x000001 != 0 and self.v_mount != 0 and self.v_mount.mnt_flag & 0x00004000 != 0:
ret = "/"
else:
elements = []
files = []
self._do_calc_path(elements, self, self.v_name)
elements.reverse()
for e in elements:
files.append(e.decode("utf-8"))
ret = "/".join(files)
if ret:
ret = "/" + ret
return ret
class vm_map_entry(generic.GenericIntelProcess):
def is_suspicious(self, context, config_prefix):
"""Flags memory regions that are mapped rwx or that map an executable not back from a file on disk"""
ret = False
perms = self.get_perms()
if perms == "rwx":
ret = True
elif perms == "r-x" and self.get_path(context, config_prefix) == "":
ret = True
return ret
def get_perms(self):
permask = "rwx"
perms = ""
for (ctr, i) in enumerate([1, 3, 5]):
if (self.protection & i) == i:
perms = perms + permask[ctr]
else:
perms = perms + "-"
return perms
def get_range_alias(self):
if self.has_member("alias"):
ret = int(self.alias)
else:
ret = int(self.vme_offset) & 0xfff
return ret
def get_special_path(self):
check = self.get_range_alias()
if 0 < check < 10:
ret = "[heap]"
elif check == 30:
ret = "[stack]"
else:
ret = ""
return ret
def get_path(self, context, config_prefix):
node = self.get_vnode(context, config_prefix)
if type(node) == str and node == "sub_map":
ret = node
elif node:
path = []
while node:
v_name = utility.pointer_to_string(node.v_name, 255)
path.append(v_name)
node = node.v_parent
path.reverse()
ret = "/" + "/".join(path)
else:
ret = ""
return ret
def get_object(self):
if self.has_member("vme_object"):
return self.vme_object
elif self.has_member("object"):
return self.object
raise AttributeError("vm_map_entry -> get_object: Unable to determine object")
def get_offset(self):
if self.has_member("vme_offset"):
return self.vme_offset
elif self.has_member("offset"):
return self.offset
raise AttributeError("vm_map_entry -> get_offset: Unable to determine offset")
def get_vnode(self, context, config_prefix):
if self.is_sub_map == 1:
return "sub_map"
# based on find_vnode_object
vnode_object = self.get_object().get_map_object()
found_end = False
while not found_end:
try:
tmp_vnode_object = vnode_object.shadow.dereference()
except exceptions.PagedInvalidAddressException:
break
if tmp_vnode_object.vol.offset == 0:
found_end = True
else:
vnode_object = tmp_vnode_object
try:
ops = vnode_object.pager.mo_pager_ops.dereference()
except exceptions.PagedInvalidAddressException:
return None
found = False
for sym in context.symbol_space.get_symbols_by_location(ops.vol.offset):
if sym.split(constants.BANG)[1] in ["vnode_pager_ops", "_vnode_pager_ops"]:
found = True
break
if found:
vpager = context.object(
config_prefix + constants.BANG + "vnode_pager",
layer_name = vnode_object.vol.layer_name,
offset = vnode_object.pager)
ret = vpager.vnode_handle
else:
ret = None
return ret