[ Modified ] Converts a binary SID to its string representation . https : // msdn . microsoft . com / en - us / library / windows / desktop / aa379597 . aspx The byte representation of an SID is as follows : Offset Length Description 00 01 revision 01 01 sub - authority count 02 06 authority ( big endian ) 08 04 subauthority #1 ( little endian ) 0b 04 subauthority #2 ( little endian ) ... Args : sid : A byte array . Returns : SID in string form . Raises : ValueError : If the binary SID is malformed .

def SID_converter(self, sid):
		#Original form Source: https://github.com/google/grr/blob/master/grr/parsers/wmi_parser.py
		"""[Modified] Converts a binary SID to its string representation.
		https://msdn.microsoft.com/en-us/library/windows/desktop/aa379597.aspx
		The byte representation of an SID is as follows:
			Offset  Length  Description
			00      01      revision
			01      01      sub-authority count
			02      06      authority (big endian)
			08      04      subauthority #1 (little endian)
			0b      04      subauthority #2 (little endian)
			...
		Args:
			sid: A byte array.
		Returns:
			SID in string form.
		Raises:
			ValueError: If the binary SID is malformed.
		"""
		if sid is None:
			return 'None'

		if not sid:
			return 'None'

		sid_components = [ ord(sid[0]) ]
		# Now decode the 48-byte portion

		if len(sid) >= 8:
			subauthority_count = ord(sid[1])
			identifier_authority = struct.unpack(">H", sid[2:4])[0]
			identifier_authority <<= 32
			identifier_authority |= struct.unpack(">L", sid[4:8])[0]
			sid_components.append(identifier_authority)
			start = 8
			#print subauthority_count.encode('hex')
			for i in range( subauthority_count ):
				authority = sid[start:start + 4]
				if not authority:
					break
				if len(authority) < 4:
					raise ValueError("In binary SID '%s', component %d has been truncated. "
									"Expected 4 bytes, found %d: (%s)",
									",".join([str(ord(c)) for c in sid]), i,
									len(authority), authority)
				sid_components.append(struct.unpack("<L", authority)[0])
				start += 4

		return "S-%s" % ("-".join([str(x) for x in sid_components]))


Convert a Windows 64 Hex Big - Endian value to a date

def from_win_64_hex(dateAndTime):
    """Convert a Windows 64 Hex Big-Endian value to a date"""
    base10_microseconds = int(dateAndTime, 16) / 10

    try:
        dateAndTime = datetime(1601, 1, 1) + timedelta(microseconds=base10_microseconds)

    except:
        dateAndTime = datetime(1601, 1, 1)

    return dateAndTime.strftime('%Y-%m-%d %H:%M:%S.%f')

def
from_win_64_hex
(
dateAndTime
)
:
base10_microseconds
=
int
(
dateAndTime
,
16
)
/
10
try
:
dateAndTime
=
datetime
(
1601
,
1
,
1
)
+
timedelta
(
microseconds
=
base10_microseconds
)
except
:
dateAndTime
=
datetime
(
1601
,
1
,
1
)
return
dateAndTime
.
strftime
(
'%Y-%m-%d %H:%M:%S.%f'
)


Return a hexdump output string of the given buffer .

def hexdump(buf, pcap=False):
    """Return a hexdump output string of the given buffer."""
    total = len(buf)
    file = io.BytesIO(buf)
    res = []
    hextime = time.time()

    for length in range(0, len(buf), 16):
        i = file.tell()

        if (time.time() - hextime) > 1 and not args.hex_dump:
            progress(i, total, status='Dumping Hex')

        data = file.read(16)
        hexa = ' '.join(['{:02x}'.format(i) for i in data])
        line = ''.join([31 < i < 127 and chr(i) or '.' for i in data])
        res.append('  {:08x}  {:47}  {}'.format(length, hexa, line))

    if (time.time() - hextime) > 1 and not args.hex_dump:
        print('\n')

    if pcap:
        packet.write('\n'.join(res))
        packet.write('\n\n')

        return '\n'.join(res)

    else:
        return '\n'.join(res)+'\n\n'

def
hexdump
(
buf
,
pcap
=
False
)
:
total
=
len
(
buf
)
file
=
io
.
BytesIO
(
buf
)
res
=
[
]
hextime
=
time
.
time
(
)
for
length
in
range
(
0
,
len
(
buf
)
,
16
)
:
i
=
file
.
tell
(
)
if
(
time
.
time
(
)
hextime
)
>
1
and
not
args
.
hex_dump
:
progress
(
i
,
total
,
status
=
'Dumping Hex'
)
data
=
file
.
read
(
16
)
hexa
=
' '
.
join
(
[
'{:02x}'
.
format
(
i
)
for
i
in
data
]
)
line
=
''
.
join
(
[
31
<
i
<
127
and
chr
(
i
)
or
'.'
for
i
in
data
]
)
res
.
append
(
'  {:08x}  {:47}  {}'
.
format
(
length
,
hexa
,
line
)
)
if
(
time
.
time
(
)
hextime
)
>
1
and
not
args
.
hex_dump
:
print
(
'\n'
)
if
pcap
:
packet
.
write
(
'\n'
.
join
(
res
)
)
packet
.
write
(
'\n\n'
)
return
'\n'
.
join
(
res
)
else
:
return
'\n'
.
join
(
res
)
+
'\n\n'


Translate a SID string to an account name .

def TranslateSid(sid_string):
    """Translate a SID string to an account name.

    Args:
        sid_string: a SID in string form

    Returns:
        A string with the account name if the name resolves.
        None if the name is not found.
    """
    if os.name == 'nt':
        try:
            account = LookupAccountSid(None, GetBinarySid(sid_string))
        except:
            account = None

    if account:
        if len(account[1]):
            return account[1] + '\\' + account[0]
        else:
            return account[0]

def TranslateSid ( sid_string ) : if os . name == 'nt' : try : account = LookupAccountSid ( None , GetBinarySid ( sid_string ) ) except : account = None if account : if len ( account [ 1 ] ) : return account [ 1 ] + '\\' + account [ 0 ] else : return account [ 0 ]


Custom sorting function to sort SDDL . ACE objects by trustee .

def SortAceByTrustee(x, y):
    """Custom sorting function to sort SDDL.ACE objects by trustee.

    Args:
        x: first object being compared
        y: second object being compared

    Returns:
        The results of a cmp() between the objects.
    """
    return cmp(x.trustee, y.trustee)

def SortAceByTrustee ( x , y ) : return cmp ( x . trustee , y . trustee )


Convert a hex access rights specifier to it s string equivalent .

def AccessFromHex(hex):
    """Convert a hex access rights specifier to it's string equivalent.

    Args:
        hex: The hexadecimal string to be converted

    Returns:
        A string containing the converted access rights
    """
    hex = int(hex, 16)
    rights = ""

    for spec in ACCESS_HEX.items():
        if hex & spec[0]:
            rights += spec[1]

    return rights

def AccessFromHex ( hex ) : hex = int ( hex , 16 ) rights = "" for spec in ACCESS_HEX . items ( ) : if hex & spec [ 0 ] : rights += spec [ 1 ] return rights


def __init__(self, ace_string):
        """Initializes the SDDL::ACE object.

        Args:
            ace_string: a string representing a single access control entry
            access_constants: a dictionary of access constants for translation

        Raises:
            InvalidAceStringError: If the ace string provided doesn't appear to be
                valid.
        """
        self.ace_string = ace_string
        self.flags = []
        self.perms = []
        fields = ace_string.split(';')

        if len(fields) != 6:
            raise InvalidAceStringError

        if (ACE_TYPE[fields[0]]):
            self.ace_type = ACE_TYPE[fields[0]]
        else:
            self.ace_type = fields[0]

        for flag in re.findall(re_const, fields[1]):
            if ACE_FLAGS[flag]:
                self.flags.append(ACE_FLAGS[flag])
            else:
                self.flags.append(flag)

        if fields[2][0:2] == '0x':    # If specified in hex
            fields[2] = AccessFromHex(fields[2])

        for perm in re.findall(re_const, fields[2]):
            if ACCESS[perm]:
                self.perms.append(ACCESS[perm])
            else:
                self.perms.append(perm)

        self.perms.sort()
        self.object_type = fields[3]
        self.inherited_type = fields[4]
        self.sid = fields[5]
        self.trustee = None

        if TRUSTEE.__contains__(fields[5]):
            self.trustee = TRUSTEE[fields[5]]

        if os.name == 'nt':
            if not self.trustee:
                self.trustee = TranslateSid(fields[5])

        if not self.trustee:
            self.trustee = 'Unknown or invalid SID.'

def
__init__
(
self
,
ace_string
)
:
self
.
ace_string
=
ace_string
self
.
flags
=
[
]
self
.
perms
=
[
]
fields
=
ace_string
.
split
(
';'
)
if
len
(
fields
)
!=
6
:
raise
InvalidAceStringError
if
(
ACE_TYPE
[
fields
[
0
]
]
)
:
self
.
ace_type
=
ACE_TYPE
[
fields
[
0
]
]
else
:
self
.
ace_type
=
fields
[
0
]
for
flag
in
re
.
findall
(
re_const
,
fields
[
1
]
)
:
if
ACE_FLAGS
[
flag
]
:
self
.
flags
.
append
(
ACE_FLAGS
[
flag
]
)
else
:
self
.
flags
.
append
(
flag
)
if
fields
[
2
]
[
0
:
2
]
==
'0x'
:
# If specified in hex
fields
[
2
]
=
AccessFromHex
(
fields
[
2
]
)
for
perm
in
re
.
findall
(
re_const
,
fields
[
2
]
)
:
if
ACCESS
[
perm
]
:
self
.
perms
.
append
(
ACCESS
[
perm
]
)
else
:
self
.
perms
.
append
(
perm
)
self
.
perms
.
sort
(
)
self
.
object_type
=
fields
[
3
]
self
.
inherited_type
=
fields
[
4
]
self
.
sid
=
fields
[
5
]
self
.
trustee
=
if
TRUSTEE
.
__contains__
(
fields
[
5
]
)
:
self
.
trustee
=
TRUSTEE
[
fields
[
5
]
]
if
os
.
name
==
'nt'
:
if
not
self
.
trustee
:
self
.
trustee
=
TranslateSid
(
fields
[
5
]
)
if
not
self
.
trustee
:
self
.
trustee
=
'Unknown or invalid SID.'


