both yara-python and yara-x support

This commit is contained in:
Davide Arcuri
2024-07-22 13:58:33 +02:00
parent 0b84a5627b
commit ce9832a2b3
3 changed files with 95 additions and 8 deletions
+1
View File
@@ -3,6 +3,7 @@ sphinx>=4.0.0,<7
sphinx_autodoc_typehints>=1.4.0
sphinx-rtd-theme>=0.4.3
yara-python
yara-x
pycryptodome
pefile
+1
View File
@@ -5,6 +5,7 @@ pefile>=2017.8.1 #foo
# If certain packages are not necessary, place a comment (#) at the start of the line.
# This is required for the yara plugins
yara-python>=3.8.0
yara-x>=0.5.0
pytest>=7.0.0
+93 -8
View File
@@ -13,14 +13,73 @@ from volatility3.framework.renderers import format_hints
vollog = logging.getLogger(__name__)
USE_YARA_X = False
try:
import yara_x
USE_YARA_X = True
except ImportError:
raise
try:
import yara
if tuple(int(x) for x in yara.__version__.split(".")) < (3, 8):
raise ImportError
vollog.info("Using yara-python module")
except ImportError:
vollog.info(
"Python Yara (>3.8.0) module not found, plugin (and dependent plugins) not available"
)
raise
class YaraScanner(interfaces.layers.ScannerInterface):
class YaraPythonScanner(interfaces.layers.ScannerInterface):
_version = (2, 0, 0)
# yara.Rules isn't exposed, so we can't type this properly
def __init__(self, rules) -> None:
super().__init__()
if rules is None:
raise ValueError("No rules provided to YaraScanner")
self._rules = rules
self.st_object = not tuple(int(x) for x in yara.__version__.split(".")) < (4, 3)
def __call__(
self, data: bytes, data_offset: int
) -> Iterable[Tuple[int, str, str, bytes]]:
for match in self._rules.match(data=data):
if self.st_object:
for match_string in match.strings:
for instance in match_string.instances:
yield (
instance.offset + data_offset,
match.rule,
match_string.identifier,
instance.matched_data,
)
else:
for offset, name, value in match.strings:
yield (offset + data_offset, match.rule, name, value)
@staticmethod
def get_rule(rule):
return yara.compile(
sources={"n": f"rule r1 {{strings: $a = {rule} condition: $a}}"}
)
@staticmethod
def from_compiled_file(filepath):
return yara.load(file=resources.ResourceAccessor().open(filepath, "rb"))
@staticmethod
def from_file(filepath):
return yara.compile(file=resources.ResourceAccessor().open(filepath, "rb"))
class YaraXScanner(interfaces.layers.ScannerInterface):
_version = (2, 0, 0)
# yara.Rules isn't exposed, so we can't type this properly
@@ -44,6 +103,25 @@ class YaraScanner(interfaces.layers.ScannerInterface):
data[instance.offset : instance.offset + instance.length],
)
@staticmethod
def get_rule(rule):
return yara_x.compile(f"rule r1 {{strings: $a = {rule} condition: $a}}")
@staticmethod
def from_compiled_file(filepath):
return yara_x.Rules.deserialize_from(
file=resources.ResourceAccessor().open(filepath, "rb")
)
@staticmethod
def from_file(filepath):
return yara_x.compile(
resources.ResourceAccessor().open(filepath, "rb").read().decode()
)
YaraScanner = YaraXScanner if USE_YARA_X else YaraPythonScanner
class YaraScan(plugins.PluginInterface):
"""Scans kernel memory using yara rules (string or file)."""
@@ -88,9 +166,14 @@ class YaraScan(plugins.PluginInterface):
description="Yara rules (as a string)",
optional=True,
),
requirements.URIRequirement(
name="yara_file",
description="Yara rules (as a file)",
optional=True,
),
requirements.URIRequirement(
name="yara_compiled_file",
description="Yara-x compiled rules (as a file)",
description="Yara compiled rules (as a file)",
optional=True,
),
requirements.IntRequirement(
@@ -112,13 +195,15 @@ class YaraScan(plugins.PluginInterface):
rule += " nocase"
if config.get("wide", False):
rule += " wide ascii"
rules = yara_x.compile(f"rule r1 {{strings: $a = {rule} condition: $a}}")
rules = YaraScanner.get_rule(rule)
elif config.get("yara_file") is not None:
vollog.debug(f"Plain file: {config["yara_file"]} - yara-x: {USE_YARA_X}")
rules = YaraScanner.from_file(config["yara_file"])
elif config.get("yara_compiled_file") is not None:
rules = yara_x.Rules.deserialize_from(
file=resources.ResourceAccessor().open(
config["yara_compiled_file"], "rb"
)
vollog.debug(
f"Compiled file: {config["yara_compiled_file"]} - yara-x: {USE_YARA_X}"
)
rules = YaraScanner.from_compiled_file(config["yara_compiled_file"])
else:
vollog.error("No yara rules, nor yara rules file were specified")
return rules