def list_tasks_process_group(cls,
                                 context: interfaces.context.ContextInterface,
                                 layer_name: str,
                                 darwin_symbols: str,
                                 filter_func: Callable[[int], bool] = lambda _: False) -> \
            Iterable[interfaces.objects.ObjectInterface]:
        """Lists all the tasks in the primary layer using process groups

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            darwin_symbols: The name of the table containing the kernel symbols
            filter_func: A function which takes a task object and returns True if the task should be ignored/filtered

        Returns:
            The list of task objects from the `layer_name` layer's `tasks` list after filtering
        """

        kernel = contexts.Module(context, darwin_symbols, layer_name, 0)

        table_size = kernel.object_from_symbol(symbol_name = "pgrphash")

        pgrphashtbl = kernel.object_from_symbol(symbol_name = "pgrphashtbl")

        proc_array = kernel.object(object_type = "array",
                                   offset = pgrphashtbl,
                                   count = table_size + 1,
                                   subtype = kernel.get_type("pgrphashhead"))

        for proc_list in proc_array:
            for pgrp in mac.MacUtilities.walk_list_head(proc_list, "pg_hash"):
                for proc in mac.MacUtilities.walk_list_head(pgrp.pg_members, "p_pglist"):
                    if not filter_func(proc):
                        yield proc

def list_tasks_process_group ( cls , context : interfaces . context . ContextInterface , layer_name : str , darwin_symbols : str , filter_func : Callable [ [ int ] , bool ] = lambda _ : False ) -> Iterable [ interfaces . objects . ObjectInterface ] : kernel = contexts . Module ( context , darwin_symbols , layer_name , 0 ) table_size = kernel . object_from_symbol ( symbol_name = "pgrphash" ) pgrphashtbl = kernel . object_from_symbol ( symbol_name = "pgrphashtbl" ) proc_array = kernel . object ( object_type = "array" , offset = pgrphashtbl , count = table_size + 1 , subtype = kernel . get_type ( "pgrphashhead" ) ) for proc_list in proc_array : for pgrp in mac . MacUtilities . walk_list_head ( proc_list , "pg_hash" ) : for proc in mac . MacUtilities . walk_list_head ( pgrp . pg_members , "p_pglist" ) : if not filter_func ( proc ) : yield proc


def list_tasks_pid_hash_table(cls,
                                  context: interfaces.context.ContextInterface,
                                  layer_name: str,
                                  darwin_symbols: str,
                                  filter_func: Callable[[int], bool] = lambda _: False) -> \
            Iterable[interfaces.objects.ObjectInterface]:
        """Lists all the tasks in the primary layer using the pid hash table

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            darwin_symbols: The name of the table containing the kernel symbols
            filter_func: A function which takes a task object and returns True if the task should be ignored/filtered

        Returns:
            The list of task objects from the `layer_name` layer's `tasks` list after filtering
        """

        kernel = contexts.Module(context, darwin_symbols, layer_name, 0)

        table_size = kernel.object_from_symbol(symbol_name = "pidhash")

        pidhashtbl = kernel.object_from_symbol(symbol_name = "pidhashtbl")

        proc_array = kernel.object(object_type = "array",
                                   offset = pidhashtbl,
                                   count = table_size + 1,
                                   subtype = kernel.get_type("pidhashhead"))

        for proc_list in proc_array:
            for proc in mac.MacUtilities.walk_list_head(proc_list, "p_hash"):
                if not filter_func(proc):
                    yield proc

def list_tasks_pid_hash_table ( cls , context : interfaces . context . ContextInterface , layer_name : str , darwin_symbols : str , filter_func : Callable [ [ int ] , bool ] = lambda _ : False ) -> Iterable [ interfaces . objects . ObjectInterface ] : kernel = contexts . Module ( context , darwin_symbols , layer_name , 0 ) table_size = kernel . object_from_symbol ( symbol_name = "pidhash" ) pidhashtbl = kernel . object_from_symbol ( symbol_name = "pidhashtbl" ) proc_array = kernel . object ( object_type = "array" , offset = pidhashtbl , count = table_size + 1 , subtype = kernel . get_type ( "pidhashhead" ) ) for proc_list in proc_array : for proc in mac . MacUtilities . walk_list_head ( proc_list , "p_hash" ) : if not filter_func ( proc ) : yield proc


Lists all the modules in the primary layer .

def list_modules(cls, context: interfaces.context.ContextInterface, layer_name: str, darwin_symbols: str):
        """Lists all the modules in the primary layer.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            darwin_symbols: The name of the table containing the kernel symbols

        Returns:
            A list of modules from the `layer_name` layer
        """
        kernel = contexts.Module(context, darwin_symbols, layer_name, 0)

        kmod_ptr = kernel.object_from_symbol(symbol_name = "kmod")

        # TODO - use smear-proof list walking API after dev release
        kmod = kmod_ptr.dereference().cast("kmod_info")
        while kmod != 0:
            yield kmod
            kmod = kmod.next


Lists all the mount structures in the primary layer .

def list_mounts(cls, context: interfaces.context.ContextInterface, layer_name: str, darwin_symbols: str):
        """Lists all the mount structures in the primary layer.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            darwin_symbols: The name of the table containing the kernel symbols

        Returns:
            A list of mount structures from the `layer_name` layer
        """
        kernel = contexts.Module(context, darwin_symbols, layer_name, 0)

        list_head = kernel.object_from_symbol(symbol_name = "mountlist")

        for mount in mac.MacUtilities.walk_tailq(list_head, "mnt_list"):
            yield mount

def list_mounts ( cls , context : interfaces . context . ContextInterface , layer_name : str , darwin_symbols : str ) : kernel = contexts . Module ( context , darwin_symbols , layer_name , 0 ) list_head = kernel . object_from_symbol ( symbol_name = "mountlist" ) for mount in mac . MacUtilities . walk_tailq ( list_head , "mnt_list" ) : yield mount


Lists the registered VFS event watching processes Also lists which event ( s ) a process is registered for

def _generator(self):
        """
        Lists the registered VFS event watching processes
        Also lists which event(s) a process is registered for
        """

        kernel = contexts.Module(self.context, self.config['darwin'], self.config['primary'], 0)

        watcher_table = kernel.object_from_symbol("watcher_table")

        for watcher in watcher_table:
            if watcher == 0:
                continue

            task_name = utility.array_to_string(watcher.proc_name)
            task_pid = watcher.pid

            events = []

            try:
                event_array = kernel.object(object_type = "array",
                                            offset = watcher.event_list,
                                            count = 13,
                                            subtype = kernel.get_type("unsigned char"))

            except exceptions.InvalidAddressException:
                continue

            for i, event in enumerate(event_array):
                if event == 1:
                    events.append(self.event_types[i])

            if events != []:
                yield (0, (task_name, task_pid, ",".join(events)))

def
_generator
(
self
)
:
kernel
=
contexts
.
Module
(
self
.
context
,
self
.
config
[
'darwin'
]
,
self
.
config
[
'primary'
]
,
0
)
watcher_table
=
kernel
.
object_from_symbol
(
"watcher_table"
)
for
watcher
in
watcher_table
:
if
watcher
==
0
:
continue
task_name
=
utility
.
array_to_string
(
watcher
.
proc_name
)
task_pid
=
watcher
.
pid
events
=
[
]
try
:
event_array
=
kernel
.
object
(
object_type
=
"array"
,
offset
=
watcher
.
event_list
,
count
=
13
,
subtype
=
kernel
.
get_type
(
"unsigned char"
)
)
except
exceptions
.
InvalidAddressException
:
continue
for
i
,
event
in
enumerate
(
event_array
)
:
if
event
==
1
:
events
.
append
(
self
.
event_types
[
i
]
)
if
events
!=
[
]
:
yield
(
0
,
(
task_name
,
task_pid
,
","
.
join
(
events
)
)
)


Returns the size of the table based on the next symbol .

def _get_table_size_next_symbol(self, table_addr, ptr_sz, vmlinux):
        """Returns the size of the table based on the next symbol."""
        ret = 0

        sym_table = self.context.symbol_space[vmlinux.name]

        sorted_symbols = sorted([(sym_table.get_symbol(sn).address, sn) for sn in sym_table.symbols])

        sym_address = 0

        for tmp_sym_address, sym_name in sorted_symbols:
            if tmp_sym_address > table_addr:
                sym_address = tmp_sym_address
                break

        if sym_address > 0:
            ret = int((sym_address - table_addr) / ptr_sz)

        return ret

def _get_table_size_next_symbol ( self , table_addr , ptr_sz , vmlinux ) : ret = 0 sym_table = self . context . symbol_space [ vmlinux . name ] sorted_symbols = sorted ( [ ( sym_table . get_symbol ( sn ) . address , sn ) for sn in sym_table . symbols ] ) sym_address = 0 for tmp_sym_address , sym_name in sorted_symbols : if tmp_sym_address > table_addr : sym_address = tmp_sym_address break if sym_address > 0 : ret = int ( ( sym_address - table_addr ) / ptr_sz ) return ret


returns the number of symbols that start with __syscall_meta__ this is a fast way to determine the number of system calls but not the most accurate .

def _get_table_size_meta(self, vmlinux):
        """returns the number of symbols that start with __syscall_meta__ this
        is a fast way to determine the number of system calls, but not the most
        accurate."""

        return len(
            [sym for sym in self.context.symbol_space[vmlinux.name].symbols if sym.startswith("__syscall_meta__")])

def _get_table_size_meta ( self , vmlinux ) : return len ( [ sym for sym in self . context . symbol_space [ vmlinux . name ] . symbols if sym . startswith ( "__syscall_meta__" ) ] )


Find the size of the system call table by disassembling functions that immediately reference it in their first isntruction This is in the form cmp reg NR_syscalls .

def _get_table_info_disassembly(self, ptr_sz, vmlinux):
        """Find the size of the system call table by disassembling functions
        that immediately reference it in their first isntruction This is in the
        form 'cmp reg,NR_syscalls'."""
        table_size = 0

        if not has_capstone:
            return table_size

        if ptr_sz == 4:
            syscall_entry_func = "sysenter_do_call"
            mode = capstone.CS_MODE_32
        else:
            syscall_entry_func = "system_call_fastpath"
            mode = capstone.CS_MODE_64

        md = capstone.Cs(capstone.CS_ARCH_X86, mode)

        try:
            func_addr = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + syscall_entry_func).address
        except exceptions.SymbolError as e:
            # if we can't find the disassemble function then bail and rely on a different method
            return 0

        data = self.context.layers.read(self.config['primary'], func_addr, 6)

        for (address, size, mnemonic, op_str) in md.disasm_lite(data, func_addr):
            if mnemonic == 'CMP':
                table_size = int(op_str.split(",")[1].strip()) & 0xffff
                break

        return table_size


