Skip to content

Commit 7012edb

Browse files
authored
Merge pull request #885 from eve-mem/vmayarascan
add linux.vmayarascan
2 parents d56297c + faa8b2e commit 7012edb

File tree

2 files changed

+128
-3
lines changed

2 files changed

+128
-3
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# This file is Copyright 2023 Volatility Foundation and licensed under the Volatility Software License 1.0
2+
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
3+
#
4+
5+
from typing import Iterable, List, Tuple
6+
7+
from volatility3.framework import interfaces, renderers
8+
from volatility3.framework.configuration import requirements
9+
from volatility3.framework.renderers import format_hints
10+
from volatility3.plugins import yarascan
11+
from volatility3.plugins.linux import pslist
12+
13+
14+
class VmaYaraScan(interfaces.plugins.PluginInterface):
15+
"""Scans all virtual memory areas for tasks using yara."""
16+
17+
_required_framework_version = (2, 4, 0)
18+
_version = (1, 0, 0)
19+
20+
@classmethod
21+
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
22+
# create a list of requirements for vmayarascan
23+
vmayarascan_requirements = [
24+
requirements.ListRequirement(
25+
name="pid",
26+
element_type=int,
27+
description="Process IDs to include (all other processes are excluded)",
28+
optional=True,
29+
),
30+
requirements.PluginRequirement(
31+
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
32+
),
33+
requirements.PluginRequirement(
34+
name="yarascan", plugin=yarascan.YaraScan, version=(1, 2, 0)
35+
),
36+
requirements.VersionRequirement(
37+
name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0)
38+
),
39+
requirements.ModuleRequirement(
40+
name="kernel",
41+
description="Linux kernel",
42+
architectures=["Intel32", "Intel64"],
43+
),
44+
]
45+
46+
# get base yarascan requirements for command line options
47+
yarascan_requirements = yarascan.YaraScan.get_yarascan_option_requirements()
48+
49+
# return the combined requirements
50+
return yarascan_requirements + vmayarascan_requirements
51+
52+
def _generator(self):
53+
# use yarascan to parse the yara options provided and create the rules
54+
rules = yarascan.YaraScan.process_yara_options(dict(self.config))
55+
56+
# filter based on the pid option if provided
57+
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
58+
for task in pslist.PsList.list_tasks(
59+
context=self.context,
60+
vmlinux_module_name=self.config["kernel"],
61+
filter_func=filter_func,
62+
):
63+
# attempt to create a process layer for each task and skip those
64+
# that cannot (e.g. kernel threads)
65+
proc_layer_name = task.add_process_layer()
66+
if not proc_layer_name:
67+
continue
68+
69+
# get the proc_layer object from the context
70+
proc_layer = self.context.layers[proc_layer_name]
71+
72+
# scan the process layer with the yarascanner
73+
for offset, rule_name, name, value in proc_layer.scan(
74+
context=self.context,
75+
scanner=yarascan.YaraScanner(rules=rules),
76+
sections=self.get_vma_maps(task),
77+
):
78+
yield 0, (
79+
format_hints.Hex(offset),
80+
task.tgid,
81+
rule_name,
82+
name,
83+
value,
84+
)
85+
86+
@staticmethod
87+
def get_vma_maps(
88+
task: interfaces.objects.ObjectInterface,
89+
) -> Iterable[Tuple[int, int]]:
90+
"""Creates a map of start/end addresses for each virtual memory area in a task.
91+
92+
Args:
93+
task: The task object of which to read the vmas from
94+
95+
Returns:
96+
An iterable of tuples containing start and end addresses for each descriptor
97+
"""
98+
if task.mm:
99+
for vma in task.mm.get_vma_iter():
100+
vm_size = vma.vm_end - vma.vm_start
101+
yield (vma.vm_start, vm_size)
102+
103+
def run(self):
104+
return renderers.TreeGrid(
105+
[
106+
("Offset", format_hints.Hex),
107+
("PID", int),
108+
("Rule", str),
109+
("Component", str),
110+
("Value", bytes),
111+
],
112+
self._generator(),
113+
)

volatility3/framework/plugins/yarascan.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,31 @@ class YaraScan(plugins.PluginInterface):
6161
"""Scans kernel memory using yara rules (string or file)."""
6262

6363
_required_framework_version = (2, 0, 0)
64-
_version = (1, 1, 0)
64+
_version = (1, 2, 0)
6565

6666
# TODO: When the major version is bumped, take the opportunity to rename the yara_rules config to yara_string
6767
# or something that makes more sense
6868

6969
@classmethod
7070
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
71-
return [
71+
"""Returns the requirements needed to run yarascan directly, combining the TranslationLayerRequirement
72+
and the requirements from get_yarascan_option_requirements."""
73+
return cls.get_yarascan_option_requirements() + [
7274
requirements.TranslationLayerRequirement(
7375
name="primary",
7476
description="Memory layer for the kernel",
7577
architectures=["Intel32", "Intel64"],
76-
),
78+
)
79+
]
80+
81+
@classmethod
82+
def get_yarascan_option_requirements(
83+
cls,
84+
) -> List[interfaces.configuration.RequirementInterface]:
85+
"""Returns the requirements needed for the command lines options used by yarascan. This can
86+
then also be used by other plugins that are using yarascan. This does not include a
87+
TranslationLayerRequirement or a ModuleRequirement."""
88+
return [
7789
requirements.BooleanRequirement(
7890
name="insensitive",
7991
description="Makes the search case insensitive",

0 commit comments

Comments
 (0)