def __init__(self, sddl_string, target=None):
        """Initializes the SDDL object.

        Args:
            input_string: The SDDL string representation of the ACL
            target: Some values of the SDDL string change their meaning depending
                on the type of object they describe.
                Note:    The only supported type right now is 'service'

        Raises:
            SDDLInvalidStringError: if the string doesn't appear to be valid
        """
        self.target = target
        self.sddl_string = sddl_string
        self.sddl_type = None
        self.sddl_dacl = None
        self.sddl_sacl = None
        self.acl = []
        self.dacl = []
        self.sacl = []
        self.dacl_flags = []
        self.sacl_flags = []
        sddl_owner_part = re.search(re_owner, sddl_string)
        sddl_group_part = re.search(re_group, sddl_string)
        sddl_acl_part = re.search(re_acl, sddl_string)
        sddl_dacl_part = re.search(re_dacl, sddl_string)
        sddl_sacl_part = re.search(re_sacl, sddl_string)
        self.ACCESS = ACCESS
        self.group_sid = None
        self.group_account = None
        self.owner_sid = None
        self.owner_account = None

        if self.target == 'service':
            self.ACCESS['CC'] = 'Query Configuration'
            self.ACCESS['DC'] = 'Change Configuration'
            self.ACCESS['LC'] = 'Query State'
            self.ACCESS['SW'] = 'Enumerate Dependencies'
            self.ACCESS['RP'] = 'Start'
            self.ACCESS['WP'] = 'Stop'
            self.ACCESS['DT'] = 'Pause'
            self.ACCESS['LO'] = 'Interrogate'
            self.ACCESS['CR'] = 'User Defined'
            self.ACCESS['SD'] = 'Delete'
            self.ACCESS['RC'] = 'Read the Security Descriptor'
            self.ACCESS['WD'] = 'Change Permissions'
            self.ACCESS['WO'] = 'Change Owner'

        for match in (sddl_owner_part, sddl_group_part, sddl_dacl_part, sddl_sacl_part):
            try:
                part = match.group()
                sddl_prefix = re.match(re_type, part).group()
            except:
                part = ''
                pass
#            sddl_prefix = re.match(re_type, part).group()

            if (sddl_prefix == 'D'):
                self.sddl_dacl = SDDL_TYPE[sddl_prefix]
                dacl_flags = re.search(re_acl_flags, part)
                if dacl_flags is not None:
                    for flag in re.findall(re_flags, dacl_flags.group(1)):
                        if ACL_FLAGS[flag]:
                            self.dacl_flags.append(ACL_FLAGS[flag])
                for perms in re.findall(re_perms, part):
                    self.dacl.append(ACE(perms))

            elif (sddl_prefix == 'S'):
                self.sddl_sacl = SDDL_TYPE[sddl_prefix]
                sacl_flags = re.search(re_acl_flags, part)
                if sacl_flags is not None:
                    for flag in re.findall(re_flags, sacl_flags.group(1)):
                        if ACL_FLAGS[flag]:
                            self.sacl_flags.append(ACL_FLAGS[flag])
                for perms in re.findall(re_perms, part):
                    self.sacl.append(ACE(perms))

            elif (sddl_prefix == 'G'):
                self.group_sid = re.search(re_non_acl, part).group()

                if self.group_sid in TRUSTEE:
                    self.group_account = TRUSTEE[self.group_sid]
                else:
                    if os.name == 'nt':
                        self.group_account = TranslateSid(self.group_sid)

                if not self.group_account:
                    self.group_account = 'Unknown'

            elif (sddl_prefix == 'O'):
                self.owner_sid = re.search(re_non_acl, part).group()

                if self.owner_sid in TRUSTEE:
                    self.owner_account = TRUSTEE[self.owner_sid]
                else:
                    if os.name == 'nt':
                        self.owner_account = TranslateSid(self.owner_sid)

                if not self.owner_account:
                    self.owner_account = 'Unknown'

            else:
                raise InvalidSddlStringError


A simple function for providing text - based feedback .

def __call__(self, progress: Union[int, float], description: str = None):
        """A simple function for providing text-based feedback.

        .. warning:: Only for development use.

        Args:
            progress: Percentage of progress of the current procedure
        """
        message = "\rProgress: {0: 7.2f}\t\t{1:}".format(round(progress, 2), description or '')
        message_len = len(message)
        self._max_message_len = max([self._max_message_len, message_len])

def
__call__
(
self
,
progress
:
Union
[
int
,
float
]
,
description
:
str
=
)
:
message
=
"\rProgress: {0: 7.2f}\t\t{1:}"
.
format
(
round
(
progress
,
2
)
,
description
or
''
)
message_len
=
len
(
message
)
self
.
_max_message_len
=
max
(
[
self
.
_max_message_len
,
message_len
]
)


Mock find_spec method that just checks the name this must go first .

def find_spec(fullname: str, path, target = None):
        """Mock find_spec method that just checks the name, this must go
        first."""
        if fullname.startswith("volatility.framework.plugins."):
            warning = "Please do not use the volatility.framework.plugins namespace directly, only use volatility.plugins"
            # Pyinstaller uses walk_packages to import, but needs to read the modules to figure out dependencies
            # As such, we only print the warning when directly imported rather than from within walk_packages
            if inspect.stack()[-2].function != 'walk_packages':
                raise Warning(warning)


A convenience function for constructing and running the : class : CommandLine s run method .

def main():
    """A convenience function for constructing and running the
    :class:`CommandLine`'s run method."""
    CommandLine().run()

def main ( ) : CommandLine ( ) . run ( )


def __call__(self, progress: Union[int, float], description: str = None):
        """A simple function for providing text-based feedback.

        .. warning:: Only for development use.

        Args:
            progress: Percentage of progress of the current procedure
        """
        message = "\rProgress: {0: 7.2f}\t\t{1:}".format(round(progress, 2), description or '')
        message_len = len(message)
        self._max_message_len = max([self._max_message_len, message_len])
        sys.stderr.write(message + (' ' * (self._max_message_len - message_len)) + '\r')

def
__call__
(
self
,
progress
:
Union
[
int
,
float
]
,
description
:
str
=
)
:
message
=
"\rProgress: {0: 7.2f}\t\t{1:}"
.
format
(
round
(
progress
,
2
)
,
description
or
''
)
message_len
=
len
(
message
)
self
.
_max_message_len
=
max
(
[
self
.
_max_message_len
,
message_len
]
)
sys
.
stderr
.
write
(
message
+
(
' '
*
(
self
.
_max_message_len
message_len
)
)
+
'\r'
)


Executes the command line module taking the system arguments determining the plugin to run and then running it .

def run(self):
        """Executes the command line module, taking the system arguments,
        determining the plugin to run and then running it."""

        volatility.framework.require_interface_version(2, 0, 0)

        renderers = dict([(x.name.lower(), x) for x in framework.class_subclasses(text_renderer.CLIRenderer)])

        parser = volargparse.HelpfulArgParser(add_help = False,
                                              prog = self.CLI_NAME,
                                              description = "An open-source memory forensics framework")
        parser.add_argument(
            "-h",
            "--help",
            action = "help",
            default = argparse.SUPPRESS,
            help = "Show this help message and exit, for specific plugin options use '{} <pluginname> --help'".format(
                parser.prog))
        parser.add_argument("-c",
                            "--config",
                            help = "Load the configuration from a json file",
                            default = None,
                            type = str)
        parser.add_argument("--parallelism",
                            help = "Enables parallelism (defaults to processes if no argument given)",
                            nargs = '?',
                            choices = ['processes', 'threads', 'off'],
                            const = 'processes',
                            default = None,
                            type = str)
        parser.add_argument("-e",
                            "--extend",
                            help = "Extend the configuration with a new (or changed) setting",
                            default = None,
                            action = 'append')
        parser.add_argument("-p",
                            "--plugin-dirs",
                            help = "Semi-colon separated list of paths to find plugins",
                            default = "",
                            type = str)
        parser.add_argument("-s",
                            "--symbol-dirs",
                            help = "Semi-colon separated list of paths to find symbols",
                            default = "",
                            type = str)
        parser.add_argument("-v", "--verbosity", help = "Increase output verbosity", default = 0, action = "count")
        parser.add_argument("-l",
                            "--log",
                            help = "Log output to a file as well as the console",
                            default = None,
                            type = str)
        parser.add_argument("-o",
                            "--output-dir",
                            help = "Directory in which to output any generated files",
                            default = os.getcwd(),
                            type = str)
        parser.add_argument("-q", "--quiet", help = "Remove progress feedback", default = False, action = 'store_true')
        parser.add_argument("-r",
                            "--renderer",
                            metavar = 'RENDERER',
                            help = "Determines how to render the output ({})".format(", ".join(list(renderers))),
                            default = "quick",
                            choices = list(renderers))
        parser.add_argument("-f",
                            "--file",
                            metavar = 'FILE',
                            default = None,
                            type = str,
                            help = "Shorthand for --single-location=file:// if single-location is not defined")
        parser.add_argument("--write-config",
                            help = "Write configuration JSON file out to config.json",
                            default = False,
                            action = 'store_true')
        parser.add_argument("--clear-cache",
                            help = "Clears out all short-term cached items",
                            default = False,
                            action = 'store_true')

        # We have to filter out help, otherwise parse_known_args will trigger the help message before having
        # processed the plugin choice or had the plugin subparser added.
        known_args = [arg for arg in sys.argv if arg != '--help' and arg != '-h']
        partial_args, _ = parser.parse_known_args(known_args)

        banner_output = sys.stdout
        if renderers[partial_args.renderer].structured_output:
            banner_output = sys.stderr
        banner_output.write("Volatility 3 Framework {}\n".format(constants.PACKAGE_VERSION))

        if partial_args.plugin_dirs:
            volatility.plugins.__path__ = [os.path.abspath(p)
                                           for p in partial_args.plugin_dirs.split(";")] + constants.PLUGINS_PATH

        if partial_args.symbol_dirs:
            volatility.symbols.__path__ = [os.path.abspath(p)
                                           for p in partial_args.symbol_dirs.split(";")] + constants.SYMBOL_BASEPATHS

        if partial_args.log:
            file_logger = logging.FileHandler(partial_args.log)
            file_logger.setLevel(1)
            file_formatter = logging.Formatter(datefmt = '%y-%m-%d %H:%M:%S',
                                               fmt = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
            file_logger.setFormatter(file_formatter)
            vollog.addHandler(file_logger)
            vollog.info("Logging started")
        if partial_args.verbosity < 3:
            console.setLevel(30 - (partial_args.verbosity * 10))
        else:
            console.setLevel(10 - (partial_args.verbosity - 2))

        vollog.info("Volatility plugins path: {}".format(volatility.plugins.__path__))
        vollog.info("Volatility symbols path: {}".format(volatility.symbols.__path__))

        # Set the PARALLELISM
        if partial_args.parallelism == 'processes':
            constants.PARALLELISM = constants.Parallelism.Multiprocessing
        elif partial_args.parallelism == 'threads':
            constants.PARALLELISM = constants.Parallelism.Threading
        else:
            constants.PARALLELISM = constants.Parallelism.Off

        if partial_args.clear_cache:
            framework.clear_cache()

        # Do the initialization
        ctx = contexts.Context()  # Construct a blank context
        failures = framework.import_files(volatility.plugins,
                                          True)  # Will not log as console's default level is WARNING
        if failures:
            parser.epilog = "The following plugins could not be loaded (use -vv to see why): " + \
                            ", ".join(sorted(failures))
            vollog.info(parser.epilog)
        automagics = automagic.available(ctx)

        plugin_list = framework.list_plugins()

        seen_automagics = set()
        chosen_configurables_list = {}
        for amagic in automagics:
            if amagic in seen_automagics:
                continue
            seen_automagics.add(amagic)
            if isinstance(amagic, interfaces.configuration.ConfigurableInterface):
                self.populate_requirements_argparse(parser, amagic.__class__)

        subparser = parser.add_subparsers(title = "Plugins",
                                          dest = "plugin",
                                          description = "For plugin specific options, run '{} <plugin> --help'".format(
                                              self.CLI_NAME),
                                          action = volargparse.HelpfulSubparserAction)
        for plugin in sorted(plugin_list):
            plugin_parser = subparser.add_parser(plugin, help = plugin_list[plugin].__doc__)
            self.populate_requirements_argparse(plugin_parser, plugin_list[plugin])

        ###
        # PASS TO UI
        ###
        # Hand the plugin requirements over to the CLI (us) and let it construct the config tree

        # Run the argparser
        args = parser.parse_args()
        if args.plugin is None:
            parser.error("Please select a plugin to run")

        vollog.log(constants.LOGLEVEL_VVV, "Cache directory used: {}".format(constants.CACHE_PATH))

        plugin = plugin_list[args.plugin]
        chosen_configurables_list[args.plugin] = plugin
        base_config_path = "plugins"
        plugin_config_path = interfaces.configuration.path_join(base_config_path, plugin.__name__)

        # Special case the -f argument because people use is so frequently
        # It has to go here so it can be overridden by single-location if it's defined
        # NOTE: This will *BREAK* if LayerStacker, or the automagic configuration system, changes at all
        ###
        if args.file:
            file_name = os.path.abspath(args.file)
            if not os.path.exists(file_name):
                vollog.log(logging.INFO, "File does not exist: {}".format(file_name))
            else:
                single_location = "file:" + request.pathname2url(file_name)
                ctx.config['automagic.LayerStacker.single_location'] = single_location

        # UI fills in the config, here we load it from the config file and do it before we process the CL parameters
        if args.config:
            with open(args.config, "r") as f:
                json_val = json.load(f)
                ctx.config.splice(plugin_config_path, interfaces.configuration.HierarchicalDict(json_val))

        # It should be up to the UI to determine which automagics to run, so this is before BACK TO THE FRAMEWORK
        automagics = automagic.choose_automagic(automagics, plugin)
        for amagic in automagics:
            chosen_configurables_list[amagic.__class__.__name__] = amagic

        if ctx.config.get('automagic.LayerStacker.stackers', None) is None:
            ctx.config['automagic.LayerStacker.stackers'] = stacker.choose_os_stackers(plugin)
        self.output_dir = args.output_dir

        self.populate_config(ctx, chosen_configurables_list, args, plugin_config_path)

        if args.extend:
            for extension in args.extend:
                if '=' not in extension:
                    raise ValueError("Invalid extension (extensions must be of the format \"conf.path.value='value'\")")
                address, value = extension[:extension.find('=')], json.loads(extension[extension.find('=') + 1:])
                ctx.config[address] = value

        ###
        # BACK TO THE FRAMEWORK
        ###
        constructed = None
        try:
            progress_callback = PrintedProgress()
            if args.quiet:
                progress_callback = MuteProgress()

            constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback,
                                                   self.file_handler_class_factory())

            if args.write_config:
                vollog.debug("Writing out configuration data to config.json")
                with open("config.json", "w") as f:
                    json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2)
        except exceptions.UnsatisfiedException as excp:
            self.process_unsatisfied_exceptions(excp)
            parser.exit(1, "Unable to validate the plugin requirements: {}\n".format([x for x in excp.unsatisfied]))

        try:
            # Construct and run the plugin
            if constructed:
                renderers[args.renderer]().render(constructed.run())
        except (exceptions.VolatilityException) as excp:
            self.process_exceptions(excp)