Finds how deep the pid is in the processes list .

def find_level(self, pid):
        """Finds how deep the pid is in the processes list."""
        seen = set([])
        seen.add(pid)
        level = 0
        proc = self._processes.get(pid, None)
        while proc is not None and proc.parent != 0 and proc.parent.pid not in seen:
            ppid = int(proc.parent.pid)

            child_list = self._children.get(ppid, set([]))
            child_list.add(proc.pid)
            self._children[ppid] = child_list
            proc = self._processes.get(ppid, None)
            level += 1
        self._levels[pid] = level

def find_level ( self , pid ) : seen = set ( [ ] ) seen . add ( pid ) level = 0 proc = self . _processes . get ( pid , None ) while proc is not None and proc . parent != 0 and proc . parent . pid not in seen : ppid = int ( proc . parent . pid ) child_list = self . _children . get ( ppid , set ( [ ] ) ) child_list . add ( proc . pid ) self . _children [ ppid ] = child_list proc = self . _processes . get ( ppid , None ) level += 1 self . _levels [ pid ] = level


def _generator(self):
        """Generates the."""
        for proc in self.list_tasks(self.context, self.config['primary'], self.config['vmlinux']):
            self._processes[proc.pid] = proc

        # Build the child/level maps
        for pid in self._processes:
            self.find_level(pid)

        def yield_processes(pid):
            proc = self._processes[pid]
            row = (proc.pid, proc.parent.pid, utility.array_to_string(proc.comm))

            yield (self._levels[pid] - 1, row)
            for child_pid in self._children.get(pid, []):
                yield from yield_processes(child_pid)

        for pid in self._levels:
            if self._levels[pid] == 1:
                yield from yield_processes(pid)

def
_generator
(
self
)
:
for
proc
in
self
.
list_tasks
(
self
.
context
,
self
.
config
[
'primary'
]
,
self
.
config
[
'vmlinux'
]
)
:
self
.
_processes
[
proc
.
pid
]
=
proc
# Build the child/level maps
for
pid
in
self
.
_processes
:
self
.
find_level
(
pid
)
def
yield_processes
(
pid
)
:
proc
=
self
.
_processes
[
pid
]
row
=
(
proc
.
pid
,
proc
.
parent
.
pid
,
utility
.
array_to_string
(
proc
.
comm
)
)
yield
(
self
.
_levels
[
pid
]
1
,
row
)
for
child_pid
in
self
.
_children
.
get
(
pid
,
[
]
)
:
yield
from
yield_processes
(
child_pid
)
for
pid
in
self
.
_levels
:
if
self
.
_levels
[
pid
]
==
1
:
yield
from
yield_processes
(
pid
)


Generate memory regions for a process that may contain injected code .

def _list_injections(self, task):
        """Generate memory regions for a process that may contain injected
        code."""

        proc_layer_name = task.add_process_layer()
        if not proc_layer_name:
            return

        proc_layer = self.context.layers[proc_layer_name]

        for vma in task.mm.get_mmap_iter():
            if vma.is_suspicious() and vma.get_name(self.context, task) != "[vdso]":
                data = proc_layer.read(vma.vm_start, 64, pad = True)
                yield vma, data

def _list_injections ( self , task ) : proc_layer_name = task . add_process_layer ( ) if not proc_layer_name : return proc_layer = self . context . layers [ proc_layer_name ] for vma in task . mm . get_mmap_iter ( ) : if vma . is_suspicious ( ) and vma . get_name ( self . context , task ) != "[vdso]" : data = proc_layer . read ( vma . vm_start , 64 , pad = True ) yield vma , data


Constructs a filter function for process IDs .

def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[Any], bool]:
        """Constructs a filter function for process IDs.

        Args:
            pid_list: List of process IDs that are acceptable (or None if all are acceptable)

        Returns:
            Function which, when provided a process object, returns True if the process is to be filtered out of the list
        """
        # FIXME: mypy #4973 or #2608
        pid_list = pid_list or []
        filter_list = [x for x in pid_list if x is not None]
        if filter_list:

            def filter_func(x):
                return x.pid not in filter_list

            return filter_func
        else:
            return lambda _: False

def
create_pid_filter
(
cls
,
pid_list
:
List
[
int
]
=
)
->
Callable
[
[
Any
]
,
bool
]
:
# FIXME: mypy #4973 or #2608
pid_list
=
pid_list
or
[
]
filter_list
=
[
x
for
x
in
pid_list
if
x
is
not
]
if
filter_list
:
def
filter_func
(
x
)
:
return
x
.
pid
not
in
filter_list
return
filter_func
else
:
return
lambda
_
:
False


Lists all the tasks in the primary layer .

def list_tasks(
            cls,
            context: interfaces.context.ContextInterface,
            layer_name: str,
            vmlinux_symbols: str,
            filter_func: Callable[[int], bool] = lambda _: False) -> Iterable[interfaces.objects.ObjectInterface]:
        """Lists all the tasks in the primary layer.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            vmlinux_symbols: The name of the table containing the kernel symbols

        Yields:
            Process objects
        """
        vmlinux = contexts.Module(context, vmlinux_symbols, layer_name, 0)

        init_task = vmlinux.object_from_symbol(symbol_name = "init_task")

        for task in init_task.tasks:
            if not filter_func(task):
                yield task

def list_tasks ( cls , context : interfaces . context . ContextInterface , layer_name : str , vmlinux_symbols : str , filter_func : Callable [ [ int ] , bool ] = lambda _ : False ) -> Iterable [ interfaces . objects . ObjectInterface ] : vmlinux = contexts . Module ( context , vmlinux_symbols , layer_name , 0 ) init_task = vmlinux . object_from_symbol ( symbol_name = "init_task" ) for task in init_task . tasks : if not filter_func ( task ) : yield task


def list_modules(cls, context: interfaces.context.ContextInterface, layer_name: str,
                     vmlinux_symbols: str) -> Iterable[interfaces.objects.ObjectInterface]:
        """Lists all the modules in the primary layer.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            vmlinux_symbols: The name of the table containing the kernel symbols

        Yields:
            The modules present in the `layer_name` layer's modules list

        This function will throw a SymbolError exception if kernel module support is not enabled.
        """
        vmlinux = contexts.Module(context, vmlinux_symbols, layer_name, 0)

        modules = vmlinux.object_from_symbol(symbol_name = "modules").cast("list_head")

        table_name = modules.vol.type_name.split(constants.BANG)[0]

        for module in modules.to_list(table_name + constants.BANG + "module", "list"):
            yield module

def list_modules ( cls , context : interfaces . context . ContextInterface , layer_name : str , vmlinux_symbols : str ) -> Iterable [ interfaces . objects . ObjectInterface ] : vmlinux = contexts . Module ( context , vmlinux_symbols , layer_name , 0 ) modules = vmlinux . object_from_symbol ( symbol_name = "modules" ) . cast ( "list_head" ) table_name = modules . vol . type_name . split ( constants . BANG ) [ 0 ] for module in modules . to_list ( table_name + constants . BANG + "module" , "list" ) : yield module


def __init__(self,
                 element_type: Type[interfaces.configuration.SimpleTypes] = str,
                 max_elements: Optional[int] = 0,
                 min_elements: Optional[int] = None,
                 *args,
                 **kwargs) -> None:
        """Constructs the object.

        Args:
            element_type: The (requirement) type of each element within the list
            max_elements; The maximum number of acceptable elements this list can contain
            min_elements: The minimum number of acceptable elements this list can contain
        """
        super().__init__(*args, **kwargs)
        if not issubclass(element_type, interfaces.configuration.BasicTypes):
            raise TypeError("ListRequirements can only be populated with simple InstanceRequirements")
        self.element_type = element_type  # type: Type
        self.min_elements = min_elements or 0  # type: int
        self.max_elements = max_elements


Check the types on each of the returned values and their number and then call the element type s check for each one .

def unsatisfied(self, context: interfaces.context.ContextInterface,
                    config_path: str) -> Dict[str, interfaces.configuration.RequirementInterface]:
        """Check the types on each of the returned values and their number and
        then call the element type's check for each one."""
        config_path = interfaces.configuration.path_join(config_path, self.name)
        default = None
        value = self.config_value(context, config_path, default)
        if not value and self.min_elements > 0:
            vollog.log(constants.LOGLEVEL_V, "ListRequirement Unsatisfied - ListRequirement has non-zero min_elements")
            return {config_path: self}
        if value is None and not self.optional:
            # We need to differentiate between no value and an empty list
            vollog.log(constants.LOGLEVEL_V, "ListRequirement Unsatisfied - Value was not specified")
            return {config_path: self}
        elif value is None:
            context.config[config_path] = []
        if not isinstance(value, list):
            # TODO: Check this is the correct response for an error
            raise TypeError("Unexpected config value found: {}".format(repr(value)))
        if not (self.min_elements <= len(value)):
            vollog.log(constants.LOGLEVEL_V, "TypeError - Too few values provided to list option.")
            return {config_path: self}
        if self.max_elements and not (len(value) < self.max_elements):
            vollog.log(constants.LOGLEVEL_V, "TypeError - Too many values provided to list option.")
            return {config_path: self}
        if not all([isinstance(element, self.element_type) for element in value]):
            vollog.log(constants.LOGLEVEL_V, "TypeError - At least one element in the list is not of the correct type.")
            return {config_path: self}
        return {}


def __init__(self, choices: List[str], *args, **kwargs) -> None:
        """Constructs the object.

        Args:
            choices: A list of possible string options that can be chosen from
        """
        super().__init__(*args, **kwargs)
        if not isinstance(choices, list) or any([not isinstance(choice, str) for choice in choices]):
            raise TypeError("ChoiceRequirement takes a list of strings as choices")
        self.choices = choices


Validates the provided value to ensure it is one of the available choices .

def unsatisfied(self, context: interfaces.context.ContextInterface,
                    config_path: str) -> Dict[str, interfaces.configuration.RequirementInterface]:
        """Validates the provided value to ensure it is one of the available
        choices."""
        config_path = interfaces.configuration.path_join(config_path, self.name)
        value = self.config_value(context, config_path)
        if value not in self.choices:
            vollog.log(constants.LOGLEVEL_V, "ValueError - Value is not within the set of available choices")
            return {config_path: self}
        return {}


