From bb4cfb6e00a9457081ebf4242c0f18996c4c1d08 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 7 Jul 2024 15:13:06 +0100 Subject: [PATCH] Initial draft of complete-vad vadyarascan --- .../framework/plugins/windows/vadyarascan.py | 52 ++++++++++++------- volatility3/framework/plugins/yarascan.py | 15 ++++-- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/volatility3/framework/plugins/windows/vadyarascan.py b/volatility3/framework/plugins/windows/vadyarascan.py index d795818e9..81530ec67 100644 --- a/volatility3/framework/plugins/windows/vadyarascan.py +++ b/volatility3/framework/plugins/windows/vadyarascan.py @@ -2,10 +2,11 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import io import logging from typing import Iterable, List, Tuple -from volatility3.framework import interfaces, renderers +from volatility3.framework import exceptions, interfaces, renderers from volatility3.framework.configuration import requirements from volatility3.framework.renderers import format_hints from volatility3.plugins import yarascan @@ -18,7 +19,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface): """Scans all the Virtual Address Descriptor memory maps using yara.""" _required_framework_version = (2, 4, 0) - _version = (1, 0, 1) + _version = (1, 1, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -32,11 +33,8 @@ class VadYaraScan(interfaces.plugins.PluginInterface): requirements.PluginRequirement( name="pslist", plugin=pslist.PsList, version=(2, 0, 0) ), - requirements.VersionRequirement( - name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0) - ), requirements.PluginRequirement( - name="yarascan", plugin=yarascan.YaraScan, version=(1, 2, 0) + name="yarascan", plugin=yarascan.YaraScan, version=(1, 3, 0) ), requirements.ListRequirement( name="pid", @@ -59,6 +57,8 @@ class VadYaraScan(interfaces.plugins.PluginInterface): filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + sanity_check = 0x1000 * 0x1000 * 0x1000 + for task in pslist.PsList.list_processes( context=self.context, layer_name=kernel.layer_name, @@ -67,18 +67,34 @@ class VadYaraScan(interfaces.plugins.PluginInterface): ): layer_name = task.add_process_layer() layer = self.context.layers[layer_name] - for offset, rule_name, name, value in layer.scan( - context=self.context, - scanner=yarascan.YaraScanner(rules=rules), - sections=self.get_vad_maps(task), - ): - yield 0, ( - format_hints.Hex(offset), - task.UniqueProcessId, - rule_name, - name, - value, - ) + for start, end in self.get_vad_maps(task): + size = end - start + if size > sanity_check: + vollog.warn( + f"VAD at 0x{start:x} over sanity-check size, not scanning" + ) + continue + + for match in rules.match(data=layer.read(start, end - start, True)): + if yarascan.YaraScan.yara_returns_instances(): + for match_string in match.strings: + for instance in match_string.instances: + yield 0, ( + format_hints.Hex(instance.offset + start), + task.UniqueProcessId, + match.rule, + match_string.identifier, + instance.matched_data, + ) + else: + for offset, name, value in match.strings: + yield 0, ( + format_hints.Hex(offset + start), + task.UniqueProcessId, + match.rule, + name, + value, + ) @staticmethod def get_vad_maps( diff --git a/volatility3/framework/plugins/yarascan.py b/volatility3/framework/plugins/yarascan.py index 11c708607..887b6027c 100644 --- a/volatility3/framework/plugins/yarascan.py +++ b/volatility3/framework/plugins/yarascan.py @@ -2,10 +2,11 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import io import logging from typing import Any, Dict, Iterable, List, Tuple -from volatility3.framework import interfaces, renderers +from volatility3.framework import exceptions, interfaces, renderers from volatility3.framework.configuration import requirements from volatility3.framework.interfaces import plugins from volatility3.framework.layers import resources @@ -43,7 +44,7 @@ class YaraScanner(interfaces.layers.ScannerInterface): self, data: bytes, data_offset: int ) -> Iterable[Tuple[int, str, str, bytes]]: for match in self._rules.match(data=data): - if self.st_object: + if YaraScan.yara_returns_instances(): for match_string in match.strings: for instance in match_string.instances: yield ( @@ -61,7 +62,7 @@ class YaraScan(plugins.PluginInterface): """Scans kernel memory using yara rules (string or file).""" _required_framework_version = (2, 0, 0) - _version = (1, 2, 0) + _version = (1, 3, 0) # TODO: When the major version is bumped, take the opportunity to rename the yara_rules config to yara_string # or something that makes more sense @@ -119,6 +120,14 @@ class YaraScan(plugins.PluginInterface): ), ] + @classmethod + def yara_returns_instances(self) -> bool: + st_object = not tuple([int(x) for x in yara.__version__.split(".")]) < ( + 4, + 3, + ) + return st_object + @classmethod def process_yara_options(cls, config: Dict[str, Any]): rules = None