/
KuzminVD
/
Dumpy
Обзор
Документация
Войти
/
KuzminVD
/
Dumpy
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
TreeGridParser.py
234 строки
8 KB
vladimir
Load code
03 июн 2024, 00:08
03 июн 2024, 00:08
f404d0f
Код
Авторство
О чём код?
import datetime import json import logging import sys from functools import wraps from typing import Any, Callable, Dict, List, Tuple from volatility3.framework import interfaces, renderers from volatility3.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") 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(f"hex_bytes_as_text takes bytes not: {type(value)}") ascii = [] hex = [] count = 0 output = "" for byte in value: hex.append(f"{byte:02x}") 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 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 optional(func: Callable) -> Callable: @wraps(func) def wrapped(x: Any) -> str: if isinstance(x, interfaces.renderers.BaseAbsentValue): if isinstance(x, renderers.NotApplicableValue): return "N/A" else: return "-" return func(x) return wrapped def quoted_optional(func: Callable) -> Callable: @wraps(func) def wrapped(x: Any) -> str: result = optional(func)(x) if result == "-" or result == "N/A": return "" if isinstance(x, format_hints.MultiTypeData) and x.converted_int: return f"{result}" if isinstance(x, int) and not isinstance( x, (format_hints.Hex, format_hints.Bin) ): return f"{result}" return f'"{result}"' return wrapped 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 rendered 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 += f"\n0x{i.address:x}:\t{i.mnemonic}\t{i.op_str}" return output return ArrayParser._type_renderers[bytes](disasm.data) class ArrayParser(): _type_renderers = { format_hints.Bin: optional(lambda x: f"0b{x:b}"), format_hints.Hex: optional(lambda x: f"0x{x:x}"), format_hints.HexBytes: optional(hex_bytes_as_text), format_hints.MultiTypeData: optional(multitypedata_as_text), interfaces.renderers.Disassembly: optional(display_disassembly), bytes: optional(lambda x: " ".join([f"{b:02x}" for b in x])), datetime.datetime: optional(lambda x: x.strftime("%Y-%m-%d %H:%M:%S.%f %Z")), "default": optional(lambda x: f"{x}"), } name = "csv" structured_output = True def get_render_options(self): pass def parse(self, grid: interfaces.renderers.TreeGrid): headers = ["TreeDepth"] result = [] for column in grid.columns: # Ignore the type because named tuples don't realize they have accessible attributes headers.append(f"{column.name}") def visitor(node: interfaces.renderers.TreeNode, _accumulator): # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case row = {"TreeDepth": str(max(0, node.path_depth - 1))} for column_index in range(len(grid.columns)): column = grid.columns[column_index] renderer = self._type_renderers.get( column.type, self._type_renderers["default"] ) row[f"{column.name}"] = renderer(node.values[column_index]) result.append(row) if not grid.populated: grid.populate(visitor, None) else: grid.visit(node=None, function=visitor, initial_accumulator=None) return headers, result class JsonRenderer(): _type_renderers = { format_hints.HexBytes: quoted_optional(hex_bytes_as_text), interfaces.renderers.Disassembly: quoted_optional(display_disassembly), format_hints.MultiTypeData: quoted_optional(multitypedata_as_text), bytes: optional(lambda x: " ".join([f"{b:02x}" for b in x])), datetime.datetime: lambda x: ( x.isoformat() if not isinstance(x, interfaces.renderers.BaseAbsentValue) else None ), "default": lambda x: x, } name = "JSON" structured_output = True def get_render_options(self) -> List[interfaces.renderers.RenderOption]: pass def output_result(self, outfd, result): """Outputs the JSON data to a file in a particular format""" outfd.write("{}\n".format(json.dumps(result, indent=2, sort_keys=True))) def Parse(self, grid: interfaces.renderers.TreeGrid): outfd = sys.stdout outfd.write("\n") final_output: Tuple[ Dict[str, List[interfaces.renderers.TreeNode]], List[interfaces.renderers.TreeNode], ] = ({}, []) def visitor( node: interfaces.renderers.TreeNode, accumulator: Tuple[Dict[str, Dict[str, Any]], List[Dict[str, Any]]], ) -> Tuple[Dict[str, Dict[str, Any]], List[Dict[str, Any]]]: # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case acc_map, final_tree = accumulator node_dict: Dict[str, Any] = {"__children": []} 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(list(node.values)[column_index]) if isinstance(data, interfaces.renderers.BaseAbsentValue): data = None node_dict[column.name] = data if node.parent: acc_map[node.parent.path]["__children"].append(node_dict) else: final_tree.append(node_dict) acc_map[node.path] = node_dict return (acc_map, final_tree) if not grid.populated: grid.populate(visitor, final_output) else: grid.visit(node=None, function=visitor, initial_accumulator=final_output) self.output_result(outfd, final_output[1]) class JsonLinesRenderer(JsonRenderer): name = "JSONL" 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")