def unsatisfied(self, context: interfaces.context.ContextInterface,
                    config_path: str) -> Dict[str, interfaces.configuration.RequirementInterface]:
        """Validates the provided value to ensure it is one of the available
        choices."""
        config_path = interfaces.configuration.path_join(config_path, self.name)
        ret_list = super().unsatisfied(context, config_path)
        if ret_list:
            return ret_list
        if (self.config_value(context, config_path, None) is None
                or self.config_value(context, interfaces.configuration.path_join(config_path, 'number_of_elements'))):
            return {config_path: self}
        return {}

def unsatisfied ( self , context : interfaces . context . ContextInterface , config_path : str ) -> Dict [ str , interfaces . configuration . RequirementInterface ] : config_path = interfaces . configuration . path_join ( config_path , self . name ) ret_list = super ( ) . unsatisfied ( context , config_path ) if ret_list : return ret_list if ( self . config_value ( context , config_path , None ) is None or self . config_value ( context , interfaces . configuration . path_join ( config_path , 'number_of_elements' ) ) ) : return { config_path : self } return { }


Method for constructing within the context any required elements from subrequirements .

def construct(self, context: interfaces.context.ContextInterface, config_path: str) -> None:
        """Method for constructing within the context any required elements
        from subrequirements."""

def construct ( self , context : interfaces . context . ContextInterface , config_path : str ) -> None :


Builds a new requirement based on the specified index .

def new_requirement(self, index) -> interfaces.configuration.RequirementInterface:
        """Builds a new requirement based on the specified index."""

def new_requirement ( self , index ) -> interfaces . configuration . RequirementInterface :


def construct(self, context: interfaces.context.ContextInterface, config_path: str) -> None:
        """Method for constructing within the context any required elements
        from subrequirements."""
        new_config_path = interfaces.configuration.path_join(config_path, self.name)
        num_layers_path = interfaces.configuration.path_join(new_config_path, "number_of_elements")
        number_of_layers = context.config[num_layers_path]

        # Build all the layers that can be built
        for i in range(number_of_layers):
            layer_req = self.requirements.get(self.name + str(i), None)
            if layer_req is not None and isinstance(layer_req, TranslationLayerRequirement):
                layer_req.construct(context, new_config_path)


Constructs a new requirement based on the specified index .

def new_requirement(self, index) -> interfaces.configuration.RequirementInterface:
        """Constructs a new requirement based on the specified index."""
        return TranslationLayerRequirement(name = self.name + str(index),
                                           description = "Layer for swap space",
                                           optional = False)

def
new_requirement
(
self
,
index
)
->
interfaces
.
configuration
.
RequirementInterface
:
return
TranslationLayerRequirement
(
name
=
self
.
name
+
str
(
index
)
,
description
=
"Layer for swap space"
,
optional
=
False
)


Constructs a Translation Layer Requirement .

def __init__(self,
                 name: str,
                 description: str = None,
                 default: interfaces.configuration.ConfigSimpleType = None,
                 optional: bool = False,
                 oses: List = None,
                 architectures: List = None) -> None:
        """Constructs a Translation Layer Requirement.

        The configuration option's value will be the name of the layer once it exists in the store

        Args:
            name: Name of the configuration requirement
            description: Description of the configuration requirement
            default: A default value (should not be used for TranslationLayers)
            optional: Whether the translation layer is required or not
            oses: A list of valid operating systems which can satisfy this requirement
            architectures: A list of valid architectures which can satisfy this requirement
        """
        if oses is None:
            oses = []
        if architectures is None:
            architectures = []
        self.oses = oses
        self.architectures = architectures
        super().__init__(name, description, default, optional)

def __init__ ( self , name : str , description : str = None , default : interfaces . configuration . ConfigSimpleType = None , optional : bool = False , oses : List = None , architectures : List = None ) -> None : if oses is None : oses = [ ] if architectures is None : architectures = [ ] self . oses = oses self . architectures = architectures super ( ) . __init__ ( name , description , default , optional )


Validate that the value is a valid layer name and that the layer adheres to the requirements .

def unsatisfied(self, context: interfaces.context.ContextInterface,
                    config_path: str) -> Dict[str, interfaces.configuration.RequirementInterface]:
        """Validate that the value is a valid layer name and that the layer
        adheres to the requirements."""
        config_path = interfaces.configuration.path_join(config_path, self.name)
        value = self.config_value(context, config_path, None)
        if isinstance(value, str):
            if value not in context.layers:
                vollog.log(constants.LOGLEVEL_V, "IndexError - Layer not found in memory space: {}".format(value))
                return {config_path: self}
            if self.oses and context.layers[value].metadata.get('os', None) not in self.oses:
                vollog.log(constants.LOGLEVEL_V, "TypeError - Layer is not the required OS: {}".format(value))
                return {config_path: self}
            if (self.architectures
                    and context.layers[value].metadata.get('architecture', None) not in self.architectures):
                vollog.log(constants.LOGLEVEL_V, "TypeError - Layer is not the required Architecture: {}".format(value))
                return {config_path: self}
            return {}

        if value is not None:
            vollog.log(constants.LOGLEVEL_V,
                       "TypeError - Translation Layer Requirement only accepts string labels: {}".format(repr(value)))
            return {config_path: self}

        # TODO: check that the space in the context lives up to the requirements for arch/os etc

        ### NOTE: This validate method has side effects (the dependencies can change)!!!

        self._validate_class(context, interfaces.configuration.parent_path(config_path))
        vollog.log(constants.LOGLEVEL_V, "IndexError - No configuration provided: {}".format(config_path))
        return {config_path: self}


Constructs the appropriate layer and adds it based on the class parameter .

def construct(self, context: interfaces.context.ContextInterface, config_path: str) -> None:
        """Constructs the appropriate layer and adds it based on the class
        parameter."""
        config_path = interfaces.configuration.path_join(config_path, self.name)

        # Determine the layer name
        name = self.name
        counter = 2
        while name in context.layers:
            name = self.name + str(counter)
            counter += 1

        args = {"context": context, "config_path": config_path, "name": name}

        if any(
            [subreq.unsatisfied(context, config_path) for subreq in self.requirements.values() if not subreq.optional]):
            return None

        obj = self._construct_class(context, config_path, args)
        if obj is not None and isinstance(obj, interfaces.layers.DataLayerInterface):
            context.add_layer(obj)
            # This should already be done by the _construct_class method
            # context.config[config_path] = obj.name
        return None


Builds the appropriate configuration for the specified requirement .

def build_configuration(self, context: interfaces.context.ContextInterface, _: str,
                            value: Any) -> interfaces.configuration.HierarchicalDict:
        """Builds the appropriate configuration for the specified
        requirement."""
        return context.layers[value].build_configuration()

def build_configuration ( self , context : interfaces . context . ContextInterface , _ : str , value : Any ) -> interfaces . configuration . HierarchicalDict : return context . layers [ value ] . build_configuration ( )


Validate that the value is a valid within the symbol space of the provided context .

def unsatisfied(self, context: interfaces.context.ContextInterface,
                    config_path: str) -> Dict[str, interfaces.configuration.RequirementInterface]:
        """Validate that the value is a valid within the symbol space of the
        provided context."""
        config_path = interfaces.configuration.path_join(config_path, self.name)
        value = self.config_value(context, config_path, None)
        if not isinstance(value, str) and value is not None:
            vollog.log(constants.LOGLEVEL_V,
                       "TypeError - SymbolTableRequirement only accepts string labels: {}".format(repr(value)))
            return {config_path: self}
        if value and value in context.symbol_space:
            # This is an expected situation, so return rather than raise
            return {}
        elif value:
            vollog.log(constants.LOGLEVEL_V, "IndexError - Value not present in the symbol space: {}".format(value
                                                                                or ""))

        ### NOTE: This validate method has side effects (the dependencies can change)!!!

        self._validate_class(context, interfaces.configuration.parent_path(config_path))
        vollog.log(constants.LOGLEVEL_V, "Symbol table requirement not yet fulfilled: {}".format(config_path))
        return {config_path: self}


Constructs the symbol space within the context based on the subrequirements .

def construct(self, context: interfaces.context.ContextInterface, config_path: str) -> None:
        """Constructs the symbol space within the context based on the
        subrequirements."""
        config_path = interfaces.configuration.path_join(config_path, self.name)
        # Determine the space name
        name = context.symbol_space.free_table_name(self.name)

        args = {"context": context, "config_path": config_path, "name": name}

        if any(
            [subreq.unsatisfied(context, config_path) for subreq in self.requirements.values() if not subreq.optional]):
            return None

        # Fill out the parameter for class creation
        if not isinstance(self.requirements["class"], interfaces.configuration.ClassRequirement):
            raise TypeError("Class requirement is not of type ClassRequirement: {}".format(
                repr(self.requirements["class"])))
        cls = self.requirements["class"].cls
        node_config = context.config.branch(config_path)
        for req in cls.get_requirements():
            if req.name in node_config.data and req.name != "class":
                args[req.name] = node_config.data[req.name]

        obj = self._construct_class(context, config_path, args)
        if obj is not None and isinstance(obj, interfaces.symbols.SymbolTableInterface):
            context.symbol_space.append(obj)
        return None


def build_configuration(self, context: interfaces.context.ContextInterface, _: str,
                            value: Any) -> interfaces.configuration.HierarchicalDict:
        """Builds the appropriate configuration for the specified
        requirement."""
        return context.symbol_space[value].build_configuration()

def build_configuration ( self , context : interfaces . context . ContextInterface , _ : str , value : Any ) -> interfaces . configuration . HierarchicalDict : return context . symbol_space [ value ] . build_configuration ( )


A function for raising exceptions if a given set of values is invalid according to the column properties .

def _validate_values(self, values: List[interfaces.renderers.BaseTypes]) -> None:
        """A function for raising exceptions if a given set of values is
        invalid according to the column properties."""
        if not (isinstance(values, collections.abc.Sequence) and len(values) == len(self._treegrid.columns)):
            raise TypeError(
                "Values must be a list of objects made up of simple types and number the same as the columns")
        for index in range(len(self._treegrid.columns)):
            column = self._treegrid.columns[index]
            val = values[index]
            if not isinstance(val, (column.type, interfaces.renderers.BaseAbsentValue)):
                raise TypeError(
                    "Values item with index {} is the wrong type for column {} (got {} but expected {})".format(
                        index, column.name, type(val), column.type))


Returns the list of values from the particular node based on column index .

def values(self) -> List[interfaces.renderers.BaseTypes]:
        """Returns the list of values from the particular node, based on column
        index."""
        return self._values

def values ( self ) -> List [ interfaces . renderers . BaseTypes ] : return self . _values