Provide useful feedback if an exception occurs during a run of a plugin .

def process_exceptions(self, excp):
        """Provide useful feedback if an exception occurs during a run of a plugin."""
        # Ensure there's nothing in the cache
        sys.stdout.write("\n\n")
        sys.stdout.flush()
        sys.stderr.flush()

        # Log the full exception at a high level for easy access
        fulltrace = traceback.TracebackException.from_exception(excp).format(chain = True)
        vollog.debug("".join(fulltrace))

        if isinstance(excp, exceptions.InvalidAddressException):
            general = "Volatility was unable to read a requested page:"
            if isinstance(excp, exceptions.SwappedInvalidAddressException):
                detail = "Swap error {} in layer {} ({})".format(hex(excp.invalid_address), excp.layer_name, excp)
                caused_by = [
                    "No suitable swap file having been provided (locate and provide the correct swap file)",
                    "An intentionally invalid page (operating system protection)"
                ]
            elif isinstance(excp, exceptions.PagedInvalidAddressException):
                detail = "Page error {} in layer {} ({})".format(hex(excp.invalid_address), excp.layer_name, excp)
                caused_by = [
                    "Memory smear during acquisition (try re-acquiring if possible)",
                    "An intentionally invalid page lookup (operating system protection)",
                    "A bug in the plugin/volatility (re-run with -vvv and file a bug)"
                ]
            else:
                detail = "{} in layer {} ({})".format(hex(excp.invalid_address), excp.layer_name, excp)
                caused_by = [
                    "The base memory file being incomplete (try re-acquiring if possible)",
                    "Memory smear during acquisition (try re-acquiring if possible)",
                    "An intentionally invalid page lookup (operating system protection)",
                    "A bug in the plugin/volatility (re-run with -vvv and file a bug)"
                ]
        elif isinstance(excp, exceptions.SymbolError):
            general = "Volatility experienced a symbol-related issue:"
            detail = "{}{}{}: {}".format(excp.table_name, constants.BANG, excp.symbol_name, excp)
            caused_by = [
                "An invalid symbol table",
                "A plugin requesting a bad symbol",
                "A plugin requesting a symbol from the wrong table",
            ]
        elif isinstance(excp, exceptions.SymbolSpaceError):
            general = "Volatility experienced an issue related to a symbol table:"
            detail = "{}".format(excp)
            caused_by = [
                "An invalid symbol table", "A plugin requesting a bad symbol",
                                           "A plugin requesting a symbol from the wrong table"
            ]
        elif isinstance(excp, exceptions.LayerException):
            general = "Volatility experienced a layer-related issue: {}".format(excp.layer_name)
            detail = "{}".format(excp)
            caused_by = ["A faulty layer implementation (re-run with -vvv and file a bug)"]
        elif isinstance(excp, exceptions.MissingModuleException):
            general = "Volatility could not import a necessary module: {}".format(excp.module)
            detail = "{}".format(excp)
            caused_by = ["A required python module is not installed (install the module and re-run)"]
        else:
            general = "Volatilty encountered an unexpected situation."
            detail = ""
            caused_by = [
                "Please re-run using with -vvv and file a bug with the output", "at {}".format(constants.BUG_URL)
            ]

        # Code that actually renders the exception
        output = sys.stderr
        output.write(general + "\n")
        output.write(detail + "\n\n")
        for cause in caused_by:
            output.write("\t* " + cause + "\n")
        output.write("\nNo further results will be produced\n")
        sys.exit(1)


Provide useful feedback if an exception occurs during requirement fulfillment .

def process_unsatisfied_exceptions(self, excp):
        """Provide useful feedback if an exception occurs during requirement fulfillment."""
        # Add a blank newline
        print("")
        translation_failed = False
        symbols_failed = False
        for config_path in excp.unsatisfied:
            translation_failed = translation_failed or isinstance(
                excp.unsatisfied[config_path], configuration.requirements.TranslationLayerRequirement)
            symbols_failed = symbols_failed or isinstance(excp.unsatisfied[config_path],
                                                          configuration.requirements.SymbolTableRequirement)

            print("Unsatisfied requirement {}: {}".format(config_path, excp.unsatisfied[config_path].description))

        if symbols_failed:
            print("\nA symbol table requirement was not fulfilled.  Please verify that:\n"
                  "\tYou have the correct symbol file for the requirement\n"
                  "\tThe symbol file is under the correct directory or zip file\n"
                  "\tThe symbol file is named appropriately or contains the correct banner\n")
        if translation_failed:
            print("\nA translation layer requirement was not fulfilled.  Please verify that:\n"
                  "\tA file was provided to create this layer (by -f, --single-location or by config)\n"
                  "\tThe file exists and is readable\n"
                  "\tThe necessary symbols are present and identified by volatility")


Populate the context config based on the returned args .

def populate_config(self, context: interfaces.context.ContextInterface,
                        configurables_list: Dict[str, Type[interfaces.configuration.ConfigurableInterface]],
                        args: argparse.Namespace, plugin_config_path: str) -> None:
        """Populate the context config based on the returned args.

        We have already determined these elements must be descended from ConfigurableInterface

        Args:
            context: The volatility context to operate on
            configurables_list: A dictionary of configurable items that can be configured on the plugin
            args: An object containing the arguments necessary
            plugin_config_path: The path within the context's config containing the plugin's configuration
        """
        vargs = vars(args)
        for configurable in configurables_list:
            for requirement in configurables_list[configurable].get_requirements():
                value = vargs.get(requirement.name, None)

                if value is not None:
                    if isinstance(requirement, requirements.URIRequirement):
                        if isinstance(value, str):
                            if not parse.urlparse(value).scheme:
                                if not os.path.exists(value):
                                    raise FileNotFoundError(
                                        "Non-existant file {} passed to URIRequirement".format(value))
                                value = "file://" + request.pathname2url(os.path.abspath(value))
                    if isinstance(requirement, requirements.ListRequirement):
                        if not isinstance(value, list):
                            raise TypeError("Configuration for ListRequirement was not a list: {}".format(
                                requirement.name))
                        value = [requirement.element_type(x) for x in value]
                    if not inspect.isclass(configurables_list[configurable]):
                        config_path = configurables_list[configurable].config_path
                    else:
                        # We must be the plugin, so name it appropriately:
                        config_path = plugin_config_path
                    extended_path = interfaces.configuration.path_join(config_path, requirement.name)
                    context.config[extended_path] = value


Adds the plugin s simple requirements to the provided parser .

