mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-20 21:52:21 +02:00
158 lines
5.7 KiB
Python
158 lines
5.7 KiB
Python
# This file was contributed to the Volatility Framework Version 3.
|
|
# Copyright (C) 2018 Volatility Foundation.
|
|
#
|
|
# THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE Volatility Contributors
|
|
# Public License V1.0("LICENSE") AS FIRST COMPLETED BY: Volatility Foundation,
|
|
# Inc. ANY USE, PUBLIC DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION
|
|
# OF, OR PREPARATION OF SUBSEQUENT WORKS, DERIVATIVE WORKS OR DERIVED WORKS BASED
|
|
# ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS
|
|
# TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THE LICENSE. "LICENSED
|
|
# WORK,” “RECIPIENT" AND “DISTRIBUTOR" ARE DEFINED IN THE LICENSE. A COPY OF THE
|
|
# LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.txt" ACCOMPANYING THE
|
|
# CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS FILE, A
|
|
# COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE:
|
|
# https://www.volatilityfoundation.org/license/vcpl_v1.0
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
|
|
# specific language governing rights and limitations under the License.
|
|
#
|
|
|
|
import datetime
|
|
import logging
|
|
import sys
|
|
from typing import Callable, Any
|
|
|
|
from volatility.framework.renderers import format_hints
|
|
|
|
vollog = logging.getLogger(__name__)
|
|
|
|
try:
|
|
CAPSTONE_PRESENT = True
|
|
import capstone
|
|
except ImportError:
|
|
CAPSTONE_PRESENT = False
|
|
vollog.debug("Disassembly library capstone not found")
|
|
|
|
from volatility.framework import interfaces, renderers
|
|
|
|
|
|
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
|
|
|
|
|
|
class Optional(object):
|
|
|
|
def __init__(self, func: Callable[[Any], str]) -> None:
|
|
self._func = func
|
|
|
|
def __call__(self, x: Any) -> str:
|
|
if isinstance(x, interfaces.renderers.BaseAbsentValue):
|
|
if isinstance(x, renderers.NotApplicableValue):
|
|
return "N/A"
|
|
else:
|
|
return "-"
|
|
return self._func(x)
|
|
|
|
|
|
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)
|
|
|
|
|
|
class QuickTextRenderer(interfaces.renderers.Renderer):
|
|
type_renderers = {
|
|
format_hints.Bin: Optional(lambda x: "0b{:b}".format(x)),
|
|
format_hints.Hex: Optional(lambda x: "0x{:x}".format(x)),
|
|
format_hints.HexBytes: Optional(hex_bytes_as_text),
|
|
interfaces.renderers.Disassembly: Optional(display_disassembly),
|
|
bytes: Optional(lambda x: " ".join(["{0:2x}".format(b) for b in x])),
|
|
datetime.datetime: Optional(lambda x: x.strftime("%Y-%m-%d %H:%M:%S.%f %Z")),
|
|
'default': Optional(lambda x: "{}".format(x))
|
|
}
|
|
|
|
def __init__(self, options = None) -> None:
|
|
super().__init__(options)
|
|
|
|
def get_render_options(self):
|
|
pass
|
|
|
|
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, 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))
|
|
line = []
|
|
for column in grid.columns:
|
|
renderer = self.type_renderers.get(column.type, self.type_renderers['default'])
|
|
line.append(renderer(node.values[column.index]))
|
|
accumulator.write("{}".format("\t".join(line)))
|
|
return accumulator
|
|
|
|
grid.populate(visitor, outfd)
|
|
|
|
outfd.write("\n")
|