def path(self) -> str:
        """Returns a path identifying string.

        This should be seen as opaque by external classes, Parsing of
        path locations based on this string are not guaranteed to remain
        stable.
        """
        return self._path

def path ( self ) -> str : return self . _path


Returns the parent node of this node or None .

def parent(self) -> Optional['TreeNode']:
        """Returns the parent node of this node or None."""
        return self._parent

def parent ( self ) -> Optional [ 'TreeNode' ] : return self . _parent


Return the path depth of the current node .

def path_depth(self) -> int:
        """Return the path depth of the current node."""
        return len(self.path.split(TreeGrid.path_sep))

def path_depth ( self ) -> int : return len ( self . path . split ( TreeGrid . path_sep ) )


Updates the path based on the addition or removal of a node higher up in the tree .

def path_changed(self, path: str, added: bool = False) -> None:
        """Updates the path based on the addition or removal of a node higher
        up in the tree.

        This should only be called by the containing TreeGrid and
        expects to only be called for affected nodes.
        """
        components = self._path.split(TreeGrid.path_sep)
        changed = path.split(TreeGrid.path_sep)
        changed_index = len(changed) - 1
        if int(components[changed_index]) >= int(changed[-1]):
            components[changed_index] = str(int(components[changed_index]) + (1 if added else -1))
        self._path = TreeGrid.path_sep.join(components)

def path_changed ( self , path : str , added : bool = False ) -> None : components = self . _path . split ( TreeGrid . path_sep ) changed = path . split ( TreeGrid . path_sep ) changed_index = len ( changed ) - 1 if int ( components [ changed_index ] ) >= int ( changed [ - 1 ] ) : components [ changed_index ] = str ( int ( components [ changed_index ] ) + ( 1 if added else - 1 ) ) self . _path = TreeGrid . path_sep . join ( components )


Constructs a TreeGrid object using a specific set of columns .

def __init__(self, columns: List[Tuple[str, interfaces.renderers.BaseTypes]],
                 generator: Optional[Iterable[Tuple[int, Tuple]]]) -> None:
        """Constructs a TreeGrid object using a specific set of columns.

        The TreeGrid itself is a root element, that can have children but no values.
        The TreeGrid does *not* contain any information about formatting,
        these are up to the renderers and plugins.

        Args:
            columns: A list of column tuples made up of (name, type).
            generator: An iterable containing row for a tree grid, each row contains a indent level followed by the values for each column in order.
        """
        self._populated = False
        self._row_count = 0
        self._children = []  # type: List[TreeNode]
        converted_columns = []  # type: List[interfaces.renderers.Column]
        if len(columns) < 1:
            raise ValueError("Columns must be a list containing at least one column")
        for (name, column_type) in columns:
            is_simple_type = issubclass(column_type, self.base_types)
            if not is_simple_type:
                raise TypeError("Column {}'s type is not a simple type: {}".format(name,
                                                                                column_type.__class__.__name__))
            converted_columns.append(interfaces.renderers.Column(name, column_type))
        self.RowStructure = RowStructureConstructor([column.name for column in converted_columns])
        self._columns = converted_columns
        if generator is None:
            generator = []
        generator = iter(generator)

        self._generator = generator


Populates the tree by consuming the TreeGrid s construction generator Func is called on every node so can be used to create output on demand .

def populate(self,
                 function: interfaces.renderers.VisitorSignature = None,
                 initial_accumulator: Any = None,
                 fail_on_errors: bool = True) -> Optional[Exception]:
        """Populates the tree by consuming the TreeGrid's construction
        generator Func is called on every node, so can be used to create output
        on demand.

        This is equivalent to a one-time visit.

        Args:
            function: The visitor to be called on each row of the treegrid
            initial_accumulator: The initial value for an accumulator passed to the visitor to allow it to maintain state
            fail_on_errors: A boolean defining whether exceptions should be caught or bubble up
        """
        accumulator = initial_accumulator
        if function is None:

            def function(_x: interfaces.renderers.TreeNode, _y: Any) -> Any:
                return None

        if not self.populated:
            try:
                prev_nodes = []  # type: List[TreeNode]
                for (level, item) in self._generator:
                    parent_index = min(len(prev_nodes), level)
                    parent = prev_nodes[parent_index - 1] if parent_index > 0 else None
                    treenode = self._append(parent, item)
                    prev_nodes = prev_nodes[0:parent_index] + [treenode]
                    if function is not None:
                        accumulator = function(treenode, accumulator)
                    self._row_count += 1
            except Exception as excp:
                if fail_on_errors:
                    raise
                vollog.debug("Exception during population: {}".format(excp))
                self._populated = True
                return excp
        self._populated = True
        return None

def
populate
(
self
,
function
:
interfaces
.
renderers
.
VisitorSignature
=
,
initial_accumulator
:
Any
=
,
fail_on_errors
:
bool
=
True
)
->
Optional
[
Exception
]
:
accumulator
=
initial_accumulator
if
function
is
:
def
function
(
_x
:
interfaces
.
renderers
.
TreeNode
,
_y
:
Any
)
->
Any
:
return
if
not
self
.
populated
:
try
:
prev_nodes
=
[
]
# type: List[TreeNode]
for
(
level
,
item
)
in
self
.
_generator
:
parent_index
=
min
(
len
(
prev_nodes
)
,
level
)
parent
=
prev_nodes
[
parent_index
1
]
if
parent_index
>
0
else
treenode
=
self
.
_append
(
parent
,
item
)
prev_nodes
=
prev_nodes
[
0
:
parent_index
]
+
[
treenode
]
if
function
is
not
:
accumulator
=
function
(
treenode
,
accumulator
)
self
.
_row_count
+=
1
except
Exception
as
excp
:
if
fail_on_errors
:
raise
vollog
.
debug
(
"Exception during population: {}"
.
format
(
excp
)
)
self
.
_populated
=
True
return
excp
self
.
_populated
=
True
return


Indicates that population has completed and the tree may now be manipulated separately .

def populated(self):
        """Indicates that population has completed and the tree may now be
        manipulated separately."""
        return self._populated

def populated ( self ) : return self . _populated


Returns the available columns and their ordering and types .

def columns(self) -> List[interfaces.renderers.Column]:
        """Returns the available columns and their ordering and types."""
        return self._columns

def columns ( self ) -> List [ interfaces . renderers . Column ] : return self . _columns


def row_count(self) -> int:
        """Returns the number of rows populated."""
        return self._row_count

def row_count ( self ) -> int : return self . _row_count


Returns the subnodes of a particular node in order .

def children(self, node) -> List[interfaces.renderers.TreeNode]:
        """Returns the subnodes of a particular node in order."""
        return [node for node, _ in self._find_children(node)]

def children ( self , node ) -> List [ interfaces . renderers . TreeNode ] : return [ node for node , _ in self . _find_children ( node ) ]


Returns the children list associated with a particular node .

def _find_children(self, node):
        """Returns the children list associated with a particular node.

        Returns None if the node does not exist
        """
        children = self._children
        try:
            if node is not None:
                for path_component in node.path.split(self.path_sep):
                    _, children = children[int(path_component)]
        except IndexError:
            return []
        return children

def _find_children ( self , node ) : children = self . _children try : if node is not None : for path_component in node . path . split ( self . path_sep ) : _ , children = children [ int ( path_component ) ] except IndexError : return [ ] return children


Returns the values for a particular node .

def values(self, node):
        """Returns the values for a particular node.

        The values returned are mutable,
        """
        if node is None:
            raise TypeError("Node must be a valid node within the TreeGrid")
        return node.values


Adds a new node at the top level if parent is None or under the parent node otherwise after all other children .

def _append(self, parent, values):
        """Adds a new node at the top level if parent is None, or under the
        parent node otherwise, after all other children."""
        children = self.children(parent)
        return self._insert(parent, len(children), values)

def _append ( self , parent , values ) : children = self . children ( parent ) return self . _insert ( parent , len ( children ) , values )


Inserts an element into the tree at a specific position .

def _insert(self, parent, position, values):
        """Inserts an element into the tree at a specific position."""
        parent_path = ""
        children = self._find_children(parent)
        if parent is not None:
            parent_path = parent.path + self.path_sep
        newpath = parent_path + str(position)
        tree_item = TreeNode(newpath, self, parent, values)
        for node, _ in children[position:]:
            self.visit(node, lambda child, _: child.path_changed(newpath, True), None)
        children.insert(position, (tree_item, []))
        return tree_item

def _insert ( self , parent , position , values ) : parent_path = "" children = self . _find_children ( parent ) if parent is not None : parent_path = parent . path + self . path_sep newpath = parent_path + str ( position ) tree_item = TreeNode ( newpath , self , parent , values ) for node , _ in children [ position : ] : self . visit ( node , lambda child , _ : child . path_changed ( newpath , True ) , None ) children . insert ( position , ( tree_item , [ ] ) ) return tree_item


Returns true if descendent is a child grandchild etc of node .

def is_ancestor(self, node, descendant):
        """Returns true if descendent is a child, grandchild, etc of node."""
        return descendant.path.startswith(node.path)

def is_ancestor ( self , node , descendant ) : return descendant . path . startswith ( node . path )


def max_depth(self):
        """Returns the maximum depth of the tree."""
        return self.visit(None, lambda n, a: max(a, self.path_depth(n)), 0)

def max_depth ( self ) : return self . visit ( None , lambda n , a : max ( a , self . path_depth ( n ) ) , 0 )


Visits all the nodes in a tree calling function on each one .

def visit(self,
              node: Optional[interfaces.renderers.TreeNode],
              function: Callable[[interfaces.renderers.TreeNode, _T], _T],
              initial_accumulator: _T,
              sort_key: Optional[interfaces.renderers.ColumnSortKey] = None):
        """Visits all the nodes in a tree, calling function on each one.

        function should have the signature function(node, accumulator) and return new_accumulator
        If accumulators are not needed, the function must still accept a second parameter.

        The order of that the nodes are visited is always depth first, however, the order children are traversed can
        be set based on a sort_key function which should accept a node's values and return something that can be
        sorted to receive the desired order (similar to the sort/sorted key).

        We use the private _find_children function so that we don't have to re-traverse the tree
        for every node we descend further down
        """
        if not self.populated:
            self.populate()

        # Find_nodes is path dependent, whereas _visit is not
        # So in case the function modifies the node's path, find the nodes first
        children = self._find_children(node)
        accumulator = initial_accumulator
        # We split visit into two, so that we don't have to keep calling find_children to traverse the tree
        if node is not None:
            accumulator = function(node, initial_accumulator)
        if children is not None:
            if sort_key is not None:
                sort_key_not_none = sort_key  # Only necessary because of mypy
                children = sorted(children, key = lambda x: sort_key_not_none(x[0].values))
                if not sort_key.ascending:
                    children = reversed(children)
            accumulator = self._visit(children, function, accumulator, sort_key)
        return accumulator