def populate_requirements_argparse(self, parser: Union[argparse.ArgumentParser, argparse._ArgumentGroup],
                                       configurable: Type[interfaces.configuration.ConfigurableInterface]):
        """Adds the plugin's simple requirements to the provided parser.

        Args:
            parser: The parser to add the plugin's (simple) requirements to
            configurable: The plugin object to pull the requirements from
        """
        if not issubclass(configurable, interfaces.configuration.ConfigurableInterface):
            raise TypeError("Expected ConfigurableInterface type, not: {}".format(type(configurable)))

        # Construct an argparse group

        for requirement in configurable.get_requirements():
            additional = {}  # type: Dict[str, Any]
            if not isinstance(requirement, interfaces.configuration.RequirementInterface):
                raise TypeError("Plugin contains requirements that are not RequirementInterfaces: {}".format(
                    configurable.__name__))
            if isinstance(requirement, interfaces.configuration.SimpleTypeRequirement):
                additional["type"] = requirement.instance_type
                if isinstance(requirement, requirements.IntRequirement):
                    additional["type"] = lambda x: int(x, 0)
                if isinstance(requirement, requirements.BooleanRequirement):
                    additional["action"] = "store_true"
                    if "type" in additional:
                        del additional["type"]
            elif isinstance(requirement, volatility.framework.configuration.requirements.ListRequirement):
                additional["type"] = requirement.element_type
                nargs = '*' if requirement.optional else '+'
                additional["nargs"] = nargs
            elif isinstance(requirement, volatility.framework.configuration.requirements.ChoiceRequirement):
                additional["type"] = str
                additional["choices"] = requirement.choices
            else:
                continue
            parser.add_argument("--" + requirement.name.replace('_', '-'),
                                help = requirement.description,
                                default = requirement.default,
                                dest = requirement.name,
                                required = not requirement.optional,
                                **additional)


def hex_bytes_as_text(value: bytes) -> str:
    """Renders HexBytes as text.

    Args:
        value: A series of bytes to convert to text

    Returns:
        A text representation of the hexadecimal bytes plus their ascii equivalents, separated by newline characters
    """
    if not isinstance(value, bytes):
        raise TypeError("hex_bytes_as_text takes bytes not: {}".format(type(value)))
    ascii = []
    hex = []
    count = 0
    output = ""
    for byte in value:
        hex.append("{:02x}".format(byte))
        ascii.append(chr(byte) if 0x20 < byte <= 0x7E else ".")
        if (count % 8) == 7:
            output += "\n"
            output += " ".join(hex[count - 7:count + 1])
            output += "\t"
            output += "".join(ascii[count - 7:count + 1])
        count += 1
    return output

def
hex_bytes_as_text
(
value
:
bytes
)
->
str
:
if
not
isinstance
(
value
,
bytes
)
:
raise
TypeError
(
"hex_bytes_as_text takes bytes not: {}"
.
format
(
type
(
value
)
)
)
ascii
=
[
]
hex
=
[
]
count
=
0
output
=
""
for
byte
in
value
:
hex
.
append
(
"{:02x}"
.
format
(
byte
)
)
ascii
.
append
(
chr
(
byte
)
if
0x20
<
byte
<=
0x7E
else
"."
)
if
(
count
%
8
)
==
7
:
output
+=
"\n"
output
+=
" "
.
join
(
hex
[
count
7
:
count
+
1
]
)
output
+=
"\t"
output
+=
""
.
join
(
ascii
[
count
7
:
count
+
1
]
)
count
+=
1
return
output


Renders the bytes as a string where possible otherwise it displays hex data

def multitypedata_as_text(value: format_hints.MultiTypeData) -> str:
    """Renders the bytes as a string where possible, otherwise it displays hex data

    This attempts to convert the string based on its encoding and if no data's been lost due to the split on the null character, then it displays it as is
    """
    if value.show_hex:
        return hex_bytes_as_text(value)
    string_representation = str(value, encoding = value.encoding, errors = 'replace')
    if value.split_nulls and ((len(value) / 2 - 1) <= len(string_representation) <= (len(value) / 2)):
        return "\n".join(string_representation.split("\x00"))
    if len(string_representation) - 1 <= len(string_representation.split("\x00")[0]) <= len(string_representation):
        return string_representation.split("\x00")[0]
    return hex_bytes_as_text(value)

def multitypedata_as_text ( value : format_hints . MultiTypeData ) -> str : if value . show_hex : return hex_bytes_as_text ( value ) string_representation = str ( value , encoding = value . encoding , errors = 'replace' ) if value . split_nulls and ( ( len ( value ) / 2 - 1 ) <= len ( string_representation ) <= ( len ( value ) / 2 ) ) : return "\n" . join ( string_representation . split ( "\x00" ) ) if len ( string_representation ) - 1 <= len ( string_representation . split ( "\x00" ) [ 0 ] ) <= len ( string_representation ) : return string_representation . split ( "\x00" ) [ 0 ] return hex_bytes_as_text ( value )


Renders a disassembly renderer type into string format .

def display_disassembly(disasm: interfaces.renderers.Disassembly) -> str:
    """Renders a disassembly renderer type into string format.

    Args:
        disasm: Input disassembly objects

    Returns:
        A string as rendererd by capstone where available, otherwise output as if it were just bytes
    """

    if CAPSTONE_PRESENT:
        disasm_types = {
            'intel': capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32),
            'intel64': capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64),
            'arm': capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_ARM),
            'arm64': capstone.Cs(capstone.CS_ARCH_ARM64, capstone.CS_MODE_ARM)
        }
        output = ""
        if disasm.architecture is not None:
            for i in disasm_types[disasm.architecture].disasm(disasm.data, disasm.offset):
                output += "\n0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str)
        return output
    return QuickTextRenderer._type_renderers[bytes](disasm.data)

def display_disassembly ( disasm : interfaces . renderers . Disassembly ) -> str : if CAPSTONE_PRESENT : disasm_types = { 'intel' : capstone . Cs ( capstone . CS_ARCH_X86 , capstone . CS_MODE_32 ) , 'intel64' : capstone . Cs ( capstone . CS_ARCH_X86 , capstone . CS_MODE_64 ) , 'arm' : capstone . Cs ( capstone . CS_ARCH_ARM , capstone . CS_MODE_ARM ) , 'arm64' : capstone . Cs ( capstone . CS_ARCH_ARM64 , capstone . CS_MODE_ARM ) } output = "" if disasm . architecture is not None : for i in disasm_types [ disasm . architecture ] . disasm ( disasm . data , disasm . offset ) : output += "\n0x%x:\t%s\t%s" % ( i . address , i . mnemonic , i . op_str ) return output return QuickTextRenderer . _type_renderers [ bytes ] ( disasm . data )


Renders each column immediately to stdout .

def render(self, grid: interfaces.renderers.TreeGrid) -> None:
        """Renders each column immediately to stdout.

        This does not format each line's width appropriately, it merely tab separates each field

        Args:
            grid: The TreeGrid object to render
        """
        # TODO: Docstrings
        # TODO: Improve text output
        outfd = sys.stdout

        line = []
        for column in grid.columns:
            # Ignore the type because namedtuples don't realize they have accessible attributes
            line.append("{}".format(column.name))
        outfd.write("\n{}\n".format("\t".join(line)))

        def visitor(node: interfaces.renderers.TreeNode, accumulator):
            accumulator.write("\n")
            # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case
            accumulator.write("*" * max(0, node.path_depth - 1) + ("" if (node.path_depth <= 1) else " "))
            line = []
            for column_index in range(len(grid.columns)):
                column = grid.columns[column_index]
                renderer = self._type_renderers.get(column.type, self._type_renderers['default'])
                line.append(renderer(node.values[column_index]))
            accumulator.write("{}".format("\t".join(line)))
            accumulator.flush()
            return accumulator

        if not grid.populated:
            grid.populate(visitor, outfd)
        else:
            grid.visit(node = None, function = visitor, initial_accumulator = outfd)

        outfd.write("\n")


Renders each row immediately to stdout .

def render(self, grid: interfaces.renderers.TreeGrid) -> None:
        """Renders each row immediately to stdout.

        Args:
            grid: The TreeGrid object to render
        """
        outfd = sys.stdout

        line = ['"TreeDepth"']
        for column in grid.columns:
            # Ignore the type because namedtuples don't realize they have accessible attributes
            line.append("{}".format('"' + column.name + '"'))
        outfd.write("{}".format(",".join(line)))

        def visitor(node: interfaces.renderers.TreeNode, accumulator):
            accumulator.write("\n")
            # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case
            accumulator.write(str(max(0, node.path_depth - 1)) + ",")
            line = []
            for column_index in range(len(grid.columns)):
                column = grid.columns[column_index]
                renderer = self._type_renderers.get(column.type, self._type_renderers['default'])
                line.append(renderer(node.values[column_index]))
            accumulator.write("{}".format(",".join(line)))
            return accumulator

        if not grid.populated:
            grid.populate(visitor, outfd)
        else:
            grid.visit(node = None, function = visitor, initial_accumulator = outfd)

        outfd.write("\n")


def render(self, grid: interfaces.renderers.TreeGrid) -> None:
        """Renders each column immediately to stdout.

        This does not format each line's width appropriately, it merely tab separates each field

        Args:
            grid: The TreeGrid object to render
        """
        # TODO: Docstrings
        # TODO: Improve text output
        outfd = sys.stdout

        sys.stderr.write("Formatting...\n")

        display_alignment = ">"
        column_separator = " | "

        tree_indent_column = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(20))
        max_column_widths = dict([(column.name, len(column.name)) for column in grid.columns])

        def visitor(
                node: interfaces.renderers.TreeNode,
                accumulator: List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]]
        ) -> List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]]:
            # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case
            max_column_widths[tree_indent_column] = max(max_column_widths.get(tree_indent_column, 0), node.path_depth)
            line = {}
            for column_index in range(len(grid.columns)):
                column = grid.columns[column_index]
                renderer = self._type_renderers.get(column.type, self._type_renderers['default'])
                data = renderer(node.values[column_index])
                max_column_widths[column.name] = max(max_column_widths.get(column.name, len(column.name)),
                                                     len("{}".format(data)))
                line[column] = data
            accumulator.append((node.path_depth, line))
            return accumulator

        final_output = []  # type: List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]]
        if not grid.populated:
            grid.populate(visitor, final_output)
        else:
            grid.visit(node = None, function = visitor, initial_accumulator = final_output)

        # Always align the tree to the left
        format_string_list = ["{0:<" + str(max_column_widths.get(tree_indent_column, 0)) + "s}"]
        for column_index in range(len(grid.columns)):
            column = grid.columns[column_index]
            format_string_list.append("{" + str(column_index + 1) + ":" + display_alignment +
                                      str(max_column_widths[column.name]) + "s}")

        format_string = column_separator.join(format_string_list) + "\n"

        column_titles = [""] + [column.name for column in grid.columns]
        outfd.write(format_string.format(*column_titles))
        for (depth, line) in final_output:
            outfd.write(format_string.format("*" * depth, *[line[column] for column in grid.columns]))


def output_result(self, outfd, result):
        """Outputs the JSON data to a file in a particular format"""
        outfd.write(json.dumps(result, indent = 2, sort_keys = True))

