Merge branch 'volatilityfoundation:develop' into linux_issue_1089_module_memory

This commit is contained in:
Eve
2024-02-02 14:12:29 +00:00
committed by GitHub
41 changed files with 2005 additions and 296 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ The latest generated copy of the documentation can be found at: <https://volatil
## Licensing and Copyright
Copyright (C) 2007-2023 Volatility Foundation
Copyright (C) 2007-2024 Volatility Foundation
All Rights Reserved
+1 -1
View File
@@ -169,7 +169,7 @@ master_doc = "index"
# General information about the project.
project = "Volatility 3"
copyright = "2012-2022, Volatility Foundation"
copyright = "2012-2024, Volatility Foundation"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
+5 -3
View File
@@ -389,9 +389,11 @@ class JsonRenderer(CLIRenderer):
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,
datetime.datetime: lambda x: (
x.isoformat()
if not isinstance(x, interfaces.renderers.BaseAbsentValue)
else None
),
"default": lambda x: x,
}
+3 -3
View File
@@ -206,9 +206,9 @@ def _zipwalk(path: str):
if not file.is_dir():
dirlist = zip_results.get(os.path.dirname(file.filename), [])
dirlist.append(os.path.basename(file.filename))
zip_results[
os.path.join(path, os.path.dirname(file.filename))
] = dirlist
zip_results[os.path.join(path, os.path.dirname(file.filename))] = (
dirlist
)
for value in zip_results:
yield value, zip_results[value]
+3 -3
View File
@@ -138,9 +138,9 @@ class MacIntelStacker(interfaces.automagic.StackerLayerInterface):
config_path = join("automagic", "MacIntelHelper", new_layer_name)
context.config[join(config_path, "memory_layer")] = layer_name
context.config[join(config_path, "page_map_offset")] = dtb
context.config[
join(config_path, MacSymbolFinder.banner_config_key)
] = str(banner, "latin-1")
context.config[join(config_path, MacSymbolFinder.banner_config_key)] = (
str(banner, "latin-1")
)
new_layer = intel.Intel32e(
context,
+3 -3
View File
@@ -34,9 +34,9 @@ class KernelModule(interfaces.automagic.AutomagicInterface):
return None
# The requirement is unfulfilled and is a ModuleRequirement
context.config[
interfaces.configuration.path_join(new_config_path, "class")
] = "volatility3.framework.contexts.Module"
context.config[interfaces.configuration.path_join(new_config_path, "class")] = (
"volatility3.framework.contexts.Module"
)
for req in requirement.requirements:
if (
@@ -150,12 +150,12 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface):
clazz = self.symbol_class
# Set the discovered options
path_join = interfaces.configuration.path_join
context.config[
path_join(config_path, requirement.name, "class")
] = clazz
context.config[
path_join(config_path, requirement.name, "isf_url")
] = isf_path
context.config[path_join(config_path, requirement.name, "class")] = (
clazz
)
context.config[path_join(config_path, requirement.name, "isf_url")] = (
isf_path
)
context.config[
path_join(config_path, requirement.name, "symbol_mask")
] = layer.address_mask
+10 -10
View File
@@ -402,19 +402,19 @@ class WinSwapLayers(interfaces.automagic.AutomagicInterface):
if swap_location:
context.config[current_layer_path] = current_layer_name
try:
context.config[
layer_loc_path
] = requirements.URIRequirement.location_from_file(
swap_location
context.config[layer_loc_path] = (
requirements.URIRequirement.location_from_file(
swap_location
)
)
except ValueError:
vollog.warning(
f"Volatility swap_location {swap_location} could not be validated - swap layer disabled"
)
continue
context.config[
layer_class_path
] = "volatility3.framework.layers.physical.FileLayer"
context.config[layer_class_path] = (
"volatility3.framework.layers.physical.FileLayer"
)
# Add the requirement
new_req = requirements.TranslationLayerRequirement(
@@ -424,9 +424,9 @@ class WinSwapLayers(interfaces.automagic.AutomagicInterface):
)
swap_req.add_requirement(new_req)
context.config[
path_join(swap_sub_config, "number_of_elements")
] = counter
context.config[path_join(swap_sub_config, "number_of_elements")] = (
counter
)
context.config[swap_sub_config] = True
swap_req.construct(context, swap_config)
@@ -550,9 +550,9 @@ class VersionRequirement(interfaces.configuration.RequirementInterface):
config_path = interfaces.configuration.path_join(config_path, self.name)
if not self.matches_required(self._version, self._component.version):
return {config_path: self}
context.config[
interfaces.configuration.path_join(config_path, self.name)
] = True
context.config[interfaces.configuration.path_join(config_path, self.name)] = (
True
)
return {}
@classmethod
+1 -1
View File
@@ -87,7 +87,7 @@ class ContextInterface(metaclass=ABCMeta):
offset: int,
native_layer_name: str = None,
**arguments,
):
) -> "interfaces.objects.ObjectInterface":
"""Object factory, takes a context, symbol, offset and optional
layer_name.
+3 -3
View File
@@ -224,7 +224,7 @@ class AVMLStacker(interfaces.automagic.StackerLayerInterface):
except exceptions.LayerException:
return None
new_name = context.layers.free_layer_name("AVMLLayer")
context.config[
interfaces.configuration.path_join(new_name, "base_layer")
] = layer_name
context.config[interfaces.configuration.path_join(new_name, "base_layer")] = (
layer_name
)
return AVMLLayer(context, new_name, new_name)
@@ -20,7 +20,6 @@ try:
except ImportError:
HAS_GCSFS = False
from volatility3.framework import exceptions
from volatility3.framework.layers import resources
vollog = logging.getLogger(__file__)
+3 -3
View File
@@ -115,9 +115,9 @@ class Elf64Stacker(interfaces.automagic.StackerLayerInterface):
vollog.log(constants.LOGLEVEL_VVVV, f"Exception: {excp}")
return None
new_name = context.layers.free_layer_name("Elf64Layer")
context.config[
interfaces.configuration.path_join(new_name, "base_layer")
] = layer_name
context.config[interfaces.configuration.path_join(new_name, "base_layer")] = (
layer_name
)
try:
return Elf64Layer(context, new_name, new_name)
+3 -3
View File
@@ -277,9 +277,9 @@ class Intel(linear.LinearlyMappedLayer):
This allows translation layers to provide maps of contiguous
regions in one layer
"""
stashed_offset = (
stashed_mapped_offset
) = stashed_size = stashed_mapped_size = stashed_map_layer = None
stashed_offset = stashed_mapped_offset = stashed_size = stashed_mapped_size = (
stashed_map_layer
) = None
for offset, size, mapped_offset, mapped_size, map_layer in self._mapping(
offset, length, ignore_errors
):
+3 -3
View File
@@ -104,7 +104,7 @@ class LimeStacker(interfaces.automagic.StackerLayerInterface):
except LimeFormatException:
return None
new_name = context.layers.free_layer_name("LimeLayer")
context.config[
interfaces.configuration.path_join(new_name, "base_layer")
] = layer_name
context.config[interfaces.configuration.path_join(new_name, "base_layer")] = (
layer_name
)
return LimeLayer(context, new_name, new_name)
+3 -3
View File
@@ -486,9 +486,9 @@ class QemuStacker(interfaces.automagic.StackerLayerInterface):
except exceptions.LayerException:
return None
new_name = context.layers.free_layer_name("QemuSuspendLayer")
context.config[
interfaces.configuration.path_join(new_name, "base_layer")
] = layer_name
context.config[interfaces.configuration.path_join(new_name, "base_layer")] = (
layer_name
)
layer = QemuSuspendLayer(context, new_name, new_name)
cls.stacker_slow_warning()
return layer
+3 -3
View File
@@ -173,8 +173,8 @@ class XenCoreDumpStacker(elf.Elf64Stacker):
vollog.log(constants.LOGLEVEL_VVVV, f"Exception: {excp}")
return None
new_name = context.layers.free_layer_name("XenCoreDumpLayer")
context.config[
interfaces.configuration.path_join(new_name, "base_layer")
] = layer_name
context.config[interfaces.configuration.path_join(new_name, "base_layer")] = (
layer_name
)
return XenCoreDumpLayer(context, new_name, new_name)
+2 -4
View File
@@ -768,12 +768,10 @@ class Array(interfaces.objects.ObjectInterface, collections.abc.Sequence):
raise IndexError(f"Member not present in array template: {child}")
@overload
def __getitem__(self, i: int) -> interfaces.objects.Template:
...
def __getitem__(self, i: int) -> interfaces.objects.Template: ...
@overload
def __getitem__(self, s: slice) -> List[interfaces.objects.Template]:
...
def __getitem__(self, s: slice) -> List[interfaces.objects.Template]: ...
def __getitem__(self, i):
"""Returns the i-th item from the array."""
+115 -6
View File
@@ -1,12 +1,121 @@
from volatility3.plugins.linux import envvars
# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from volatility3.framework import exceptions, renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.framework.objects import utility
from volatility3.plugins.linux import pslist
vollog = logging.getLogger(__name__)
class Envars(envvars.Envvars):
def run(self, *args, **kwargs):
vollog.warning(
"The linux.envars plugin has been renamed to linux.envvars and will only be accessible through the new name in a future release"
class Envars(plugins.PluginInterface):
"""Lists processes with their environment variables"""
_required_framework_version = (2, 0, 0)
@classmethod
def get_requirements(cls):
# Since we're calling the plugin, make sure we have the plugin's requirements
return [
requirements.ModuleRequirement(
name="kernel",
description="Linux kernel",
architectures=["Intel32", "Intel64"],
),
requirements.PluginRequirement(
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
),
requirements.ListRequirement(
name="pid",
description="Filter on specific process IDs",
element_type=int,
optional=True,
),
]
def _generator(self, tasks):
"""Generates a listing of processes along with environment variables"""
# walk the process list and return the envars
for task in tasks:
pid = task.pid
# get process name as string
name = utility.array_to_string(task.comm)
# try and get task parent
try:
ppid = task.parent.pid
except exceptions.InvalidAddressException:
vollog.debug(
f"Unable to read parent pid for task {pid} {name}, setting ppid to 0."
)
ppid = 0
# kernel threads never have an mm as they do not have userland mappings
try:
mm = task.mm
except exceptions.InvalidAddressException:
# no mm so cannot get envars
vollog.debug(
f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars."
)
mm = None
continue
# if mm exists attempt to get envars
if mm:
# get process layer to read envars from
proc_layer_name = task.add_process_layer()
if proc_layer_name is None:
vollog.debug(
f"Unable to construct process layer for task {pid} {name}, will not extract any envars."
)
continue
proc_layer = self.context.layers[proc_layer_name]
# get the size of the envars with sanity checking
envars_size = task.mm.env_end - task.mm.env_start
if not (0 < envars_size <= 8192):
vollog.debug(
f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars."
)
continue
# attempt to read all envars data
try:
envar_data = proc_layer.read(task.mm.env_start, envars_size)
except exceptions.InvalidAddressException:
vollog.debug(
f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars."
)
continue
# parse envar data, envars are null terminated, keys and values are separated by '='
envar_data = envar_data.rstrip(b"\x00")
for envar_pair in envar_data.split(b"\x00"):
try:
key, value = envar_pair.decode().split("=", 1)
except ValueError:
vollog.debug(
f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated"
)
continue
yield (0, (pid, ppid, name, key, value))
def run(self):
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
return renderers.TreeGrid(
[("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)],
self._generator(
pslist.PsList.list_tasks(
self.context, self.config["kernel"], filter_func=filter_func
)
),
)
return super().run(*args, **kwargs)
@@ -1,121 +0,0 @@
# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from volatility3.framework import exceptions, renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.framework.objects import utility
from volatility3.plugins.linux import pslist
vollog = logging.getLogger(__name__)
class Envvars(plugins.PluginInterface):
"""Lists processes with their environment variables"""
_required_framework_version = (2, 0, 0)
@classmethod
def get_requirements(cls):
# Since we're calling the plugin, make sure we have the plugin's requirements
return [
requirements.ModuleRequirement(
name="kernel",
description="Linux kernel",
architectures=["Intel32", "Intel64"],
),
requirements.PluginRequirement(
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
),
requirements.ListRequirement(
name="pid",
description="Filter on specific process IDs",
element_type=int,
optional=True,
),
]
def _generator(self, tasks):
"""Generates a listing of processes along with environment variables"""
# walk the process list and return the envars
for task in tasks:
pid = task.pid
# get process name as string
name = utility.array_to_string(task.comm)
# try and get task parent
try:
ppid = task.parent.pid
except exceptions.InvalidAddressException:
vollog.debug(
f"Unable to read parent pid for task {pid} {name}, setting ppid to 0."
)
ppid = 0
# kernel threads never have an mm as they do not have userland mappings
try:
mm = task.mm
except exceptions.InvalidAddressException:
# no mm so cannot get envars
vollog.debug(
f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars."
)
mm = None
continue
# if mm exists attempt to get envars
if mm:
# get process layer to read envars from
proc_layer_name = task.add_process_layer()
if proc_layer_name is None:
vollog.debug(
f"Unable to construct process layer for task {pid} {name}, will not extract any envars."
)
continue
proc_layer = self.context.layers[proc_layer_name]
# get the size of the envars with sanity checking
envars_size = task.mm.env_end - task.mm.env_start
if not (0 < envars_size <= 8192):
vollog.debug(
f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars."
)
continue
# attempt to read all envars data
try:
envar_data = proc_layer.read(task.mm.env_start, envars_size)
except exceptions.InvalidAddressException:
vollog.debug(
f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars."
)
continue
# parse envar data, envars are null terminated, keys and values are separated by '='
envar_data = envar_data.rstrip(b"\x00")
for envar_pair in envar_data.split(b"\x00"):
try:
key, value = envar_pair.decode().split("=", 1)
except ValueError:
vollog.debug(
f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated"
)
continue
yield (0, (pid, ppid, name, key, value))
def run(self):
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
return renderers.TreeGrid(
[("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)],
self._generator(
pslist.PsList.list_tasks(
self.context, self.config["kernel"], filter_func=filter_func
)
),
)
+1 -3
View File
@@ -49,9 +49,7 @@ class PsList(interfaces.plugins.PluginInterface):
]
@classmethod
def get_list_tasks(
cls, method: str
) -> Callable[
def get_list_tasks(cls, method: str) -> Callable[
[interfaces.context.ContextInterface, str, Callable[[int], bool]],
Iterable[interfaces.objects.ObjectInterface],
]:
@@ -46,9 +46,9 @@ class Crashinfo(interfaces.plugins.PluginInterface):
bitmap_size = format_hints.Hex(summary_header.BitmapSize)
bitmap_pages = format_hints.Hex(summary_header.Pages)
else:
bitmap_header_size = (
bitmap_size
) = bitmap_pages = renderers.NotApplicableValue()
bitmap_header_size = bitmap_size = bitmap_pages = (
renderers.NotApplicableValue()
)
yield (
0,
@@ -4,11 +4,12 @@
import logging
import ntpath
import re
from typing import List, Tuple, Type, Optional, Generator
from volatility3.framework import interfaces, renderers, exceptions, constants
from volatility3.framework.configuration import requirements
from volatility3.framework.renderers import format_hints
from volatility3.framework.renderers import format_hints, UnreadableValue
from volatility3.plugins.windows import handles
from volatility3.plugins.windows import pslist
@@ -53,6 +54,17 @@ class DumpFiles(interfaces.plugins.PluginInterface):
description="Dump a single _FILE_OBJECT at this physical address",
optional=True,
),
requirements.StringRequirement(
name="filter",
description="Dump files matching regular expression FILTER",
optional=True,
),
requirements.BooleanRequirement(
name="ignore-case",
description="Ignore case in filter match",
default=False,
optional=True,
),
requirements.VersionRequirement(
name="pslist", component=pslist.PsList, version=(2, 0, 0)
),
@@ -208,6 +220,10 @@ class DumpFiles(interfaces.plugins.PluginInterface):
def _generator(self, procs: List, offsets: List):
kernel = self.context.modules[self.config["kernel"]]
file_re = None
if self.config["filter"]:
flags = re.I if self.config["ignore-case"] else 0
file_re = re.compile(self.config["filter"], flags)
if procs:
# The handles plugin doesn't expose any staticmethod/classmethod, and it also requires stashing
@@ -243,6 +259,14 @@ class DumpFiles(interfaces.plugins.PluginInterface):
obj_type = entry.get_object_type(type_map, cookie)
if obj_type == "File":
file_obj = entry.Body.cast("_FILE_OBJECT")
if file_re:
name = file_obj.file_name_with_device()
if isinstance(name, UnreadableValue):
continue
if not file_re.search(name):
continue
for result in self.process_file_object(
self.context, kernel.layer_name, self.open, file_obj
):
@@ -272,6 +296,13 @@ class DumpFiles(interfaces.plugins.PluginInterface):
if not file_obj.is_valid():
continue
if file_re:
name = file_obj.file_name_with_device()
if isinstance(name, UnreadableValue):
continue
if not file_re.search(name):
continue
for result in self.process_file_object(
self.context, kernel.layer_name, self.open, file_obj
):
@@ -315,6 +346,11 @@ class DumpFiles(interfaces.plugins.PluginInterface):
procs = list()
kernel = self.context.modules[self.config["kernel"]]
if self.config["filter"] and (
self.config["virtaddr"] or self.config["physaddr"]
):
raise ValueError("Cannot use filter flag with an address flag")
if self.config.get("virtaddr", None) is not None:
offsets.append((self.config["virtaddr"], True))
elif self.config.get("physaddr", None) is not None:
@@ -141,16 +141,30 @@ class Malfind(interfaces.plugins.PluginInterface):
# determine if we're on a 32 or 64 bit kernel
kernel = self.context.modules[self.config["kernel"]]
# set refined criteria to know when to add to "Notes" column
refined_criteria = {
b"MZ": "MZ header",
b"\x55\x8B": "PE header",
b"\x55\x48": "Function prologue",
b"\x55\x89": "Function prologue",
}
is_32bit_arch = not symbols.symbol_table_is_64bit(
self.context, kernel.symbol_table_name
)
for proc in procs:
# by default, "Notes" column will be set to N/A
notes = renderers.NotApplicableValue()
process_name = utility.array_to_string(proc.ImageFileName)
for vad, data in self.list_injections(
self.context, kernel.layer_name, kernel.symbol_table_name, proc
):
# Check for unique headers and update "Notes" column if criteria is met
if data[0:2] in refined_criteria:
notes = refined_criteria[data[0:2]]
# if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64
if is_32bit_arch or proc.get_is_wow64():
architecture = "intel"
@@ -196,6 +210,7 @@ class Malfind(interfaces.plugins.PluginInterface):
vad.get_commit_charge(),
vad.get_private_memory(),
file_output,
notes,
format_hints.HexBytes(data),
disasm,
),
@@ -216,6 +231,7 @@ class Malfind(interfaces.plugins.PluginInterface):
("CommitCharge", int),
("PrivateMemory", int),
("File output", str),
("Notes", str),
("Hexdump", format_hints.HexBytes),
("Disasm", interfaces.renderers.Disassembly),
],
@@ -53,7 +53,6 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
# get each of the individual Field Sets
mft_object = symbol_table + constants.BANG + "MFT_ENTRY"
attribute_object = symbol_table + constants.BANG + "ATTRIBUTE"
header_object = symbol_table + constants.BANG + "ATTR_HEADER"
si_object = symbol_table + constants.BANG + "STANDARD_INFORMATION_ENTRY"
fn_object = symbol_table + constants.BANG + "FILE_NAME_ENTRY"
@@ -176,7 +175,6 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
class ADS(interfaces.plugins.PluginInterface):
"""Scans for Alternate Data Stream"""
_required_framework_version = (2, 0, 0)
@@ -487,10 +487,12 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
if not isinstance(row_data[9], datetime.datetime):
continue
row_data = [
"N/A"
if isinstance(i, renderers.UnreadableValue)
or isinstance(i, renderers.UnparsableValue)
else i
(
"N/A"
if isinstance(i, renderers.UnreadableValue)
or isinstance(i, renderers.UnparsableValue)
else i
)
for i in row_data
]
description = (
@@ -5,7 +5,7 @@ import datetime
import logging
from typing import Callable, Dict, Set, Tuple
from volatility3.framework import objects, interfaces, renderers
from volatility3.framework import objects, interfaces, renderers, exceptions
from volatility3.framework.configuration import requirements
from volatility3.framework.renderers import format_hints
from volatility3.plugins.windows import pslist
@@ -132,6 +132,25 @@ class PsTree(interfaces.plugins.PluginInterface):
proc.get_exit_time(),
)
try:
audit = proc.SeAuditProcessCreationInfo.ImageFileName.Name
# If 'audit' is set to the empty string, display NotAvailableValue
row += (audit.get_string() or renderers.NotAvailableValue(),)
except exceptions.InvalidAddressException:
row += (renderers.NotAvailableValue(),)
try:
process_params = proc.get_peb().ProcessParameters
row += (
process_params.CommandLine.get_string(),
process_params.ImagePathName.get_string(),
)
except exceptions.InvalidAddressException:
row += (
renderers.NotAvailableValue(),
renderers.NotAvailableValue(),
)
yield (self._levels[pid] - 1, row)
for child_pid in self._children.get(pid, []):
yield from yield_processes(
@@ -161,6 +180,9 @@ class PsTree(interfaces.plugins.PluginInterface):
("Wow64", bool),
("CreateTime", datetime.datetime),
("ExitTime", datetime.datetime),
("Audit", str),
("Cmd", str),
("Path", str),
],
self._generator(
filter_func=pslist.PsList.create_pid_filter(
@@ -193,9 +193,9 @@ class PrintKey(interfaces.plugins.PluginInterface):
vollog.debug(
"Couldn't read registry value type, so data is unreadable"
)
value_data: Union[
interfaces.renderers.BaseAbsentValue, bytes
] = renderers.UnreadableValue()
value_data: Union[interfaces.renderers.BaseAbsentValue, bytes] = (
renderers.UnreadableValue()
)
else:
try:
value_data = node.decode_data()
+179 -68
View File
@@ -4,25 +4,42 @@
import logging
import os
from typing import List
from typing import Dict, List, NamedTuple, Optional, Tuple, Union, cast
from volatility3.framework import interfaces, renderers, constants, symbols, exceptions
from volatility3.framework import (
constants,
exceptions,
interfaces,
objects,
renderers,
symbols,
)
from volatility3.framework.configuration import requirements
from volatility3.framework.layers import scanners
from volatility3.framework.renderers import format_hints
from volatility3.framework.symbols import intermed
from volatility3.framework.symbols.windows import versions
from volatility3.framework.symbols.windows.extensions import services
from volatility3.plugins.windows import poolscanner, vadyarascan, pslist
from volatility3.plugins.windows import poolscanner, pslist, vadyarascan
from volatility3.plugins.windows.registry import hivelist
vollog = logging.getLogger(__name__)
ServiceBinaryInfo = NamedTuple(
"ServiceBinaryInfo",
[
("dll", Union[str, interfaces.renderers.BaseAbsentValue]),
("binary", Union[str, interfaces.renderers.BaseAbsentValue]),
],
)
class SvcScan(interfaces.plugins.PluginInterface):
"""Scans for windows services."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 0)
_version = (2, 0, 0)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -42,10 +59,16 @@ class SvcScan(interfaces.plugins.PluginInterface):
requirements.PluginRequirement(
name="vadyarascan", plugin=vadyarascan.VadYaraScan, version=(1, 0, 0)
),
requirements.PluginRequirement(
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
),
]
@staticmethod
def get_record_tuple(service_record: interfaces.objects.ObjectInterface):
def get_record_tuple(
service_record: interfaces.objects.ObjectInterface,
binary_info: ServiceBinaryInfo,
):
return (
format_hints.Hex(service_record.vol.offset),
service_record.Order,
@@ -56,8 +79,32 @@ class SvcScan(interfaces.plugins.PluginInterface):
service_record.get_name(),
service_record.get_display(),
service_record.get_binary(),
binary_info.binary,
binary_info.dll,
)
# These checks must be completed from newest -> oldest OS version.
_win_version_file_map: List[Tuple[versions.OsDistinguisher, bool, str]] = [
(versions.is_win10_25398_or_later, True, "services-win10-25398-x64"),
(versions.is_win10_19041_or_later, True, "services-win10-19041-x64"),
(versions.is_win10_19041_or_later, False, "services-win10-19041-x86"),
(versions.is_win10_18362_or_later, True, "services-win10-18362-x64"),
(versions.is_win10_18362_or_later, False, "services-win10-18362-x86"),
(versions.is_win10_17763_or_later, False, "services-win10-17763-x86"),
(versions.is_win10_16299_or_later, True, "services-win10-16299-x64"),
(versions.is_win10_16299_or_later, False, "services-win10-16299-x86"),
(versions.is_win10_15063, True, "services-win10-15063-x64"),
(versions.is_win10_15063, False, "services-win10-15063-x86"),
(versions.is_win10_up_to_15063, True, "services-win8-x64"),
(versions.is_win10_up_to_15063, False, "services-win8-x86"),
(versions.is_windows_8_or_later, True, "services-win8-x64"),
(versions.is_windows_8_or_later, True, "services-win8-x86"),
(versions.is_vista_or_later, True, "services-vista-x64"),
(versions.is_vista_or_later, False, "services-vista-x86"),
(versions.is_windows_xp, False, "services-xp-x86"),
(versions.is_xp_or_2003, True, "services-xp-2003-x64"),
]
@staticmethod
def create_service_table(
context: interfaces.context.ContextInterface,
@@ -78,67 +125,14 @@ class SvcScan(interfaces.plugins.PluginInterface):
native_types = context.symbol_space[symbol_table].natives
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
if (
versions.is_windows_xp(context=context, symbol_table=symbol_table)
and not is_64bit
):
symbol_filename = "services-xp-x86"
elif (
versions.is_xp_or_2003(context=context, symbol_table=symbol_table)
and is_64bit
):
symbol_filename = "services-xp-2003-x64"
elif (
versions.is_win10_16299_or_later(context=context, symbol_table=symbol_table)
and is_64bit
):
symbol_filename = "services-win10-16299-x64"
elif (
versions.is_win10_16299_or_later(context=context, symbol_table=symbol_table)
and not is_64bit
):
symbol_filename = "services-win10-16299-x86"
elif (
versions.is_win10_up_to_15063(context=context, symbol_table=symbol_table)
and is_64bit
):
symbol_filename = "services-win8-x64"
elif (
versions.is_win10_up_to_15063(context=context, symbol_table=symbol_table)
and not is_64bit
):
symbol_filename = "services-win8-x86"
elif (
versions.is_win10_15063(context=context, symbol_table=symbol_table)
and is_64bit
):
symbol_filename = "services-win10-15063-x64"
elif (
versions.is_win10_15063(context=context, symbol_table=symbol_table)
and not is_64bit
):
symbol_filename = "services-win10-15063-x86"
elif (
versions.is_windows_8_or_later(context=context, symbol_table=symbol_table)
and is_64bit
):
symbol_filename = "services-win8-x64"
elif (
versions.is_windows_8_or_later(context=context, symbol_table=symbol_table)
and not is_64bit
):
symbol_filename = "services-win8-x86"
elif (
versions.is_vista_or_later(context=context, symbol_table=symbol_table)
and is_64bit
):
symbol_filename = "services-vista-x64"
elif (
versions.is_vista_or_later(context=context, symbol_table=symbol_table)
and not is_64bit
):
symbol_filename = "services-vista-x86"
else:
try:
symbol_filename = next(
filename
for version_check, for_64bit, filename in SvcScan._win_version_file_map
if is_64bit == for_64bit
and version_check(context=context, symbol_table=symbol_table)
)
except StopIteration:
raise NotImplementedError("This version of Windows is not supported!")
return intermed.IntermediateSymbolTable.create(
@@ -150,6 +144,94 @@ class SvcScan(interfaces.plugins.PluginInterface):
native_types=native_types,
)
def _get_service_key(self, kernel) -> Optional[objects.StructType]:
for hive in hivelist.HiveList.list_hives(
context=self.context,
base_config_path=interfaces.configuration.path_join(
self.config_path, "hivelist"
),
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
filter_string="machine\\system",
):
# Get ControlSet\Services.
try:
return cast(
objects.StructType, hive.get_key(r"CurrentControlSet\Services")
)
except (KeyError, exceptions.InvalidAddressException):
try:
return cast(
objects.StructType, hive.get_key(r"ControlSet001\Services")
)
except (KeyError, exceptions.InvalidAddressException):
vollog.log(
constants.LOGLEVEL_VVVV,
"Could not retrieve any control set from SYSTEM hive",
)
return None
@staticmethod
def _get_service_dll(
service_key,
) -> Union[str, interfaces.renderers.BaseAbsentValue]:
try:
param_key = next(
key
for key in service_key.get_subkeys()
if key.get_name() == "Parameters"
)
return (
next(
val
for val in param_key.get_values()
if val.get_name() == "ServiceDll"
)
.decode_data()
.decode("utf-16")
.rstrip("\x00")
)
except UnicodeDecodeError:
return renderers.UnparsableValue()
except StopIteration:
return renderers.UnreadableValue()
@staticmethod
def _get_service_binary(
service_key,
) -> Union[str, interfaces.renderers.BaseAbsentValue]:
try:
return (
next(
val
for val in service_key.get_values()
if val.get_name() == "ImagePath"
)
.decode_data()
.decode("utf-16")
.rstrip("\x00")
)
except UnicodeDecodeError:
return renderers.UnparsableValue()
except StopIteration:
return renderers.UnreadableValue()
@staticmethod
def _get_service_binary_map(
services_key: interfaces.objects.ObjectInterface,
) -> Dict[str, ServiceBinaryInfo]:
services = services_key.get_subkeys()
return {
service_key.get_name(): ServiceBinaryInfo(
SvcScan._get_service_dll(service_key),
SvcScan._get_service_binary(service_key),
)
for service_key in services
}
def _generator(self):
kernel = self.context.modules[self.config["kernel"]]
@@ -157,6 +239,15 @@ class SvcScan(interfaces.plugins.PluginInterface):
self.context, kernel.symbol_table_name, self.config_path
)
# Building the dictionary ahead of time is much better for performance
# vs looking up each service's DLL individually.
services_key = self._get_service_key(kernel)
service_binary_dll_map = (
self._get_service_binary_map(services_key)
if services_key is not None
else {}
)
relative_tag_offset = self.context.symbol_space.get_type(
service_table_name + constants.BANG + "_SERVICE_RECORD"
).relative_child_offset("Tag")
@@ -209,7 +300,16 @@ class SvcScan(interfaces.plugins.PluginInterface):
if not service_record.is_valid():
continue
yield (0, self.get_record_tuple(service_record))
service_info = service_binary_dll_map.get(
service_record.get_name(),
ServiceBinaryInfo(
renderers.UnreadableValue(), renderers.UnreadableValue()
),
)
yield (
0,
self.get_record_tuple(service_record, service_info),
)
else:
service_header = self.context.object(
service_table_name + constants.BANG + "_SERVICE_HEADER",
@@ -227,7 +327,16 @@ class SvcScan(interfaces.plugins.PluginInterface):
if service_record in seen:
break
seen.append(service_record)
yield (0, self.get_record_tuple(service_record))
service_info = service_binary_dll_map.get(
service_record.get_name(),
ServiceBinaryInfo(
renderers.UnreadableValue(), renderers.UnreadableValue()
),
)
yield (
0,
self.get_record_tuple(service_record, service_info),
)
def run(self):
return renderers.TreeGrid(
@@ -241,6 +350,8 @@ class SvcScan(interfaces.plugins.PluginInterface):
("Name", str),
("Display", str),
("Binary", str),
("Binary (Registry)", str),
("Dll", str),
],
self._generator(),
)
@@ -28,9 +28,9 @@ def wintime_to_datetime(
def unixtime_to_datetime(
unixtime: int,
) -> Union[interfaces.renderers.BaseAbsentValue, datetime.datetime]:
ret: Union[
interfaces.renderers.BaseAbsentValue, datetime.datetime
] = renderers.UnparsableValue()
ret: Union[interfaces.renderers.BaseAbsentValue, datetime.datetime] = (
renderers.UnparsableValue()
)
if unixtime > 0:
with contextlib.suppress(ValueError):
+13 -13
View File
@@ -35,9 +35,9 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface):
def __init__(self) -> None:
super().__init__()
self._dict: Dict[
str, interfaces.symbols.BaseSymbolTableInterface
] = collections.OrderedDict()
self._dict: Dict[str, interfaces.symbols.BaseSymbolTableInterface] = (
collections.OrderedDict()
)
# Permanently cache all resolved symbols
self._resolved: Dict[str, interfaces.objects.Template] = {}
self._resolved_symbols: Dict[str, interfaces.objects.Template] = {}
@@ -73,9 +73,9 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface):
self, offset: int, size: int = 0, table_name: str = None
) -> Iterable[str]:
"""Returns all symbols that exist at a specific relative address."""
table_list: Iterable[
interfaces.symbols.BaseSymbolTableInterface
] = self._dict.values()
table_list: Iterable[interfaces.symbols.BaseSymbolTableInterface] = (
self._dict.values()
)
if table_name is not None:
if table_name in self._dict:
table_list = [self._dict[table_name]]
@@ -179,15 +179,15 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface):
if child.vol.type_name not in self._resolved:
traverse_list.append(child.vol.type_name)
try:
self._resolved[
child.vol.type_name
] = self._weak_resolve(
SymbolType.TYPE, child.vol.type_name
self._resolved[child.vol.type_name] = (
self._weak_resolve(
SymbolType.TYPE, child.vol.type_name
)
)
except exceptions.SymbolError:
self._resolved[
child.vol.type_name
] = self.UnresolvedTemplate(child.vol.type_name)
self._resolved[child.vol.type_name] = (
self.UnresolvedTemplate(child.vol.type_name)
)
# Stash the replacement
replacements.add((traverser, child))
elif child.children:
@@ -408,7 +408,7 @@ class maple_tree(objects.StructType):
# None. If however you wanted to parse from a node, but ignore some parts of the tree below it then
# this could be populated with the addresses of the nodes you wish to ignore.
if seen == None:
if seen is None:
seen = set()
# protect against unlikely loop
@@ -452,9 +452,9 @@ class FILE_OBJECT(objects.StructType, pool.ExecutiveObject):
].is_valid(self.FileName.Buffer)
def file_name_with_device(self) -> Union[str, interfaces.renderers.BaseAbsentValue]:
name: Union[
str, interfaces.renderers.BaseAbsentValue
] = renderers.UnreadableValue()
name: Union[str, interfaces.renderers.BaseAbsentValue] = (
renderers.UnreadableValue()
)
# this pointer needs to be checked against native_layer_name because the object may
# be instantiated from a primary (virtual) layer or a memory (physical) layer.
@@ -0,0 +1,248 @@
{
"symbols": {},
"enums": {
"StateEnum": {
"base": "long",
"constants": {
"SERVICE_START_PENDING": 2,
"SERVICE_STOP_PENDING": 3,
"SERVICE_STOPPED": 1,
"SERVICE_CONTINUE_PENDING": 5,
"SERVICE_PAUSE_PENDING": 6,
"SERVICE_PAUSED": 7,
"SERVICE_RUNNING": 4
},
"size": 4
},
"StartEnum": {
"base": "long",
"constants": {
"SERVICE_DEMAND_START": 3,
"SERVICE_AUTO_START": 2,
"SERVICE_BOOT_START": 0,
"SERVICE_DISABLED": 4,
"SERVICE_SYSTEM_START": 1
},
"size": 4
}
},
"base_types": {
"unsigned long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned char": {
"kind": "char",
"size": 1,
"signed": false,
"endian": "little"
},
"pointer": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned int": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned short": {
"kind": "int",
"size": 2,
"signed": false,
"endian": "little"
},
"long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
}
},
"user_types": {
"_SERVICE_LIST_ENTRY": {
"fields": {
"Flink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 4
},
"Blink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 0
}
},
"kind": "struct",
"size": 8
},
"_SERVICE_PROCESS": {
"fields": {
"BinaryPath": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 12
},
"ProcessId": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 20
}
},
"kind": "struct",
"size": 20
},
"_SERVICE_HEADER": {
"fields": {
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"ServiceRecord": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 12
}
},
"kind": "struct",
"size": 12
},
"_SERVICE_RECORD": {
"fields": {
"DisplayName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 48
},
"ServiceProcess": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_PROCESS"
}
},
"offset": 160
},
"PrevEntry": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 12
},
"Start": {
"type": {
"kind": "enum",
"name": "StartEnum"
},
"offset": 24
},
"State": {
"type": {
"kind": "enum",
"name": "StateEnum"
},
"offset": 56
},
"ServiceName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 44
},
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"DriverName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 160
},
"Type": {
"type": {
"kind": "base",
"name": "unsigned long"
},
"offset": 52
},
"Order": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 20
}
},
"kind": "struct",
"size": 156
}
},
"metadata": {
"producer": {
"version": "0.0.1",
"name": "vtypes_to_json.py",
"datetime": "2019-04-17T13:45:16.417006"
},
"format": "4.1.0"
}
}
@@ -0,0 +1,255 @@
{
"symbols": {},
"enums": {
"StateEnum": {
"base": "long",
"constants": {
"SERVICE_START_PENDING": 2,
"SERVICE_STOP_PENDING": 3,
"SERVICE_STOPPED": 1,
"SERVICE_CONTINUE_PENDING": 5,
"SERVICE_PAUSE_PENDING": 6,
"SERVICE_PAUSED": 7,
"SERVICE_RUNNING": 4
},
"size": 4
},
"StartEnum": {
"base": "long",
"constants": {
"SERVICE_DEMAND_START": 3,
"SERVICE_AUTO_START": 2,
"SERVICE_BOOT_START": 0,
"SERVICE_DISABLED": 4,
"SERVICE_SYSTEM_START": 1
},
"size": 4
}
},
"base_types": {
"unsigned long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned char": {
"kind": "char",
"size": 1,
"signed": false,
"endian": "little"
},
"pointer": {
"kind": "int",
"size": 8,
"signed": false,
"endian": "little"
},
"unsigned int": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned short": {
"kind": "int",
"size": 2,
"signed": false,
"endian": "little"
},
"long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
}
},
"user_types": {
"_SERVICE_LIST_ENTRY": {
"fields": {
"Flink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 16
},
"Blink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 0
}
},
"kind": "struct",
"size": 16
},
"_SERVICE_PROCESS": {
"fields": {
"BinaryPath": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 24
},
"ProcessId": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 40
}
},
"kind": "struct",
"size": 40
},
"_SERVICE_HEADER": {
"fields": {
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"ServiceRecord": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
}
},
"kind": "struct",
"size": 16
},
"_SERVICE_RECORD": {
"fields": {
"ServiceList": {
"type": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
},
"offset": 0
},
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 32
},
"DisplayName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 64
},
"ServiceProcess": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_PROCESS"
}
},
"offset": 240
},
"PrevEntry": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
},
"Start": {
"type": {
"kind": "enum",
"name": "StartEnum"
},
"offset": 36
},
"State": {
"type": {
"kind": "enum",
"name": "StateEnum"
},
"offset": 76
},
"ServiceName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 56
},
"DriverName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 240
},
"Type": {
"type": {
"kind": "base",
"name": "unsigned long"
},
"offset": 72
},
"Order": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 32
}
},
"kind": "struct",
"size": 248
}
},
"metadata": {
"producer": {
"version": "0.0.1",
"name": "vtypes_to_json.py",
"datetime": "2019-04-17T13:45:16.417006"
},
"format": "4.1.0"
}
}
@@ -0,0 +1,248 @@
{
"symbols": {},
"enums": {
"StateEnum": {
"base": "long",
"constants": {
"SERVICE_START_PENDING": 2,
"SERVICE_STOP_PENDING": 3,
"SERVICE_STOPPED": 1,
"SERVICE_CONTINUE_PENDING": 5,
"SERVICE_PAUSE_PENDING": 6,
"SERVICE_PAUSED": 7,
"SERVICE_RUNNING": 4
},
"size": 4
},
"StartEnum": {
"base": "long",
"constants": {
"SERVICE_DEMAND_START": 3,
"SERVICE_AUTO_START": 2,
"SERVICE_BOOT_START": 0,
"SERVICE_DISABLED": 4,
"SERVICE_SYSTEM_START": 1
},
"size": 4
}
},
"base_types": {
"unsigned long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned char": {
"kind": "char",
"size": 1,
"signed": false,
"endian": "little"
},
"pointer": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned int": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned short": {
"kind": "int",
"size": 2,
"signed": false,
"endian": "little"
},
"long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
}
},
"user_types": {
"_SERVICE_LIST_ENTRY": {
"fields": {
"Flink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 4
},
"Blink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 0
}
},
"kind": "struct",
"size": 8
},
"_SERVICE_PROCESS": {
"fields": {
"BinaryPath": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 12
},
"ProcessId": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 20
}
},
"kind": "struct",
"size": 20
},
"_SERVICE_HEADER": {
"fields": {
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"ServiceRecord": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 12
}
},
"kind": "struct",
"size": 12
},
"_SERVICE_RECORD": {
"fields": {
"DisplayName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 48
},
"ServiceProcess": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_PROCESS"
}
},
"offset": 164
},
"PrevEntry": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 12
},
"Start": {
"type": {
"kind": "enum",
"name": "StartEnum"
},
"offset": 24
},
"State": {
"type": {
"kind": "enum",
"name": "StateEnum"
},
"offset": 56
},
"ServiceName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 44
},
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"DriverName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 164
},
"Type": {
"type": {
"kind": "base",
"name": "unsigned long"
},
"offset": 52
},
"Order": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 20
}
},
"kind": "struct",
"size": 156
}
},
"metadata": {
"producer": {
"version": "0.0.1",
"name": "vtypes_to_json.py",
"datetime": "2019-04-17T13:45:16.417006"
},
"format": "4.1.0"
}
}
@@ -0,0 +1,255 @@
{
"symbols": {},
"enums": {
"StateEnum": {
"base": "long",
"constants": {
"SERVICE_START_PENDING": 2,
"SERVICE_STOP_PENDING": 3,
"SERVICE_STOPPED": 1,
"SERVICE_CONTINUE_PENDING": 5,
"SERVICE_PAUSE_PENDING": 6,
"SERVICE_PAUSED": 7,
"SERVICE_RUNNING": 4
},
"size": 4
},
"StartEnum": {
"base": "long",
"constants": {
"SERVICE_DEMAND_START": 3,
"SERVICE_AUTO_START": 2,
"SERVICE_BOOT_START": 0,
"SERVICE_DISABLED": 4,
"SERVICE_SYSTEM_START": 1
},
"size": 4
}
},
"base_types": {
"unsigned long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned char": {
"kind": "char",
"size": 1,
"signed": false,
"endian": "little"
},
"pointer": {
"kind": "int",
"size": 8,
"signed": false,
"endian": "little"
},
"unsigned int": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned short": {
"kind": "int",
"size": 2,
"signed": false,
"endian": "little"
},
"long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
}
},
"user_types": {
"_SERVICE_LIST_ENTRY": {
"fields": {
"Flink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 16
},
"Blink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 0
}
},
"kind": "struct",
"size": 16
},
"_SERVICE_PROCESS": {
"fields": {
"BinaryPath": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 24
},
"ProcessId": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 40
}
},
"kind": "struct",
"size": 40
},
"_SERVICE_HEADER": {
"fields": {
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"ServiceRecord": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
}
},
"kind": "struct",
"size": 16
},
"_SERVICE_RECORD": {
"fields": {
"ServiceList": {
"type": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
},
"offset": 0
},
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 32
},
"DisplayName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 64
},
"ServiceProcess": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_PROCESS"
}
},
"offset": 296
},
"PrevEntry": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
},
"Start": {
"type": {
"kind": "enum",
"name": "StartEnum"
},
"offset": 36
},
"State": {
"type": {
"kind": "enum",
"name": "StateEnum"
},
"offset": 76
},
"ServiceName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 56
},
"DriverName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 296
},
"Type": {
"type": {
"kind": "base",
"name": "unsigned long"
},
"offset": 72
},
"Order": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 32
}
},
"kind": "struct",
"size": 296
}
},
"metadata": {
"producer": {
"version": "0.0.1",
"name": "David McDonald",
"datetime": "2023-11-16T15:05:35-06:00"
},
"format": "4.1.0"
}
}
@@ -0,0 +1,248 @@
{
"symbols": {},
"enums": {
"StateEnum": {
"base": "long",
"constants": {
"SERVICE_START_PENDING": 2,
"SERVICE_STOP_PENDING": 3,
"SERVICE_STOPPED": 1,
"SERVICE_CONTINUE_PENDING": 5,
"SERVICE_PAUSE_PENDING": 6,
"SERVICE_PAUSED": 7,
"SERVICE_RUNNING": 4
},
"size": 4
},
"StartEnum": {
"base": "long",
"constants": {
"SERVICE_DEMAND_START": 3,
"SERVICE_AUTO_START": 2,
"SERVICE_BOOT_START": 0,
"SERVICE_DISABLED": 4,
"SERVICE_SYSTEM_START": 1
},
"size": 4
}
},
"base_types": {
"unsigned long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned char": {
"kind": "char",
"size": 1,
"signed": false,
"endian": "little"
},
"pointer": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned int": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned short": {
"kind": "int",
"size": 2,
"signed": false,
"endian": "little"
},
"long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
}
},
"user_types": {
"_SERVICE_LIST_ENTRY": {
"fields": {
"Flink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 4
},
"Blink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 0
}
},
"kind": "struct",
"size": 8
},
"_SERVICE_PROCESS": {
"fields": {
"BinaryPath": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 12
},
"ProcessId": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 20
}
},
"kind": "struct",
"size": 20
},
"_SERVICE_HEADER": {
"fields": {
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"ServiceRecord": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
}
},
"kind": "struct",
"size": 12
},
"_SERVICE_RECORD": {
"fields": {
"DisplayName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 48
},
"ServiceProcess": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_PROCESS"
}
},
"offset": 192
},
"PrevEntry": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 12
},
"Start": {
"type": {
"kind": "enum",
"name": "StartEnum"
},
"offset": 24
},
"State": {
"type": {
"kind": "enum",
"name": "StateEnum"
},
"offset": 56
},
"ServiceName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 44
},
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"DriverName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 192
},
"Type": {
"type": {
"kind": "base",
"name": "unsigned long"
},
"offset": 52
},
"Order": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 20
}
},
"kind": "struct",
"size": 192
}
},
"metadata": {
"producer": {
"version": "0.0.1",
"name": "vtypes_to_json.py",
"datetime": "2019-04-17T13:45:16.417006"
},
"format": "4.1.0"
}
}
@@ -0,0 +1,255 @@
{
"symbols": {},
"enums": {
"StateEnum": {
"base": "long",
"constants": {
"SERVICE_START_PENDING": 2,
"SERVICE_STOP_PENDING": 3,
"SERVICE_STOPPED": 1,
"SERVICE_CONTINUE_PENDING": 5,
"SERVICE_PAUSE_PENDING": 6,
"SERVICE_PAUSED": 7,
"SERVICE_RUNNING": 4
},
"size": 4
},
"StartEnum": {
"base": "long",
"constants": {
"SERVICE_DEMAND_START": 3,
"SERVICE_AUTO_START": 2,
"SERVICE_BOOT_START": 0,
"SERVICE_DISABLED": 4,
"SERVICE_SYSTEM_START": 1
},
"size": 4
}
},
"base_types": {
"unsigned long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned char": {
"kind": "char",
"size": 1,
"signed": false,
"endian": "little"
},
"pointer": {
"kind": "int",
"size": 8,
"signed": false,
"endian": "little"
},
"unsigned int": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
},
"unsigned short": {
"kind": "int",
"size": 2,
"signed": false,
"endian": "little"
},
"long": {
"kind": "int",
"size": 4,
"signed": false,
"endian": "little"
}
},
"user_types": {
"_SERVICE_LIST_ENTRY": {
"fields": {
"Flink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 16
},
"Blink": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
}
},
"offset": 0
}
},
"kind": "struct",
"size": 16
},
"_SERVICE_PROCESS": {
"fields": {
"BinaryPath": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 24
},
"ProcessId": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 40
}
},
"kind": "struct",
"size": 40
},
"_SERVICE_HEADER": {
"fields": {
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 0
},
"ServiceRecord": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
}
},
"kind": "struct",
"size": 16
},
"_SERVICE_RECORD": {
"fields": {
"ServiceList": {
"type": {
"kind": "struct",
"name": "_SERVICE_LIST_ENTRY"
},
"offset": 0
},
"Tag": {
"type": {
"count": 4,
"subtype": {
"kind": "base",
"name": "unsigned char"
},
"kind": "array"
},
"offset": 32
},
"DisplayName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 64
},
"ServiceProcess": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_PROCESS"
}
},
"offset": 336
},
"PrevEntry": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "struct",
"name": "_SERVICE_RECORD"
}
},
"offset": 16
},
"Start": {
"type": {
"kind": "enum",
"name": "StartEnum"
},
"offset": 36
},
"State": {
"type": {
"kind": "enum",
"name": "StateEnum"
},
"offset": 84
},
"ServiceName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 56
},
"DriverName": {
"type": {
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "unsigned short"
}
},
"offset": 296
},
"Type": {
"type": {
"kind": "base",
"name": "unsigned long"
},
"offset": 80
},
"Order": {
"type": {
"kind": "base",
"name": "unsigned int"
},
"offset": 32
}
},
"kind": "struct",
"size": 336
}
},
"metadata": {
"producer": {
"version": "0.0.1",
"name": "David McDonald",
"datetime": "2023-11-16T15:05:35-06:00"
},
"format": "4.1.0"
}
}
@@ -151,11 +151,45 @@ is_win10_16299_or_later = OsDistinguisher(
],
)
is_win10_17763_or_later = OsDistinguisher(
version_check=lambda x: x >= (10, 0, 17763),
fallback_checks=[
("_EPROCESS", "TrustletIdentity", False),
("ParentSecurityDomain", None, True),
],
)
is_win10_18362_or_later = OsDistinguisher(
version_check=lambda x: x >= (10, 0, 18362),
fallback_checks=[
("ObHeaderCookie", None, True),
("_CM_CACHED_VALUE_INDEX", None, False),
("_WNF_PROCESS_CONTEXT", None, True),
],
)
is_win10_18363_or_later = OsDistinguisher(
version_check=lambda x: x >= (10, 0, 18363),
fallback_checks=[("_KQOS_GROUPING_SETS", None, True)],
)
is_win10_19041_or_later = OsDistinguisher(
version_check=lambda x: x >= (10, 0, 19041),
fallback_checks=[
("_EPROCESS", "TimerResolutionIgnore", True),
("_EPROCESS", "VmProcessorHostTransition", True),
("_KQOS_GROUPING_SETS", None, True),
],
)
is_win10_25398_or_later = OsDistinguisher(
version_check=lambda x: x >= (10, 0, 25398),
fallback_checks=[
("_EPROCESS", "MmSlabIdentity", True),
("_EPROCESS", "EnableProcessImpersonationLogging", True),
],
)
is_windows_10 = OsDistinguisher(
version_check=lambda x: x >= (10, 0),
fallback_checks=[("ObHeaderCookie", None, True)],
+3 -7
View File
@@ -31,13 +31,9 @@ class Statistics(plugins.PluginInterface):
# Do mass mapping and determine the number of different layers and how many pages go to each one
layer = self.context.layers[self.config["primary"]]
page_count = (
swap_count
) = (
invalid_page_count
) = (
large_page_count
) = large_swap_count = large_invalid_count = other_invalid = 0
page_count = swap_count = invalid_page_count = large_page_count = (
large_swap_count
) = large_invalid_count = other_invalid = 0
if isinstance(layer, intel.Intel):
page_addr = 0