def _visit(self,
               list_of_children: List['TreeNode'],
               function: Callable,
               accumulator: _T,
               sort_key: Optional[interfaces.renderers.ColumnSortKey] = None) -> _T:
        """Visits all the nodes in a tree, calling function on each one."""
        if list_of_children is not None:
            for n, children in list_of_children:
                accumulator = function(n, accumulator)
                if sort_key is not None:
                    sort_key_not_none = sort_key  # Only necessary because of mypy
                    children = sorted(children, key = lambda x: sort_key_not_none(x[0].values))
                    if not sort_key.ascending:
                        children = reversed(children)
                accumulator = self._visit(children, function, accumulator, sort_key)
        return accumulator

def
_visit
(
self
,
list_of_children
:
List
[
'TreeNode'
]
,
function
:
Callable
,
accumulator
:
_T
,
sort_key
:
Optional
[
interfaces
.
renderers
.
ColumnSortKey
]
=
)
->
_T
:
if
list_of_children
is
not
:
for
n
,
children
in
list_of_children
:
accumulator
=
function
(
n
,
accumulator
)
if
sort_key
is
not
:
sort_key_not_none
=
sort_key
# Only necessary because of mypy
children
=
sorted
(
children
,
key
=
lambda
x
:
sort_key_not_none
(
x
[
0
]
.
values
)
)
if
not
sort_key
.
ascending
:
children
=
reversed
(
children
)
accumulator
=
self
.
_visit
(
children
,
function
,
accumulator
,
sort_key
)
return
accumulator


The key function passed as the sort key .

def __call__(self, values: List[Any]) -> Any:
        """The key function passed as the sort key."""
        value = values[self._index]
        if isinstance(value, interfaces.renderers.BaseAbsentValue):
            if self._type == datetime.datetime:
                value = datetime.datetime.min
            elif self._type in [int, float]:
                value = -1
            elif self._type == bool:
                value = False
            elif self._type in [str, renderers.Disassembly]:
                value = "-"
            elif self._type == bytes:
                value = b""
        return value

def __call__ ( self , values : List [ Any ] ) -> Any : value = values [ self . _index ] if isinstance ( value , interfaces . renderers . BaseAbsentValue ) : if self . _type == datetime . datetime : value = datetime . datetime . min elif self . _type in [ int , float ] : value = - 1 elif self . _type == bool : value = False elif self . _type in [ str , renderers . Disassembly ] : value = "-" elif self . _type == bytes : value = b"" return value


Round an address up or down based on an alignment .

def round(addr: int, align: int, up: bool = False) -> int:
    """Round an address up or down based on an alignment.

    Args:
        addr: the address
        align: the alignment value
        up: Whether to round up or not

    Returns:
        The aligned address
    """

    if addr % align == 0:
        return addr
    else:
        if up:
            return (addr + (align - (addr % align)))
        return (addr - (addr % align))

def round ( addr : int , align : int , up : bool = False ) -> int : if addr % align == 0 : return addr else : if up : return ( addr + ( align - ( addr % align ) ) ) return ( addr - ( addr % align ) )


Converts the connection four_tuple : ( source ip source port dest ip dest port )

def convert_network_four_tuple(family, four_tuple):
    """Converts the connection four_tuple: (source ip, source port, dest ip,
    dest port)

    into their string equivalents. IP addresses are expected as a tuple
    of unsigned shorts Ports are converted to proper endianess as well
    """

    if family == socket.AF_INET:
        ret = (convert_ipv4(four_tuple[0]), convert_port(four_tuple[1]), convert_ipv4(four_tuple[2]),
               convert_port(four_tuple[3]))
    elif family == socket.AF_INET6:
        ret = (convert_ipv6(four_tuple[0]), convert_port(four_tuple[1]), convert_ipv6(four_tuple[2]),
               convert_port(four_tuple[3]))
    else:
        ret = None

    return ret

def convert_network_four_tuple ( family , four_tuple ) : if family == socket . AF_INET : ret = ( convert_ipv4 ( four_tuple [ 0 ] ) , convert_port ( four_tuple [ 1 ] ) , convert_ipv4 ( four_tuple [ 2 ] ) , convert_port ( four_tuple [ 3 ] ) ) elif family == socket . AF_INET6 : ret = ( convert_ipv6 ( four_tuple [ 0 ] ) , convert_port ( four_tuple [ 1 ] ) , convert_ipv6 ( four_tuple [ 2 ] ) , convert_port ( four_tuple [ 3 ] ) ) else : ret = None return ret


def _load_segments(self) -> None:
        """Load the segments from based on the PT_LOAD segments of the Elf64 format"""
        ehdr = self.context.object(self._elf_table_name + constants.BANG + "Elf64_Ehdr",
                                   layer_name = self._base_layer,
                                   offset = 0)

        segments = []

        for pindex in range(ehdr.e_phnum):
            phdr = self.context.object(self._elf_table_name + constants.BANG + "Elf64_Phdr",
                                       layer_name = self._base_layer,
                                       offset = ehdr.e_phoff + (pindex * ehdr.e_phentsize))
            # We only want PT_TYPES with valid sizes
            if phdr.p_type.lookup() == "PT_LOAD" and phdr.p_filesz == phdr.p_memsz and phdr.p_filesz > 0:
                # Cast these to ints to ensure the offsets don't need reconstructing
                segments.append((int(phdr.p_paddr), int(phdr.p_offset), int(phdr.p_memsz), int(phdr.p_memsz)))

        if len(segments) == 0:
            raise ElfFormatException(self.name, "No ELF segments defined in {}".format(self._base_layer))

        self._segments = segments


Verifies the header of the PDB file and returns the version of the file .

def _check_header(self) -> Optional[Tuple[str, interfaces.objects.ObjectInterface]]:
        """Verifies the header of the PDB file and returns the version of the
        file."""
        for header in self._headers:
            header_type = self.pdb_symbol_table + constants.BANG + header
            current_header = self.context.object(header_type, self._base_layer, 0)
            if utility.array_to_string(current_header.Magic) == self._headers[header]:
                if not (current_header.PageSize < 0x100 or current_header.PageSize > (128 * 0x10000)):
                    return header, current_header
        return None

def _check_header ( self ) -> Optional [ Tuple [ str , interfaces . objects . ObjectInterface ] ] : for header in self . _headers : header_type = self . pdb_symbol_table + constants . BANG + header current_header = self . context . object ( header_type , self . _base_layer , 0 ) if utility . array_to_string ( current_header . Magic ) == self . _headers [ header ] : if not ( current_header . PageSize < 0x100 or current_header . PageSize > ( 128 * 0x10000 ) ) : return header , current_header return None


Returns a list of the lower layers that this layer is dependent upon .

def dependencies(self) -> List[str]:
        """Returns a list of the lower layers that this layer is dependent
        upon."""
        return [self._base_layer]

def dependencies ( self ) -> List [ str ] : return [ self . _base_layer ]


def page_size(cls) -> int:
        """Page size for the intel memory layers.

        All Intel layers work on 4096 byte pages
        """
        return 1 << cls._page_size_in_bits

def page_size ( cls ) -> int : return 1 << cls . _page_size_in_bits


Returns the bits_per_register to determine the range of an IntelTranslationLayer .

def bits_per_register(cls) -> int:
        """Returns the bits_per_register to determine the range of an
        IntelTranslationLayer."""
        return cls._bits_per_register

def bits_per_register ( cls ) -> int : return cls . _bits_per_register


Returns the bits of a value between highbit and lowbit inclusive .

def _mask(value: int, high_bit: int, low_bit: int) -> int:
        """Returns the bits of a value between highbit and lowbit inclusive."""
        high_mask = (1 << (high_bit + 1)) - 1
        low_mask = (1 << low_bit) - 1
        mask = (high_mask ^ low_mask)
        # print(high_bit, low_bit, bin(mask), bin(value))
        return value & mask


Returns whether a particular page is valid based on its entry .

def _page_is_valid(entry: int) -> bool:
        """Returns whether a particular page is valid based on its entry."""
        return bool(entry & 1)

def _page_is_valid ( entry : int ) -> bool : return bool ( entry & 1 )


Translates a specific offset based on paging tables .

def _translate(self, offset: int) -> Tuple[int, int, str]:
        """Translates a specific offset based on paging tables.

        Returns the translated offset, the contiguous pagesize that the
        translated address lives in and the layer_name that the address
        lives in
        """
        entry, position = self._translate_entry(offset)

        # Now we're done
        if not self._page_is_valid(entry):
            raise exceptions.PagedInvalidAddressException(self.name, offset, position + 1, entry,
                                                          "Page Fault at entry {} in page entry".format(hex(entry)))
        page = self._mask(entry, self._maxphyaddr - 1, position + 1) | self._mask(offset, position, 0)

        return page, 1 << (position + 1), self._base_layer

def
_translate
(
self
,
offset
:
int
)
->
Tuple
[
int
,
int
,
str
]
:
entry
,
position
=
self
.
_translate_entry
(
offset
)
# Now we're done
if
not
self
.
_page_is_valid
(
entry
)
:
raise
exceptions
.
PagedInvalidAddressException
(
self
.
name
,
offset
,
position
+
1
,
entry
,
"Page Fault at entry {} in page entry"
.
format
(
hex
(
entry
)
)
)
page
=
self
.
_mask
(
entry
,
self
.
_maxphyaddr
1
,
position
+
1
)
|
self
.
_mask
(
offset
,
position
,
0
)
return
page
,
1
<<
(
position
+
1
)
,
self
.
_base_layer