def output_result ( self , outfd , result ) : outfd . write ( json . dumps ( result , indent = 2 , sort_keys = True ) )


def output_result(self, outfd, result):
        """Outputs the JSON results as JSON lines"""
        for line in result:
            outfd.write(json.dumps(line, sort_keys = True))
            outfd.write("\n")

def output_result ( self , outfd , result ) : for line in result : outfd . write ( json . dumps ( line , sort_keys = True ) ) outfd . write ( "\n" )


def main():
    """A convenience function for constructing and running the
    :class:`CommandLine`'s run method."""
    VolShell().run()


def run(self):
        """Executes the command line module, taking the system arguments,
        determining the plugin to run and then running it."""
        sys.stdout.write("Volshell (Volatility 3 Framework) {}\n".format(constants.PACKAGE_VERSION))

        framework.require_interface_version(2, 0, 0)

        parser = argparse.ArgumentParser(prog = 'volshell',
                                         description = "A tool for interactivate forensic analysis of memory images")
        parser.add_argument("-c",
                            "--config",
                            help = "Load the configuration from a json file",
                            default = None,
                            type = str)
        parser.add_argument("-e",
                            "--extend",
                            help = "Extend the configuration with a new (or changed) setting",
                            default = None,
                            action = 'append')
        parser.add_argument("-p",
                            "--plugin-dirs",
                            help = "Semi-colon separated list of paths to find plugins",
                            default = "",
                            type = str)
        parser.add_argument("-s",
                            "--symbol-dirs",
                            help = "Semi-colon separated list of paths to find symbols",
                            default = "",
                            type = str)
        parser.add_argument("-v", "--verbosity", help = "Increase output verbosity", default = 0, action = "count")
        parser.add_argument("-o",
                            "--output-dir",
                            help = "Directory in which to output any generated files",
                            default = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')),
                            type = str)
        parser.add_argument("-q", "--quiet", help = "Remove progress feedback", default = False, action = 'store_true')
        parser.add_argument("--log", help = "Log output to a file as well as the console", default = None, type = str)
        parser.add_argument("-f",
                            "--file",
                            metavar = 'FILE',
                            default = None,
                            type = str,
                            help = "Shorthand for --single-location=file:// if single-location is not defined")
        parser.add_argument("--write-config",
                            help = "Write configuration JSON file out to config.json",
                            default = False,
                            action = 'store_true')
        parser.add_argument("--clear-cache",
                            help = "Clears out all short-term cached items",
                            default = False,
                            action = 'store_true')

        # Volshell specific flags
        os_specific = parser.add_mutually_exclusive_group(required = False)
        os_specific.add_argument("-w",
                                 "--windows",
                                 default = False,
                                 action = "store_true",
                                 help = "Run a Windows volshell")
        os_specific.add_argument("-l", "--linux", default = False, action = "store_true", help = "Run a Linux volshell")
        os_specific.add_argument("-m", "--mac", default = False, action = "store_true", help = "Run a Mac volshell")

        # We have to filter out help, otherwise parse_known_args will trigger the help message before having
        # processed the plugin choice or had the plugin subparser added.
        known_args = [arg for arg in sys.argv if arg != '--help' and arg != '-h']
        partial_args, _ = parser.parse_known_args(known_args)
        if partial_args.plugin_dirs:
            volatility.plugins.__path__ = [os.path.abspath(p)
                                           for p in partial_args.plugin_dirs.split(";")] + constants.PLUGINS_PATH

        if partial_args.symbol_dirs:
            volatility.symbols.__path__ = [os.path.abspath(p)
                                           for p in partial_args.symbol_dirs.split(";")] + constants.SYMBOL_BASEPATHS

        vollog.info("Volatility plugins path: {}".format(volatility.plugins.__path__))
        vollog.info("Volatility symbols path: {}".format(volatility.symbols.__path__))

        if partial_args.log:
            file_logger = logging.FileHandler(partial_args.log)
            file_logger.setLevel(0)
            file_formatter = logging.Formatter(datefmt = '%y-%m-%d %H:%M:%S',
                                               fmt = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
            file_logger.setFormatter(file_formatter)
            vollog.addHandler(file_logger)
            vollog.info("Logging started")

        if partial_args.verbosity < 3:
            console.setLevel(30 - (partial_args.verbosity * 10))
        else:
            console.setLevel(10 - (partial_args.verbosity - 2))

        if partial_args.clear_cache:
            for cache_filename in glob.glob(os.path.join(constants.CACHE_PATH, '*.cache')):
                os.unlink(cache_filename)

        # Do the initialization
        ctx = contexts.Context()  # Construct a blank context
        failures = framework.import_files(volatility.plugins,
                                          True)  # Will not log as console's default level is WARNING
        if failures:
            parser.epilog = "The following plugins could not be loaded (use -vv to see why): " + \
                            ", ".join(sorted(failures))
            vollog.info(parser.epilog)
        automagics = automagic.available(ctx)

        # Initialize the list of plugins in case volshell needs it
        framework.list_plugins()

        seen_automagics = set()
        configurables_list = {}
        for amagic in automagics:
            if amagic in seen_automagics:
                continue
            seen_automagics.add(amagic)
            if isinstance(amagic, interfaces.configuration.ConfigurableInterface):
                self.populate_requirements_argparse(parser, amagic.__class__)
                configurables_list[amagic.__class__.__name__] = amagic

        # We don't list plugin arguments, because they can be provided within python
        volshell_plugin_list = {'generic': generic.Volshell, 'windows': windows.Volshell}
        for plugin in volshell_plugin_list:
            subparser = parser.add_argument_group(title = plugin.capitalize(),
                                                  description = "Configuration options based on {} options".format(
                                                      plugin.capitalize()))
            self.populate_requirements_argparse(subparser, volshell_plugin_list[plugin])
            configurables_list[plugin] = volshell_plugin_list[plugin]

        ###
        # PASS TO UI
        ###
        # Hand the plugin requirements over to the CLI (us) and let it construct the config tree

        # Run the argparser
        args = parser.parse_args()

        vollog.log(constants.LOGLEVEL_VVV, "Cache directory used: {}".format(constants.CACHE_PATH))

        plugin = generic.Volshell
        if args.windows:
            plugin = windows.Volshell
        if args.linux:
            plugin = linux.Volshell
        if args.mac:
            plugin = mac.Volshell

        base_config_path = "plugins"
        plugin_config_path = interfaces.configuration.path_join(base_config_path, plugin.__name__)

        # Special case the -f argument because people use is so frequently
        # It has to go here so it can be overridden by single-location if it's defined
        # NOTE: This will *BREAK* if LayerStacker, or the automagic configuration system, changes at all
        ###
        if args.file:
            file_name = os.path.abspath(args.file)
            if not os.path.exists(file_name):
                vollog.log(logging.INFO, "File does not exist: {}".format(file_name))
            else:
                single_location = "file:" + request.pathname2url(file_name)
                ctx.config['automagic.LayerStacker.single_location'] = single_location

        # UI fills in the config, here we load it from the config file and do it before we process the CL parameters
        if args.config:
            with open(args.config, "r") as f:
                json_val = json.load(f)
                ctx.config.splice(plugin_config_path, interfaces.configuration.HierarchicalDict(json_val))

        self.populate_config(ctx, configurables_list, args, plugin_config_path)

        if args.extend:
            for extension in args.extend:
                if '=' not in extension:
                    raise ValueError("Invalid extension (extensions must be of the format \"conf.path.value='value'\")")
                address, value = extension[:extension.find('=')], json.loads(extension[extension.find('=') + 1:])
                ctx.config[address] = value

        # It should be up to the UI to determine which automagics to run, so this is before BACK TO THE FRAMEWORK
        automagics = automagic.choose_automagic(automagics, plugin)
        self.output_dir = args.output_dir

        ###
        # BACK TO THE FRAMEWORK
        ###
        try:
            progress_callback = cli.PrintedProgress()
            if args.quiet:
                progress_callback = cli.MuteProgress()

            constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback,
                                                   self.file_handler_class_factory())

            if args.write_config:
                vollog.debug("Writing out configuration data to config.json")
                with open("config.json", "w") as f:
                    json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2)

            # Construct and run the plugin
            constructed.run()
        except exceptions.VolatilityException as excp:
            self.process_exceptions(excp)
            parser.exit(1, "Unable to validate the plugin requirements: {}\n".format([x for x in excp.unsatisfied]))


def run(self, additional_locals: Dict[str, Any] = None) -> interfaces.renderers.TreeGrid:
        """Runs the interactive volshell plugin.

        Returns:
            Return a TreeGrid but this is always empty since the point of this plugin is to run interactively
        """

        self._current_layer = self.config['primary']

        # Try to enable tab completion
        try:
            import readline
        except ImportError:
            pass
        else:
            import rlcompleter
            completer = rlcompleter.Completer(namespace = self._construct_locals_dict())
            readline.set_completer(completer.complete)
            readline.parse_and_bind("tab: complete")
            print("Readline imported successfully")

        # TODO: provide help, consider generic functions (pslist?) and/or providing windows/linux functions

        mode = self.__module__.split('.')[-1]
        mode = mode[0].upper() + mode[1:]

        banner = """
    Call help() to see available functions

    Volshell mode: {}
    Current Layer: {}
        """.format(mode, self.current_layer)

        sys.ps1 = "({}) >>> ".format(self.current_layer)
        code.interact(banner = banner, local = self._construct_locals_dict())

        return renderers.TreeGrid([("Terminating", str)], None)


def help(self, *args):
        """Describes the available commands"""
        if args:
            help(*args)
            return

        variables = []
        print("\nMethods:")
        for aliases, item in self.construct_locals():
            name = ", ".join(aliases)
            if item.__doc__ and callable(item):
                print("* {}".format(name))
                print("    {}".format(item.__doc__))
            else:
                variables.append(name)

        print("\nVariables:")
        for var in variables:
            print("  {}".format(var))

def
help
(
self
,
*
args
)
:
if
args
:
help
(
*
args
)
return
variables
=
[
]
print
(
"\nMethods:"
)
for
aliases
,
item
in
self
.
construct_locals
(
)
:
name
=
", "
.
join
(
aliases
)
if
item
.
__doc__
and
callable
(
item
)
:
print
(
"* {}"
.
format
(
name
)
)
print
(
"    {}"
.
format
(
item
.
__doc__
)
)
else
:
variables
.
append
(
name
)
print
(
"\nVariables:"
)
for
var
in
variables
:
print
(
"  {}"
.
format
(
var
)
)


Returns a dictionary listing the functions to be added to the environment .

def construct_locals(self) -> List[Tuple[List[str], Any]]:
        """Returns a dictionary listing the functions to be added to the
        environment."""
        return [(['dt', 'display_type'], self.display_type), (['db', 'display_bytes'], self.display_bytes),
                (['dw', 'display_words'], self.display_words), (['dd',
                                                                 'display_doublewords'], self.display_doublewords),
                (['dq', 'display_quadwords'], self.display_quadwords), (['dis', 'disassemble'], self.disassemble),
                (['cl', 'change_layer'], self.change_layer), (['context'], self.context), (['self'], self),
                (['dpo', 'display_plugin_output'], self.display_plugin_output),
                (['gt', 'generate_treegrid'], self.generate_treegrid), (['rt',
                                                                         'render_treegrid'], self.render_treegrid),
                (['ds', 'display_symbols'], self.display_symbols), (['hh', 'help'], self.help)]

def construct_locals ( self ) -> List [ Tuple [ List [ str ] , Any ] ] : return [ ( [ 'dt' , 'display_type' ] , self . display_type ) , ( [ 'db' , 'display_bytes' ] , self . display_bytes ) , ( [ 'dw' , 'display_words' ] , self . display_words ) , ( [ 'dd' , 'display_doublewords' ] , self . display_doublewords ) , ( [ 'dq' , 'display_quadwords' ] , self . display_quadwords ) , ( [ 'dis' , 'disassemble' ] , self . disassemble ) , ( [ 'cl' , 'change_layer' ] , self . change_layer ) , ( [ 'context' ] , self . context ) , ( [ 'self' ] , self ) , ( [ 'dpo' , 'display_plugin_output' ] , self . display_plugin_output ) , ( [ 'gt' , 'generate_treegrid' ] , self . generate_treegrid ) , ( [ 'rt' , 'render_treegrid' ] , self . render_treegrid ) , ( [ 'ds' , 'display_symbols' ] , self . display_symbols ) , ( [ 'hh' , 'help' ] , self . help ) ]


def _construct_locals_dict(self) -> Dict[str, Any]:
        """Returns a dictionary of the locals """
        result = {}
        for aliases, value in self.construct_locals():
            for alias in aliases:
                result[alias] = value
        return result

def _construct_locals_dict ( self ) -> Dict [ str , Any ] : result = { } for aliases , value in self . construct_locals ( ) : for alias in aliases : result [ alias ] = value return result


Reads the bytes necessary for the display_ * methods

def _read_data(self, offset, count = 128, layer_name = None):
        """Reads the bytes necessary for the display_* methods"""
        return self.context.layers[layer_name or self.current_layer].read(offset, count)

def _read_data ( self , offset , count = 128 , layer_name = None ) : return self . context . layers [ layer_name or self . current_layer ] . read ( offset , count )