def _translate_entry(self, offset):
        """Translates a specific offset based on paging tables.

        Returns the translated entry value
        """
        # Setup the entry and how far we are through the offset
        # Position maintains the number of bits left to process
        # We or with 0x1 to ensure our page_map_offset is always valid
        position = self._initial_position
        entry = self._initial_entry

        # Run through the offset in various chunks
        for (name, size, large_page) in self._structure:
            # Check we're valid
            if not self._page_is_valid(entry):
                raise exceptions.PagedInvalidAddressException(self.name, offset, position + 1, entry,
                                                              "Page Fault at entry " + hex(entry) + " in table " + name)
            # Check if we're a large page
            if large_page and (entry & (1 << 7)):
                # We're a large page, the rest is finished below
                # If we want to implement PSE-36, it would need to be done here
                break
            # Figure out how much of the offset we should be using
            start = position
            position -= size
            index = self._mask(offset, start, position + 1) >> (position + 1)

            # Grab the base address of the table we'll be getting the next entry from
            base_address = self._mask(entry, self._maxphyaddr - 1, size + self._index_shift)

            table = self._get_valid_table(base_address)
            if table is None:
                raise exceptions.PagedInvalidAddressException(self.name, offset, position + 1, entry,
                                                              "Page Fault at entry " + hex(entry) + " in table " + name)

            # Read the data for the next entry
            entry_data = table[(index << self._index_shift):(index << self._index_shift) + self._entry_size]

            # Read out the new entry from memory
            entry, = struct.unpack(self._entry_format, entry_data)

        return entry, position


Extracts the table validates it and returns it if it s valid .

def _get_valid_table(self, base_address: int) -> Optional[bytes]:
        """Extracts the table, validates it and returns it if it's valid."""
        table = self._context.layers.read(self._base_layer, base_address, self.page_size)

        # If the table is entirely duplicates, then mark the whole table as bad
        if (table == table[:self._entry_size] * self._entry_number):
            return None
        return table


Returns whether the address offset can be translated to a valid address .

def is_valid(self, offset: int, length: int = 1) -> bool:
        """Returns whether the address offset can be translated to a valid
        address."""
        try:
            # TODO: Consider reimplementing this, since calls to mapping can call is_valid
            return all([
                self._context.layers[layer].is_valid(mapped_offset)
                for _, _, mapped_offset, _, layer in self.mapping(offset, length)
            ])
        except exceptions.InvalidAddressException:
            return False


Returns a sorted iterable of ( offset sublength mapped_offset mapped_length layer ) mappings .

def mapping(self,
                offset: int,
                length: int,
                ignore_errors: bool = False) -> Iterable[Tuple[int, int, int, int, str]]:
        """Returns a sorted iterable of (offset, sublength, mapped_offset, mapped_length, layer)
        mappings.

        This allows translation layers to provide maps of contiguous
        regions in one layer
        """
        if length == 0:
            try:
                mapped_offset, _, layer_name = self._translate(offset)
                if not self._context.layers[layer_name].is_valid(mapped_offset):
                    raise exceptions.InvalidAddressException(layer_name = layer_name, invalid_address = mapped_offset)
            except exceptions.InvalidAddressException:
                if not ignore_errors:
                    raise
                return
            yield offset, length, mapped_offset, length, layer_name
            return
        while length > 0:
            try:
                chunk_offset, page_size, layer_name = self._translate(offset)
                chunk_size = min(page_size - (chunk_offset % page_size), length)
                if not self._context.layers[layer_name].is_valid(chunk_offset, chunk_size):
                    raise exceptions.InvalidAddressException(layer_name = layer_name, invalid_address = chunk_offset)
            except (exceptions.PagedInvalidAddressException, exceptions.InvalidAddressException) as excp:
                if not ignore_errors:
                    raise
                # We can jump more if we know where the page fault failed
                if isinstance(excp, exceptions.PagedInvalidAddressException):
                    mask = (1 << excp.invalid_bits) - 1
                else:
                    mask = (1 << self._page_size_in_bits) - 1
                length_diff = (mask + 1 - (offset & mask))
                length -= length_diff
                offset += length_diff
            else:
                yield offset, chunk_size, chunk_offset, chunk_size, layer_name
                length -= chunk_size
                offset += chunk_size


Returns a list of the lower layer names that this layer is dependent upon .

def dependencies(self) -> List[str]:
        """Returns a list of the lower layer names that this layer is dependent
        upon."""
        return [self._base_layer] + self._swap_layers

def dependencies ( self ) -> List [ str ] : return [ self . _base_layer ] + self . _swap_layers


def _page_is_valid(entry: int) -> bool:
        """Returns whether a particular page is valid based on its entry.

        Windows uses additional "available" bits to store flags
        These flags allow windows to determine whether a page is still valid

        Bit 11 is the transition flag, and Bit 10 is the prototype flag

        For more information, see Windows Internals (6th Ed, Part 2, pages 268-269)
        """
        return bool((entry & 1) or ((entry & 1 << 11) and not entry & 1 << 10))

def _page_is_valid ( entry : int ) -> bool : return bool ( ( entry & 1 ) or ( ( entry & 1 << 11 ) and not entry & 1 << 10 ) )


Reads an offset for length bytes and returns bytes ( not str ) of length size .

def read(self, offset: int, length: int, pad: bool = False) -> bytes:
        """Reads an offset for length bytes and returns 'bytes' (not 'str') of
        length size."""
        current_offset = offset
        output = []  # type: List[bytes]
        for (offset, _, mapped_offset, mapped_length, layer) in self.mapping(offset, length, ignore_errors = pad):
            if not pad and offset > current_offset:
                raise exceptions.InvalidAddressException(
                    self.name, current_offset, "Layer {} cannot map offset: {}".format(self.name, current_offset))
            elif offset > current_offset:
                output += [b"\x00" * (offset - current_offset)]
                current_offset = offset
            elif offset < current_offset:
                raise exceptions.LayerException(self.name, "Mapping returned an overlapping element")
            if mapped_length > 0:
                output += [self._context.layers.read(layer, mapped_offset, mapped_length, pad)]
            current_offset += mapped_length
        recovered_data = b"".join(output)
        return recovered_data + b"\x00" * (length - len(recovered_data))


Writes a value at offset distributing the writing across any underlying mapping .

def write(self, offset: int, value: bytes) -> None:
        """Writes a value at offset, distributing the writing across any
        underlying mapping."""
        current_offset = offset
        length = len(value)
        for (offset, _, mapped_offset, length, layer) in self.mapping(offset, length):
            if offset > current_offset:
                raise exceptions.InvalidAddressException(
                    self.name, current_offset, "Layer {} cannot map offset: {}".format(self.name, current_offset))
            elif offset < current_offset:
                raise exceptions.LayerException(self.name, "Mapping returned an overlapping element")
            self._context.layers.write(layer, mapped_offset, value[:length])
            value = value[length:]
            current_offset += length


Really horrible solution for ensuring files aren t left open

def cascadeCloseFile(new_fp, original_fp):
    """Really horrible solution for ensuring files aren't left open

    Args:
        new_fp: The file pointer constructed based on the original file pointer
        original_fp: The original file pointer that should be closed when the new file pointer is closed, but isn't
    """

    def close():
        original_fp.close()
        return new_fp.__class__.close(new_fp)

    new_fp.close = close
    return new_fp

def cascadeCloseFile ( new_fp , original_fp ) : def close ( ) : original_fp . close ( ) return new_fp . __class__ . close ( new_fp ) new_fp . close = close return new_fp


def __init__(self,
                 progress_callback: Optional[constants.ProgressCallback] = None,
                 context: Optional[ssl.SSLContext] = None) -> None:
        """Creates a resource accessor.

        Note: context is an SSL context, not a volatility context
        """
        self._progress_callback = progress_callback
        self._context = context
        self._handlers = list(framework.class_subclasses(urllib.request.BaseHandler))
        if self.list_handlers:
            vollog.log(constants.LOGLEVEL_VVV,
                       "Available URL handlers: {}".format(", ".join([x.__name__ for x in self._handlers])))
            self.__class__.list_handlers = False

def
__init__
(
self
,
progress_callback
:
Optional
[
constants
.
ProgressCallback
]
=
,
context
:
Optional
[
ssl
.
SSLContext
]
=
)
->
:
self
.
_progress_callback
=
progress_callback
self
.
_context
=
context
self
.
_handlers
=
list
(
framework
.
class_subclasses
(
urllib
.
request
.
BaseHandler
)
)
if
self
.
list_handlers
:
vollog
.
log
(
constants
.
LOGLEVEL_VVV
,
"Available URL handlers: {}"
.
format
(
", "
.
join
(
[
x
.
__name__
for
x
in
self
.
_handlers
]
)
)
)
self
.
__class__
.
list_handlers
=
False


def uses_cache(self, url: str) -> bool:
        """Determines whether a URLs contents should be cached"""
        parsed_url = urllib.parse.urlparse(url)

        return not parsed_url.scheme in ['file', 'jar']

def uses_cache ( self , url : str ) -> bool : parsed_url = urllib . parse . urlparse ( url ) return not parsed_url . scheme in [ 'file' , 'jar' ]


Returns a file - like object for a particular URL opened in mode .