def _display_data(self, offset: int, remaining_data: bytes, format_string: str = "B", ascii: bool = True):
        """Display a series of bytes"""
        chunk_size = struct.calcsize(format_string)
        data_length = len(remaining_data)
        remaining_data = remaining_data[:data_length - (data_length % chunk_size)]

        while remaining_data:
            current_line, remaining_data = remaining_data[:16], remaining_data[16:]

            data_blocks = [current_line[chunk_size * i:chunk_size * (i + 1)] for i in range(16 // chunk_size)]
            data_blocks = [x for x in data_blocks if x != b'']
            valid_data = [("{:0" + str(2 * chunk_size) + "x}").format(struct.unpack(format_string, x)[0])
                          for x in data_blocks]
            padding_data = [" " * 2 * chunk_size for _ in range((16 - len(current_line)) // chunk_size)]
            hex_data = " ".join(valid_data + padding_data)

            ascii_data = ""
            if ascii:
                connector = " "
                if chunk_size < 2:
                    connector = ""
                ascii_data = connector.join([self._ascii_bytes(x) for x in valid_data])

            print(hex(offset), "  ", hex_data, "  ", ascii_data)
            offset += 16

def
_display_data
(
self
,
offset
:
int
,
remaining_data
:
bytes
,
format_string
:
str
=
"B"
,
ascii
:
bool
=
True
)
:
chunk_size
=
struct
.
calcsize
(
format_string
)
data_length
=
len
(
remaining_data
)
remaining_data
=
remaining_data
[
:
data_length
(
data_length
%
chunk_size
)
]
while
remaining_data
:
current_line
,
remaining_data
=
remaining_data
[
:
16
]
,
remaining_data
[
16
:
]
data_blocks
=
[
current_line
[
chunk_size
*
i
:
chunk_size
*
(
i
+
1
)
]
for
i
in
range
(
16
//
chunk_size
)
]
data_blocks
=
[
x
for
x
in
data_blocks
if
x
!=
b''
]
valid_data
=
[
(
"{:0"
+
str
(
2
*
chunk_size
)
+
"x}"
)
.
format
(
struct
.
unpack
(
format_string
,
x
)
[
0
]
)
for
x
in
data_blocks
]
padding_data
=
[
" "
*
2
*
chunk_size
for
_
in
range
(
(
16
len
(
current_line
)
)
//
chunk_size
)
]
hex_data
=
" "
.
join
(
valid_data
+
padding_data
)
ascii_data
=
""
if
ascii
:
connector
=
" "
if
chunk_size
<
2
:
connector
=
""
ascii_data
=
connector
.
join
(
[
self
.
_ascii_bytes
(
x
)
for
x
in
valid_data
]
)
print
(
hex
(
offset
)
,
"  "
,
hex_data
,
"  "
,
ascii_data
)
offset
+=
16


def _ascii_bytes(bytes):
        """Converts bytes into an ascii string"""
        return "".join([chr(x) if 32 < x < 127 else '.' for x in binascii.unhexlify(bytes)])

def _ascii_bytes ( bytes ) : return "" . join ( [ chr ( x ) if 32 < x < 127 else '.' for x in binascii . unhexlify ( bytes ) ] )


def change_layer(self, layer_name = None):
        """Changes the current default layer"""
        if not layer_name:
            layer_name = self.config['primary']
        self._current_layer = layer_name
        sys.ps1 = "({}) >>> ".format(self.current_layer)

def
change_layer
(
self
,
layer_name
=
)
:
if
not
layer_name
:
layer_name
=
self
.
config
[
'primary'
]
self
.
_current_layer
=
layer_name
sys
.
ps1
=
"({}) >>> "
.
format
(
self
.
current_layer
)


def display_bytes(self, offset, count = 128, layer_name = None):
        """Displays byte values and ASCII characters"""
        remaining_data = self._read_data(offset, count = count, layer_name = layer_name)
        self._display_data(offset, remaining_data)

def display_bytes ( self , offset , count = 128 , layer_name = None ) : remaining_data = self . _read_data ( offset , count = count , layer_name = layer_name ) self . _display_data ( offset , remaining_data )


Displays quad - word values ( 8 bytes ) and corresponding ASCII characters

def display_quadwords(self, offset, count = 128, layer_name = None):
        """Displays quad-word values (8 bytes) and corresponding ASCII characters"""
        remaining_data = self._read_data(offset, count = count, layer_name = layer_name)
        self._display_data(offset, remaining_data, format_string = "Q")

def display_quadwords ( self , offset , count = 128 , layer_name = None ) : remaining_data = self . _read_data ( offset , count = count , layer_name = layer_name ) self . _display_data ( offset , remaining_data , format_string = "Q" )


Displays double - word values ( 4 bytes ) and corresponding ASCII characters

def display_doublewords(self, offset, count = 128, layer_name = None):
        """Displays double-word values (4 bytes) and corresponding ASCII characters"""
        remaining_data = self._read_data(offset, count = count, layer_name = layer_name)
        self._display_data(offset, remaining_data, format_string = "I")

def display_doublewords ( self , offset , count = 128 , layer_name = None ) : remaining_data = self . _read_data ( offset , count = count , layer_name = layer_name ) self . _display_data ( offset , remaining_data , format_string = "I" )


Displays word values ( 2 bytes ) and corresponding ASCII characters

def display_words(self, offset, count = 128, layer_name = None):
        """Displays word values (2 bytes) and corresponding ASCII characters"""
        remaining_data = self._read_data(offset, count = count, layer_name = layer_name)
        self._display_data(offset, remaining_data, format_string = "H")

def display_words ( self , offset , count = 128 , layer_name = None ) : remaining_data = self . _read_data ( offset , count = count , layer_name = layer_name ) self . _display_data ( offset , remaining_data , format_string = "H" )


def disassemble(self, offset, count = 128, layer_name = None, architecture = None):
        """Disassembles a number of instructions from the code at offset"""
        remaining_data = self._read_data(offset, count = count, layer_name = layer_name)
        if not has_capstone:
            print("Capstone not available - please install it to use the disassemble command")
        else:
            if isinstance(self.context.layers[layer_name or self.current_layer], intel.Intel32e):
                architecture = 'intel64'
            elif isinstance(self.context.layers[layer_name or self.current_layer], intel.Intel):
                architecture = 'intel'
            disasm_types = {
                'intel': capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32),
                'intel64': capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64),
                'arm': capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_ARM),
                'arm64': capstone.Cs(capstone.CS_ARCH_ARM64, capstone.CS_MODE_ARM)
            }
            if architecture is not None:
                for i in disasm_types[architecture].disasm(remaining_data, offset):
                    print("0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))


def display_type(self,
                     object: Union[str, interfaces.objects.ObjectInterface, interfaces.objects.Template],
                     offset: int = None):
        """Display Type describes the members of a particular object in alphabetical order"""
        if not isinstance(object, (str, interfaces.objects.ObjectInterface, interfaces.objects.Template)):
            print("Cannot display information about non-type object")
            return

        if not isinstance(object, str):
            # Mypy requires us to order things this way
            volobject = object
        elif offset is None:
            # Str and no offset
            volobject = self.context.symbol_space.get_type(object)
        else:
            # Str and offset
            volobject = self.context.object(object, layer_name = self.current_layer, offset = offset)

        if offset is not None:
            volobject = self.context.object(volobject.vol.type_name, layer_name = self.current_layer, offset = offset)

        if hasattr(volobject.vol, 'size'):
            print("{} ({} bytes)".format(volobject.vol.type_name, volobject.vol.size))
        elif hasattr(volobject.vol, 'data_format'):
            data_format = volobject.vol.data_format
            print("{} ({} bytes, {} endian, {})".format(volobject.vol.type_name, data_format.length,
                                                        data_format.byteorder,
                                                        'signed' if data_format.signed else 'unsigned'))

        if hasattr(volobject.vol, 'members'):
            longest_member = longest_offset = longest_typename = 0
            for member in volobject.vol.members:
                relative_offset, member_type = volobject.vol.members[member]
                longest_member = max(len(member), longest_member)
                longest_offset = max(len(hex(relative_offset)), longest_offset)
                longest_typename = max(len(member_type.vol.type_name), longest_typename)

            for member in sorted(volobject.vol.members, key = lambda x: (volobject.vol.members[x][0], x)):
                relative_offset, member_type = volobject.vol.members[member]
                len_offset = len(hex(relative_offset))
                len_member = len(member)
                len_typename = len(member_type.vol.type_name)
                if isinstance(volobject, interfaces.objects.ObjectInterface):
                    # We're an instance, so also display the data
                    print(" " * (longest_offset - len_offset), hex(relative_offset), ":  ", member,
                          " " * (longest_member - len_member), "  ",
                          member_type.vol.type_name, " " * (longest_typename - len_typename), "  ",
                          self._display_value(getattr(volobject, member)))
                else:
                    print(" " * (longest_offset - len_offset), hex(relative_offset), ":  ", member,
                          " " * (longest_member - len_member), "  ", member_type.vol.type_name)


def generate_treegrid(self, plugin: Type[interfaces.plugins.PluginInterface],
                          **kwargs) -> Optional[interfaces.renderers.TreeGrid]:
        """Generates a TreeGrid based on a specific plugin passing in kwarg configuration values"""
        path_join = interfaces.configuration.path_join

        # Generate a temporary configuration path
        plugin_config_suffix = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
        plugin_path = path_join(self.config_path, plugin_config_suffix)

        # Populate the configuration
        for name, value in kwargs.items():
            self.config[path_join(plugin_config_suffix, plugin.__name__, name)] = value

        try:
            constructed = plugins.construct_plugin(self.context, [], plugin, plugin_path, None, NullFileHandler)
            return constructed.run()
        except exceptions.UnsatisfiedException as excp:
            print("Unable to validate the plugin requirements: {}\n".format([x for x in excp.unsatisfied]))
        return None


def render_treegrid(self,
                        treegrid: interfaces.renderers.TreeGrid,
                        renderer: Optional[interfaces.renderers.Renderer] = None) -> None:
        """Renders a treegrid as produced by generate_treegrid"""
        if renderer is None:
            renderer = text_renderer.QuickTextRenderer()
        renderer.render(treegrid)

def render_treegrid ( self , treegrid : interfaces . renderers . TreeGrid , renderer : Optional [ interfaces . renderers . Renderer ] = None ) -> None : if renderer is None : renderer = text_renderer . QuickTextRenderer ( ) renderer . render ( treegrid )


Displays the output for a particular plugin ( with keyword arguments )

def display_plugin_output(self, plugin: Type[interfaces.plugins.PluginInterface], **kwargs) -> None:
        """Displays the output for a particular plugin (with keyword arguments)"""
        treegrid = self.generate_treegrid(plugin, **kwargs)
        if treegrid is not None:
            self.render_treegrid(treegrid)

def display_plugin_output ( self , plugin : Type [ interfaces . plugins . PluginInterface ] , * * kwargs ) -> None : treegrid = self . generate_treegrid ( plugin , * * kwargs ) if treegrid is not None : self . render_treegrid ( treegrid )


def display_symbols(self, symbol_table: str = None):
        """Prints an alphabetical list of symbols for a symbol table"""
        if symbol_table is None:
            print("No symbol table provided")
            return
        longest_offset = longest_name = 0

        table = self.context.symbol_space[symbol_table]
        for symbol_name in table.symbols:
            symbol = table.get_symbol(symbol_name)
            longest_offset = max(longest_offset, len(hex(symbol.address)))
            longest_name = max(longest_name, len(symbol.name))

        for symbol_name in sorted(table.symbols):
            symbol = table.get_symbol(symbol_name)
            len_offset = len(hex(symbol.address))
            print(" " * (longest_offset - len_offset), hex(symbol.address), " ", symbol.name)

def
display_symbols
(
self
,
symbol_table
:
str
=
)
:
if
symbol_table
is
:
print
(
"No symbol table provided"
)
return
longest_offset
=
longest_name
=
0
table
=
self
.
context
.
symbol_space
[
symbol_table
]
for
symbol_name
in
table
.
symbols
:
symbol
=
table
.
get_symbol
(
symbol_name
)
longest_offset
=
max
(
longest_offset
,
len
(
hex
(
symbol
.
address
)
)
)
longest_name
=
max
(
longest_name
,
len
(
symbol
.
name
)
)
for
symbol_name
in
sorted
(
table
.
symbols
)
:
symbol
=
table
.
get_symbol
(
symbol_name
)
len_offset
=
len
(
hex
(
symbol
.
address
)
)
print
(
" "
*
(
longest_offset
len_offset
)
,
hex
(
symbol
.
address
)
,
" "
,
symbol
.
name
)


def writelines(self, lines):
        """Dummy method"""
        pass


def write(self, data):
        """Dummy method"""
        return len(data)

def write ( self , data ) : return len ( data )


Change the current process and layer based on a process ID

def change_process(self, pid = None):
        """Change the current process and layer, based on a process ID"""
        processes = self.list_processes()
        for process in processes:
            if process.UniqueProcessId == pid:
                process_layer = process.add_process_layer()
                self.change_layer(process_layer)
                return
        print("No process with process ID {} found".format(pid))

def
change_process
(
self
,
pid
=
)
:
processes
=
self
.
list_processes
(
)
for
process
in
processes
:
if
process
.
UniqueProcessId
==
pid
:
process_layer
=
process
.
add_process_layer
(
)
self
.
change_layer
(
process_layer
)
return
print
(
"No process with process ID {} found"
.
format
(
pid
)
)


def list_processes(self):
        """Returns a list of EPROCESS objects from the primary layer"""
        # We always use the main kernel memory and associated symbols
        return list(pslist.PsList.list_processes(self.context, self.config['primary'], self.config['nt_symbols']))


def display_type(self,
                     object: Union[str, interfaces.objects.ObjectInterface, interfaces.objects.Template],
                     offset: int = None):
        """Display Type describes the members of a particular object in alphabetical order"""
        if isinstance(object, str):
            if constants.BANG not in object:
                object = self.config['nt_symbols'] + constants.BANG + object
        return super().display_type(object, offset)

def display_type ( self , object : Union [ str , interfaces . objects . ObjectInterface , interfaces . objects . Template ] , offset : int = None ) : if isinstance ( object , str ) : if constants . BANG not in object : object = self . config [ 'nt_symbols' ] + constants . BANG + object return super ( ) . display_type ( object , offset )


def display_symbols(self, symbol_table: str = None):
        """Prints an alphabetical list of symbols for a symbol table"""
        if symbol_table is None:
            symbol_table = self.config['nt_symbols']
        return super().display_symbols(symbol_table)

def display_symbols ( self , symbol_table : str = None ) : if symbol_table is None : symbol_table = self . config [ 'nt_symbols' ] return super ( ) . display_symbols ( symbol_table )


def change_task(self, pid = None):
        """Change the current process and layer, based on a process ID"""
        tasks = self.list_tasks()
        for task in tasks:
            if task.pid == pid:
                process_layer = task.add_process_layer()
                if process_layer is not None:
                    self.change_layer(process_layer)
                    return
                print("Layer for task ID {} could not be constructed".format(pid))
                return
        print("No task with task ID {} found".format(pid))


def list_tasks(self):
        """Returns a list of task objects from the primary layer"""
        # We always use the main kernel memory and associated symbols
        return list(pslist.PsList.list_tasks(self.context, self.config['primary'], self.config['vmlinux']))


def display_type(self,
                     object: Union[str, interfaces.objects.ObjectInterface, interfaces.objects.Template],
                     offset: int = None):
        """Display Type describes the members of a particular object in alphabetical order"""
        if isinstance(object, str):
            if constants.BANG not in object:
                object = self.config['vmlinux'] + constants.BANG + object
        return super().display_type(object, offset)

def display_type ( self , object : Union [ str , interfaces . objects . ObjectInterface , interfaces . objects . Template ] , offset : int = None ) : if isinstance ( object , str ) : if constants . BANG not in object : object = self . config [ 'vmlinux' ] + constants . BANG + object return super ( ) . display_type ( object , offset )


def display_symbols(self, symbol_table: str = None):
        """Prints an alphabetical list of symbols for a symbol table"""
        if symbol_table is None:
            symbol_table = self.config['vmlinux']
        return super().display_symbols(symbol_table)

def display_symbols ( self , symbol_table : str = None ) : if symbol_table is None : symbol_table = self . config [ 'vmlinux' ] return super ( ) . display_symbols ( symbol_table )


def change_task(self, pid = None):
        """Change the current process and layer, based on a process ID"""
        tasks = self.list_tasks()
        for task in tasks:
            if task.p_pid == pid:
                process_layer = task.add_process_layer()
                if process_layer is not None:
                    self.change_layer(process_layer)
                    return
                print("Layer for task ID {} could not be constructed".format(pid))
                return
        print("No task with task ID {} found".format(pid))


def list_tasks(self):
        """Returns a list of task objects from the primary layer"""
        # We always use the main kernel memory and associated symbols
        return list(pslist.PsList.list_tasks(self.context, self.config['primary'], self.config['darwin']))


def display_type(self,
                     object: Union[str, interfaces.objects.ObjectInterface, interfaces.objects.Template],
                     offset: int = None):
        """Display Type describes the members of a particular object in alphabetical order"""
        if isinstance(object, str):
            if constants.BANG not in object:
                object = self.config['darwin'] + constants.BANG + object
        return super().display_type(object, offset)

def display_type ( self , object : Union [ str , interfaces . objects . ObjectInterface , interfaces . objects . Template ] , offset : int = None ) : if isinstance ( object , str ) : if constants . BANG not in object : object = self . config [ 'darwin' ] + constants . BANG + object return super ( ) . display_type ( object , offset )


def display_symbols(self, symbol_table: str = None):
        """Prints an alphabetical list of symbols for a symbol table"""
        if symbol_table is None:
            symbol_table = self.config['darwin']
        return super().display_symbols(symbol_table)

def display_symbols ( self , symbol_table : str = None ) : if symbol_table is None : symbol_table = self . config [ 'darwin' ] return super ( ) . display_symbols ( symbol_table )


Provides the so version number of the library .

def interface_version():
    """Provides the so version number of the library."""
    return constants.VERSION_MAJOR, constants.VERSION_MINOR, constants.VERSION_PATCH

def interface_version ( ) : return constants . VERSION_MAJOR , constants . VERSION_MINOR , constants . VERSION_PATCH


Checks the required version of a plugin .

def require_interface_version(*args) -> None:
    """Checks the required version of a plugin."""
    if len(args):
        if args[0] != interface_version()[0]:
            raise RuntimeError("Framework interface version {} is incompatible with required version {}".format(
                interface_version()[0], args[0]))
        if len(args) > 1:
            if args[1] > interface_version()[1]:
                raise RuntimeError(
                    "Framework interface version {} is an older revision than the required version {}".format(
                        ".".join([str(x) for x in interface_version()[0:1]]), ".".join([str(x) for x in args[0:2]])))


Returns all the ( recursive ) subclasses of a given class .

def class_subclasses(cls: Type[T]) -> Generator[Type[T], None, None]:
    """Returns all the (recursive) subclasses of a given class."""
    if not inspect.isclass(cls):
        raise TypeError("class_subclasses parameter not a valid class: {}".format(cls))
    for clazz in cls.__subclasses__():
        # The typing system is not clever enough to realize that clazz has a hidden attr after the hasattr check
        if not hasattr(clazz, 'hidden') or not clazz.hidden:  # type: ignore
            yield clazz
        for return_value in class_subclasses(clazz):
            yield return_value


Imports all plugins present under plugins module namespace .

def import_files(base_module, ignore_errors = False) -> List[str]:
    """Imports all plugins present under plugins module namespace."""
    failures = []
    if not isinstance(base_module.__path__, list):
        raise TypeError("[base_module].__path__ must be a list of paths")
    vollog.log(constants.LOGLEVEL_VVVV,
               "Importing from the following paths: {}".format(", ".join(base_module.__path__)))
    for path in base_module.__path__:
        for root, _, files in os.walk(path, followlinks = True):
            # TODO: Figure out how to import pycache files
            if root.endswith("__pycache__"):
                continue
            for f in files:
                if (f.endswith(".py") or f.endswith(".pyc") or f.endswith(".pyo")) and not f.startswith("__"):
                    modpath = os.path.join(root[len(path) + len(os.path.sep):], f[:f.rfind(".")])
                    module = modpath.replace(os.path.sep, ".")
                    if base_module.__name__ + "." + module not in sys.modules:
                        try:
                            importlib.import_module(base_module.__name__ + "." + module)
                        except ImportError as e:
                            vollog.debug(str(e))
                            vollog.debug("Failed to import module {} based on file: {}".format(
                                base_module.__name__ + "." + module, modpath))
                            failures.append(base_module.__name__ + "." + module)
                            if not ignore_errors:
                                raise
    return failures


Method generates Tuples of ( description timestamp_type timestamp )

def generate_timeline(self) -> Generator[Tuple[str, TimeLinerType, datetime.datetime], None, None]:
        """Method generates Tuples of (description, timestamp_type, timestamp)

        These need not be generated in any particular order, sorting
        will be done later
        """

def generate_timeline ( self ) -> Generator [ Tuple [ str , TimeLinerType , datetime . datetime ] , None , None ] :


Takes a timeline sorts it and output the data from each relevant row from each plugin .

def _generator(self, runable_plugins: List[TimeLinerInterface]) -> Optional[Iterable[Tuple[int, Tuple]]]:
        """Takes a timeline, sorts it and output the data from each relevant
        row from each plugin."""
        # Generate the results for each plugin
        data = []
        for plugin in runable_plugins:
            plugin_name = plugin.__class__.__name__
            self._progress_callback((runable_plugins.index(plugin) * 100) // len(runable_plugins),
                                    "Running plugin {}...".format(plugin_name))
            try:
                vollog.log(logging.INFO, "Running {}".format(plugin_name))
                for (item, timestamp_type, timestamp) in plugin.generate_timeline():
                    times = self.timeline.get((plugin_name, item), {})
                    if times.get(timestamp_type, None) is not None:
                        vollog.debug("Multiple timestamps for the same plugin/file combination found: {} {}".format(
                            plugin_name, item))
                    times[timestamp_type] = timestamp
                    self.timeline[(plugin_name, item)] = times
                    data.append((0, [
                        plugin_name, item,
                        times.get(TimeLinerType.CREATED, renderers.NotApplicableValue()),
                        times.get(TimeLinerType.MODIFIED, renderers.NotApplicableValue()),
                        times.get(TimeLinerType.ACCESSED, renderers.NotApplicableValue()),
                        times.get(TimeLinerType.CHANGED, renderers.NotApplicableValue())
                    ]))
            except Exception:
                vollog.log(logging.INFO, "Exception occurred running plugin: {}".format(plugin_name))
                vollog.log(logging.DEBUG, traceback.format_exc())
        for data_item in sorted(data, key = self._sort_function):
            yield data_item

        # Write out a body file if necessary
        if self.config.get('create-bodyfile', True):
            with self.open("volatility.body") as file_data:
                with io.TextIOWrapper(file_data, write_through = True) as fp:
                    for (plugin_name, item) in self.timeline:
                        times = self.timeline[(plugin_name, item)]
                        # Body format is: MD5|name|inode|mode_as_string|UID|GID|size|atime|mtime|ctime|crtime

                        if self._any_time_present(times):
                            fp.write("|{} - {}||||||{}|{}|{}|{}\n".format(
                                plugin_name, self._sanitize_body_format(item),
                                self._text_format(times.get(TimeLinerType.ACCESSED, "")),
                                self._text_format(times.get(TimeLinerType.MODIFIED, "")),
                                self._text_format(times.get(TimeLinerType.CHANGED, "")),
                                self._text_format(times.get(TimeLinerType.CREATED, ""))))


Formats a value as text in case it is an AbsentValue

def _text_format(self, value):
        """Formats a value as text, in case it is an AbsentValue"""
        if isinstance(value, interfaces.renderers.BaseAbsentValue):
            return ""
        if isinstance(value, datetime.datetime):
            return int(value.timestamp())
        return value

def _text_format ( self , value ) : if isinstance ( value , interfaces . renderers . BaseAbsentValue ) : return "" if isinstance ( value , datetime . datetime ) : return int ( value . timestamp ( ) ) return value


def run(self):
        """Isolate each plugin and run it."""

        # Use all the plugins if there's no filter
        self.usable_plugins = self.usable_plugins or self.get_usable_plugins()
        self.automagics = self.automagics or automagic.available(self._context)
        plugins_to_run = []

        filter_list = self.config['plugin-filter']
        # Identify plugins that we can run which output datetimes
        for plugin_class in self.usable_plugins:
            try:
                automagics = automagic.choose_automagic(self.automagics, plugin_class)

                plugin = plugins.construct_plugin(self.context, automagics, plugin_class, self.config_path,
                                                  self._progress_callback, self.open)

                if isinstance(plugin, TimeLinerInterface):
                    if not len(filter_list) or any(
                            [filter in plugin.__module__ + '.' + plugin.__class__.__name__ for filter in filter_list]):
                        plugins_to_run.append(plugin)
            except exceptions.UnsatisfiedException as excp:
                # Remove the failed plugin from the list and continue
                vollog.debug("Unable to satisfy {}: {}".format(plugin_class.__name__, excp.unsatisfied))
                continue

        if self.config.get('record-config', False):
            total_config = {}
            for plugin in plugins_to_run:
                old_dict = dict(plugin.build_configuration())
                for entry in old_dict:
                    total_config[interfaces.configuration.path_join(plugin.__class__.__name__, entry)] = old_dict[entry]

            with self.open("config.json") as file_data:
                with io.TextIOWrapper(file_data, write_through = True) as fp:
                    json.dump(total_config, fp, sort_keys = True, indent = 2)

        return renderers.TreeGrid(columns = [("Plugin", str), ("Description", str), ("Created Date", datetime.datetime),
                                             ("Modified Date", datetime.datetime), ("Accessed Date", datetime.datetime),
                                             ("Changed Date", datetime.datetime)],
                                  generator = self._generator(plugins_to_run))


Builds the configuration to save for the plugin such that it can be reconstructed .

def build_configuration(self):
        """Builds the configuration to save for the plugin such that it can be
        reconstructed."""
        vollog.warning("Unable to record configuration data for the timeliner plugin")
        return []


Constructs a plugin object based on the parameters .

def construct_plugin(context: interfaces.context.ContextInterface,
                     automagics: List[interfaces.automagic.AutomagicInterface],
                     plugin: Type[interfaces.plugins.PluginInterface], base_config_path: str,
                     progress_callback: constants.ProgressCallback,
                     open_method: Type[interfaces.plugins.FileHandlerInterface]) -> interfaces.plugins.PluginInterface:
    """Constructs a plugin object based on the parameters.

    Clever magic figures out how to fulfill each requirement that might not be fulfilled

    Args:
        context: The volatility context to operate on
        automagics: A list of automagic modules to run to augment the context
        plugin: The plugin to run
        base_config_path: The path within the context's config containing the plugin's configuration
        progress_callback: Callback function to provide feedback for ongoing processes
        open_method: class to provide context manager for opening the file

    Returns:
        The constructed plugin object
    """
    errors = automagic.run(automagics, context, plugin, base_config_path, progress_callback = progress_callback)
    # Plugins always get their configuration stored under their plugin name
    plugin_config_path = interfaces.configuration.path_join(base_config_path, plugin.__name__)

    # Check all the requirements and/or go back to the automagic step
    unsatisfied = plugin.unsatisfied(context, plugin_config_path)
    if unsatisfied:
        for error in errors:
            error_string = [x for x in error.format_exception_only()][-1]
            vollog.warning("Automagic exception occurred: {}".format(error_string[:-1]))
            vollog.log(constants.LOGLEVEL_V, "".join(error.format(chain = True)))
        raise exceptions.UnsatisfiedException(unsatisfied)

    constructed = plugin(context, plugin_config_path, progress_callback = progress_callback)
    if open_method:
        constructed.set_open_method(open_method)
    return constructed


def list_all_isf_files(cls) -> Generator[str, None, None]:
        """Lists all the ISF files that can be found"""
        for symbol_path in symbols.__path__:
            for root, dirs, files in os.walk(symbol_path, followlinks = True):
                for filename in files:
                    base_name = os.path.join(root, filename)
                    if filename.endswith('zip'):
                        with zipfile.ZipFile(base_name, 'r') as zfile:
                            for name in zfile.namelist():
                                for extension in constants.ISF_EXTENSIONS:
                                    # By ending with an extension (and therefore, not /), we should not return any directories
                                    if name.endswith(extension):
                                        yield "jar:file:" + str(pathlib.Path(base_name)) + "!" + name

                    else:
                        for extension in constants.ISF_EXTENSIONS:
                            if filename.endswith(extension):
                                yield pathlib.Path(base_name).as_uri()


def _get_banner(self, clazz: Type[symbol_cache.SymbolBannerCache], data: Any) -> str:
        """Gets a banner from an ISF file"""
        banner_symbol = data.get('symbols', {}).get(clazz.symbol_name, {}).get('constant_data',
                                                                               renderers.NotAvailableValue())
        if not isinstance(banner_symbol, interfaces.renderers.BaseAbsentValue):
            banner_symbol = str(base64.b64decode(banner_symbol), encoding = 'latin-1')
        return banner_symbol

def _get_banner ( self , clazz : Type [ symbol_cache . SymbolBannerCache ] , data : Any ) -> str : banner_symbol = data . get ( 'symbols' , { } ) . get ( clazz . symbol_name , { } ) . get ( 'constant_data' , renderers . NotAvailableValue ( ) ) if not isinstance ( banner_symbol , interfaces . renderers . BaseAbsentValue ) : banner_symbol = str ( base64 . b64decode ( banner_symbol ) , encoding = 'latin-1' ) return banner_symbol