def open(self, url: str, mode: str = "rb") -> Any:
        """Returns a file-like object for a particular URL opened in mode.

        If the file is remote, it will be downloaded and locally cached
        """
        urllib.request.install_opener(urllib.request.build_opener(*self._handlers))

        try:
            fp = urllib.request.urlopen(url, context = self._context)
        except error.URLError as excp:
            if excp.args:
                # TODO: As of python3.7 this can be removed
                unverified_retrieval = (hasattr(ssl, "SSLCertVerificationError") and isinstance(
                    excp.args[0], ssl.SSLCertVerificationError)) or (isinstance(excp.args[0], ssl.SSLError) and
                                                                     excp.args[0].reason == "CERTIFICATE_VERIFY_FAILED")
                if unverified_retrieval:
                    vollog.warning("SSL certificate verification failed: attempting UNVERIFIED retrieval")
                    non_verifying_ctx = ssl.SSLContext()
                    non_verifying_ctx.check_hostname = False
                    non_verifying_ctx.verify_mode = ssl.CERT_NONE
                    fp = urllib.request.urlopen(url, context = non_verifying_ctx)
                else:
                    raise excp
            else:
                raise excp

        with contextlib.closing(fp) as fp:
            # Cache the file locally

            if not self.uses_cache(url):
                # ZipExtFiles (files in zips) cannot seek, so must be cached in order to use and/or decompress
                curfile = urllib.request.urlopen(url, context = self._context)
            else:
                # TODO: find a way to check if we already have this file (look at http headers?)
                block_size = 1028 * 8
                temp_filename = os.path.join(
                    constants.CACHE_PATH,
                    "data_" + hashlib.sha512(bytes(url, 'raw_unicode_escape')).hexdigest() + ".cache")

                if not os.path.exists(temp_filename):
                    vollog.debug("Caching file at: {}".format(temp_filename))

                    try:
                        content_length = fp.info().get('Content-Length', -1)
                    except AttributeError:
                        # If our fp doesn't have an info member, carry on gracefully
                        content_length = -1
                    cache_file = open(temp_filename, "wb")

                    count = 0
                    block = fp.read(block_size)
                    while block:
                        count += len(block)
                        if self._progress_callback:
                            self._progress_callback(count * 100 / max(count, int(content_length)),
                                                    "Reading file {}".format(url))
                        cache_file.write(block)
                        block = fp.read(block_size)
                    cache_file.close()
                # Re-open the cache with a different mode
                curfile = open(temp_filename, mode = "rb")

        # Determine whether the file is a particular type of file, and if so, open it as such
        IMPORTED_MAGIC = False
        if HAS_MAGIC:
            stop = False
            while not stop:
                detected = None
                try:
                    # Detect the content
                    detected = magic.detect_from_fobj(curfile)
                    IMPORTED_MAGIC = True
                    # This is because python-magic and file provide a magic module
                    # Only file's python has magic.detect_from_fobj
                except (AttributeError, IOError):
                    pass

                if detected:
                    if detected.mime_type == 'application/x-xz':
                        curfile = cascadeCloseFile(lzma.LZMAFile(curfile, mode), curfile)
                    elif detected.mime_type == 'application/x-bzip2':
                        curfile = cascadeCloseFile(bz2.BZ2File(curfile, mode), curfile)
                    elif detected.mime_type == 'application/x-gzip':
                        curfile = cascadeCloseFile(gzip.GzipFile(fileobj = curfile, mode = mode), curfile)
                    if detected.mime_type in ['application/x-xz', 'application/x-bzip2', 'application/x-gzip']:
                        # Read and rewind to ensure we're inside any compressed file layers
                        curfile.read(1)
                        curfile.seek(0)
                    else:
                        stop = True
                else:
                    stop = True

        if not IMPORTED_MAGIC:
            # Somewhat of a hack, but prevents a hard dependency on the magic module
            parsed_url = urllib.parse.urlparse(url)
            url_path = parsed_url.path
            stop = False
            while not stop:
                url_path_split = url_path.split(".")
                url_path_list, extension = url_path_split[:-1], url_path_split[-1]
                url_path = ".".join(url_path_list)
                if extension == "xz":
                    curfile = cascadeCloseFile(lzma.LZMAFile(curfile, mode), curfile)
                elif extension == "bz2":
                    curfile = cascadeCloseFile(bz2.BZ2File(curfile, mode), curfile)
                elif extension == "gz":
                    curfile = cascadeCloseFile(gzip.GzipFile(fileobj = curfile, mode = mode), curfile)
                else:
                    stop = True

        # Fallback in case the file doesn't exist
        if curfile is None:
            raise ValueError("URL does not reference an openable file")
        return curfile


Handles the request if it s the jar scheme .

def default_open(req: urllib.request.Request) -> Optional[Any]:
        """Handles the request if it's the jar scheme."""
        if req.type == 'jar':
            subscheme, remainder = req.full_url.split(":")[1], ":".join(req.full_url.split(":")[2:])
            if subscheme != 'file':
                vollog.log(constants.LOGLEVEL_VVV, "Unsupported jar subscheme {}".format(subscheme))
                return None

            zipsplit = remainder.split("!")
            if len(zipsplit) != 2:
                vollog.log(constants.LOGLEVEL_VVV,
                           "Path did not contain exactly one fragment indicator: {}".format(remainder))
                return None

            zippath, filepath = zipsplit
            return zipfile.ZipFile(zippath).open(filepath)
        return None


def _read_configuration(self, base_layer: interfaces.layers.DataLayerInterface, name: str) -> Any:
        """Reads the JSON configuration from the end of the file"""
        chunk_size = 0x4096
        data = b''
        for i in range(base_layer.maximum_address, base_layer.minimum_address, -chunk_size):
            if i != base_layer.maximum_address:
                data = base_layer.read(i, chunk_size) + data
                if b'\x00' in data:
                    start = data.rfind(b'\x00')
                    data = data[data.find(b'{', start):]
                    return json.loads(data)
        raise exceptions.LayerException(name, "Could not load JSON configuration from the end of the file")


def _get_ram_segments(self, index: int, page_size: int) -> Tuple[List[Tuple[int, int, int, int]], int]:
        """Recovers the new index and any sections of memory from a ram section"""
        done = None
        segments = []

        base_layer = self.context.layers[self._base_layer]

        while not done:
            addr = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long long',
                                       offset = index,
                                       layer_name = self._base_layer)
            flags = addr & (page_size - 1)
            page_size_bits = int(math.log(page_size, 2))
            addr = (addr >> page_size_bits) << page_size_bits
            index += 8

            if flags & self.SEGMENT_FLAG_MEM_SIZE:
                namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char',
                                               offset = index,
                                               layer_name = self._base_layer)
                while namelen != 0:
                    # if base_layer.read(index + 1, namelen) == b'pc.ram':
                    #     total_size = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned long long',
                    #                                       offset = index + 1 + namelen,
                    #                                       layer_name = self._base_layer)
                    index += 1 + namelen + 8
                    namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char',
                                                   offset = index,
                                                   layer_name = self._base_layer)
            if flags & (self.SEGMENT_FLAG_COMPRESS | self.SEGMENT_FLAG_PAGE):
                if not (flags & self.SEGMENT_FLAG_CONTINUE):
                    namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char',
                                                   offset = index,
                                                   layer_name = self._base_layer)
                    self._current_segment_name = base_layer.read(index + 1, namelen)
                    index += 1 + namelen
                if flags & self.SEGMENT_FLAG_COMPRESS:
                    if self._current_segment_name == b'pc.ram':
                        segments.append((addr, index, page_size, 1))
                        self._compressed.add(addr)
                    index += 1
                else:
                    if self._current_segment_name == b'pc.ram':
                        segments.append((addr, index, page_size, page_size))
                    index += page_size
            if flags & self.SEGMENT_FLAG_XBZRLE:
                raise exceptions.LayerException(self.name, "XBZRLE compression not supported")
            if flags & self.SEGMENT_FLAG_EOS:
                done = True
        return segments, index

#                                       offset = index + 1 + namelen,

#                                       layer_name = self._base_layer)


def _load_segments(self) -> None:
        """Populates the _segments variable.

        Segments must be (address, mapped address, length) and must be
        sorted by address when this method exits
        """


def is_valid(self, offset: int, length: int = 1) -> bool:
        """Returns whether the address offset can be translated to a valid
        address."""
        try:
            base_layer = self._context.layers[self._base_layer]
            return all(
                [base_layer.is_valid(mapped_offset) for _i, _i, mapped_offset, _i, _s in self.mapping(offset, length)])
        except exceptions.InvalidAddressException:
            return False

def is_valid ( self , offset : int , length : int = 1 ) -> bool : try : base_layer = self . _context . layers [ self . _base_layer ] return all ( [ base_layer . is_valid ( mapped_offset ) for _i , _i , mapped_offset , _i , _s in self . mapping ( offset , length ) ] ) except exceptions . InvalidAddressException : return False


Finds the segment containing a given offset .

def _find_segment(self, offset: int, next: bool = False) -> Tuple[int, int, int, int]:
        """Finds the segment containing a given offset.

        Returns the segment tuple (offset, mapped_offset, length, mapped_length)
        """

        if not self._segments:
            self._load_segments()

        # Find rightmost value less than or equal to x
        i = bisect_right(self._segments, (offset, self.context.layers[self._base_layer].maximum_address))
        if i and not next:
            segment = self._segments[i - 1]
            if segment[0] <= offset < segment[0] + segment[2]:
                return segment
        if next:
            if i < len(self._segments):
                return self._segments[i]
        raise exceptions.InvalidAddressException(self.name, offset, "Invalid address at {:0x}".format(offset))


Returns a sorted iterable of ( offset length mapped_offset mapped_length layer ) mappings .

def mapping(self,
                offset: int,
                length: int,
                ignore_errors: bool = False) -> Iterable[Tuple[int, int, int, int, str]]:
        """Returns a sorted iterable of (offset, length, mapped_offset, mapped_length, layer)
        mappings."""
        done = False
        current_offset = offset
        while not done:
            try:
                # Search for the appropriate segment that contains the current_offset
                logical_offset, mapped_offset, size, mapped_size = self._find_segment(current_offset)
                # If it starts before the current_offset, bring the lower edge up to the right place
                if current_offset > logical_offset:
                    difference = current_offset - logical_offset
                    logical_offset += difference
                    mapped_offset += difference
                    size -= difference
            except exceptions.InvalidAddressException:
                if not ignore_errors:
                    # If we're not ignoring errors, raise the invalid address exception
                    raise
                try:
                    # Find the next valid segment after our current_offset
                    logical_offset, mapped_offset, size, mapped_size = self._find_segment(current_offset, next = True)
                    # We know that the logical_offset must be greater than current_offset so skip to that value
                    current_offset = logical_offset
                    # If it starts too late then we're done
                    if logical_offset > offset + length:
                        return
                except exceptions.InvalidAddressException:
                    return
            # Crop it to the amount we need left
            chunk_size = min(size, length + offset - logical_offset)
            yield logical_offset, chunk_size, mapped_offset, chunk_size, self._base_layer
            current_offset += chunk_size
            # Terminate if we've gone (or reached) our required limit
            if current_offset >= offset + length:
                done = True


Returns a list of the lower layers that this layer is dependent upon .

def dependencies(self) -> List[str]:
        """Returns a list of the lower layers that this layer is dependent
        upon."""
        return [self._base_layer]

def dependencies ( self ) -> List [ str ] : return [ self . _base_layer ]


Return a mask that allows for the volatile bit to be set .

def address_mask(self) -> int:
        """Return a mask that allows for the volatile bit to be set."""
        return super().address_mask | 0x80000000

def address_mask ( self ) -> int : return super ( ) . address_mask | 0x80000000


Returns the offset for the root cell in this hive .

def root_cell_offset(self) -> int:
        """Returns the offset for the root cell in this hive."""
        try:
            if self._base_block.Signature.cast("string", max_length = 4, encoding = "latin-1") == 'regf':
                return self._base_block.RootCell
        except InvalidAddressException:
            pass
        return 0x20

def root_cell_offset ( self ) -> int : try : if self . _base_block . Signature . cast ( "string" , max_length = 4 , encoding = "latin-1" ) == 'regf' : return self . _base_block . RootCell except InvalidAddressException : pass return 0x20


Returns the appropriate Cell value for a cell offset .

def get_cell(self, cell_offset: int) -> 'objects.StructType':
        """Returns the appropriate Cell value for a cell offset."""
        # This would be an _HCELL containing CELL_DATA, but to save time we skip the size of the HCELL
        cell = self._context.object(object_type = self._table_name + constants.BANG + "_CELL_DATA",
                                    offset = cell_offset + 4,
                                    layer_name = self.name)
        return cell


Returns the appropriate Node interpreted from the Cell based on its Signature .

def get_node(self, cell_offset: int) -> 'objects.StructType':
        """Returns the appropriate Node, interpreted from the Cell based on its
        Signature."""
        cell = self.get_cell(cell_offset)
        signature = cell.cast('string', max_length = 2, encoding = 'latin-1')
        if signature == 'nk':
            return cell.u.KeyNode
        elif signature == 'sk':
            return cell.u.KeySecurity
        elif signature == 'vk':
            return cell.u.KeyValue
        elif signature == 'db':
            # Big Data
            return cell.u.ValueData
        elif signature == 'lf' or signature == 'lh' or signature == 'ri':
            # Fast Leaf, Hash Leaf, Index Root
            return cell.u.KeyIndex
        else:
            # It doesn't matter that we use KeyNode, we're just after the first two bytes
            vollog.debug("Unknown Signature {} (0x{:x}) at offset {}".format(signature, cell.u.KeyNode.Signature,
                                                                             cell_offset))
            return cell


Gets a specific registry key by key path .

def get_key(self, key: str, return_list: bool = False) -> Union[List[objects.StructType], objects.StructType]:
        """Gets a specific registry key by key path.

        return_list specifies whether the return result will be a single
        node (default) or a list of nodes from root to the current node
        (if return_list is true).
        """
        node_key = [self.get_node(self.root_cell_offset)]
        if key.endswith("\\"):
            key = key[:-1]
        key_array = key.split('\\')
        found_key = []  # type: List[str]
        while key_array and node_key:
            subkeys = node_key[-1].get_subkeys()
            for subkey in subkeys:
                # registry keys are not case sensitive so compare lowercase
                # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724946(v=vs.85).aspx
                if subkey.get_name().lower() == key_array[0].lower():
                    node_key = node_key + [subkey]
                    found_key, key_array = found_key + [key_array[0]], key_array[1:]
                    break
            else:
                node_key = []
        if not node_key:
            raise KeyError("Key {} not found under {}".format(key_array[0], '\\'.join(found_key)))
        if return_list:
            return node_key
        return node_key[-1]


Applies a callable ( visitor ) to all nodes within the registry tree from a given node .

def visit_nodes(self,
                    visitor: Callable[[objects.StructType], None],
                    node: Optional[objects.StructType] = None) -> None:
        """Applies a callable (visitor) to all nodes within the registry tree
        from a given node."""
        if not node:
            node = self.get_node(self.root_cell_offset)
        visitor(node)
        for node in node.get_subkeys():
            self.visit_nodes(visitor, node)

def visit_nodes ( self , visitor : Callable [ [ objects . StructType ] , None ] , node : Optional [ objects . StructType ] = None ) -> None : if not node : node = self . get_node ( self . root_cell_offset ) visitor ( node ) for node in node . get_subkeys ( ) : self . visit_nodes ( visitor , node )


Returns the bits of a value between highbit and lowbit inclusive .

def _mask(value: int, high_bit: int, low_bit: int) -> int:
        """Returns the bits of a value between highbit and lowbit inclusive."""
        high_mask = (2 ** (high_bit + 1)) - 1
        low_mask = (2 ** low_bit) - 1
        mask = (high_mask ^ low_mask)
        # print(high_bit, low_bit, bin(mask), bin(value))
        return value & mask


Translates a single cell index to a cell memory offset and the suboffset within it .

def _translate(self, offset: int) -> int:
        """Translates a single cell index to a cell memory offset and the
        suboffset within it."""

        # Ignore the volatile bit when determining maxaddr validity
        volatile = self._mask(offset, 31, 31) >> 31
        if offset & 0x7fffffff > self._get_hive_maxaddr(volatile):
            raise RegistryInvalidIndex(self.name, "Mapping request for value greater than maxaddr")

        storage = self.hive.Storage[volatile]
        dir_index = self._mask(offset, 30, 21) >> 21
        table_index = self._mask(offset, 20, 12) >> 12
        suboffset = self._mask(offset, 11, 0) >> 0

        table = storage.Map.Directory[dir_index]
        entry = table.Table[table_index]
        return entry.get_block_offset() + suboffset


Returns a list of layer names that this layer translates onto .

def dependencies(self) -> List[str]:
        """Returns a list of layer names that this layer translates onto."""
        return [self.config['base_layer']]

def dependencies ( self ) -> List [ str ] : return [ self . config [ 'base_layer' ] ]


Returns a boolean based on whether the offset is valid or not .

def is_valid(self, offset: int, length: int = 1) -> bool:
        """Returns a boolean based on whether the offset is valid or not."""
        try:
            # Pass this to the lower layers for now
            return all([
                self.context.layers[layer].is_valid(offset, length)
                for (_, _, offset, length, layer) in self.mapping(offset, length)
            ])
        except exceptions.InvalidAddressException:
            return False

def
is_valid
(
self
,
offset
:
int
,
length
:
int
=
1
)
->
bool
:
try
:
# Pass this to the lower layers for now
return
all
(
[
self
.
context
.
layers
[
layer
]
.
is_valid
(
offset
,
length
)
for
(
_
,
_
,
offset
,
length
,
layer
)
in
self
.
mapping
(
offset
,
length
)
]
)
except
exceptions
.
InvalidAddressException
:
return
False


Loads up the segments from the meta_layer .

def _load_segments(self) -> None:
        """Loads up the segments from the meta_layer."""
        header = self.context.object(self._crash_table_name + constants.BANG + self.dump_header_name,
                                     offset = 0,
                                     layer_name = self._base_layer)

        segments = []

        offset = self.headerpages
        header.PhysicalMemoryBlockBuffer.Run.count = header.PhysicalMemoryBlockBuffer.NumberOfRuns
        for x in header.PhysicalMemoryBlockBuffer.Run:
            segments.append((x.BasePage * 0x1000, offset * 0x1000, x.PageCount * 0x1000, x.PageCount * 0x1000))
            # print("Segments {:x} {:x} {:x}".format(x.BasePage * 0x1000,
            #                  offset * 0x1000,
            #                  x.PageCount * 0x1000))
            offset += x.PageCount

        if len(segments) == 0:
            raise WindowsCrashDumpFormatException(self.name, "No Crash segments defined in {}".format(self._base_layer))

        self._segments = segments

#                  x.PageCount * 0x1000))


def _load_segments(self) -> None:
        """Loads up the segments from the meta_layer."""

        segments = []

        summary_header = self.context.object(self._crash_table_name + constants.BANG + "_SUMMARY_DUMP64",
                                             offset = 0x2000,
                                             layer_name = self._base_layer)

        if self.dump_type == 0x1:
            header = self.context.object(self._crash_table_name + constants.BANG + self.dump_header_name,
                                         offset = 0,
                                         layer_name = self._base_layer)

            offset = self.headerpages
            header.PhysicalMemoryBlockBuffer.Run.count = header.PhysicalMemoryBlockBuffer.NumberOfRuns
            for x in header.PhysicalMemoryBlockBuffer.Run:
                segments.append((x.BasePage * 0x1000, offset * 0x1000, x.PageCount * 0x1000, x.PageCount * 0x1000))
                offset += x.PageCount

        elif self.dump_type == 0x05:
            summary_header.BufferLong.count = (summary_header.BitmapSize + 31) // 32
            previous_bit = 0
            start_position = 0
            # We cast as an int because we don't want to carry the context around with us for infinite loop reasons
            mapped_offset = int(summary_header.HeaderSize)
            current_word = None
            for bit_position in range(len(summary_header.BufferLong) * 32):
                if (bit_position % 32) == 0:
                    current_word = summary_header.BufferLong[bit_position // 32]
                current_bit = (current_word >> (bit_position % 32)) & 1
                if current_bit != previous_bit:
                    if previous_bit == 0:
                        # Start
                        start_position = bit_position
                    else:
                        # Finish
                        length = (bit_position - start_position) * 0x1000
                        segments.append((start_position * 0x1000, mapped_offset, length, length))
                        mapped_offset += length

                # Finish it off
                if bit_position == (len(summary_header.BufferLong) * 32) - 1 and current_bit == 1:
                    length = (bit_position - start_position) * 0x1000
                    segments.append((start_position * 0x1000, mapped_offset, length, length))
                    mapped_offset += length

                previous_bit = current_bit
        else:
            vollog.log(constants.LOGLEVEL_VVVV, "unsupported dump format 0x{:x}".format(self.dump_type))
            raise WindowsCrashDumpFormatException(self.name, "unsupported dump format 0x{:x}".format(self.dump_type))

        if len(segments) == 0:
            raise WindowsCrashDumpFormatException(self.name, "No Crash segments defined in {}".format(self._base_layer))

        self._segments = segments


Returns the largest available address in the space .

def maximum_address(self) -> int:
        """Returns the largest available address in the space."""
        return len(self._buffer) - 1

def maximum_address ( self ) -> int : return len ( self . _buffer ) - 1


Returns the smallest available address in the space .

def minimum_address(self) -> int:
        """Returns the smallest available address in the space."""
        return 0

def minimum_address ( self ) -> int : return 0


Returns whether the offset is valid or not .

def is_valid(self, offset: int, length: int = 1) -> bool:
        """Returns whether the offset is valid or not."""
        return bool(self.minimum_address <= offset <= self.maximum_address
                    and self.minimum_address <= offset + length - 1 <= self.maximum_address)

def is_valid ( self , offset : int , length : int = 1 ) -> bool : return bool ( self . minimum_address <= offset <= self . maximum_address and self . minimum_address <= offset + length - 1 <= self . maximum_address )


def read(self, address: int, length: int, pad: bool = False) -> bytes:
        """Reads the data from the buffer."""
        if not self.is_valid(address, length):
            invalid_address = address
            if self.minimum_address < address <= self.maximum_address:
                invalid_address = self.maximum_address + 1
            raise exceptions.InvalidAddressException(self.name, invalid_address,
                                                     "Offset outside of the buffer boundaries")
        return self._buffer[address:address + length]


def write(self, address: int, data: bytes):
        """Writes the data from to the buffer."""
        self._buffer = self._buffer[:address] + data + self._buffer[address + len(data):]

def write ( self , address : int , data : bytes ) : self . _buffer = self . _buffer [ : address ] + data + self . _buffer [ address + len ( data ) : ]


Returns the location on which this Layer abstracts .

def location(self) -> str:
        """Returns the location on which this Layer abstracts."""
        return self._location

def location ( self ) -> str : return self . _location