mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 17:57:38 +02:00
Merge pull request #1306 from volatilityfoundation/release/v2.8.0
Release/v2.8.0
This commit is contained in:
@@ -6,8 +6,10 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: psf/black@stable
|
||||
with:
|
||||
options: "--check --diff --verbose"
|
||||
src: "./volatility3"
|
||||
# FIXME: Remove when Volatility3 minimum Python version is >3.8
|
||||
version: "24.8.0"
|
||||
|
||||
@@ -20,24 +20,23 @@ jobs:
|
||||
matrix:
|
||||
python-version: ["3.7"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel
|
||||
pip install build
|
||||
|
||||
- name: Build PyPi packages
|
||||
run: |
|
||||
python setup.py sdist --formats=gztar,zip
|
||||
python setup.py bdist_wheel
|
||||
python -m build
|
||||
|
||||
- name: Archive dist
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: volatility3-pypi
|
||||
path: |
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
host: [ ubuntu-latest, windows-latest ]
|
||||
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
|
||||
@@ -8,9 +8,9 @@ jobs:
|
||||
matrix:
|
||||
python-version: ["3.7"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
@@ -18,13 +18,12 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install Cmake
|
||||
pip install setuptools wheel
|
||||
pip install build
|
||||
pip install -r ./test/requirements-testing.txt
|
||||
|
||||
- name: Build PyPi packages
|
||||
run: |
|
||||
python setup.py sdist --formats=gztar,zip
|
||||
python setup.py bdist_wheel
|
||||
python -m build
|
||||
|
||||
- name: Download images
|
||||
run: |
|
||||
|
||||
@@ -27,6 +27,7 @@ config*.json
|
||||
# Pyinstaller files
|
||||
build
|
||||
dist
|
||||
*.egg-info
|
||||
|
||||
# Environments
|
||||
.env
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ authors:
|
||||
identifiers:
|
||||
- type: url
|
||||
value: 'https://github.com/volatilityfoundation/volatility3'
|
||||
description: Volatility 3 source code respository
|
||||
description: Volatility 3 source code repository
|
||||
repository-code: 'https://github.com/volatilityfoundation/volatility3'
|
||||
url: 'https://github.com/volatilityfoundation/volatility3'
|
||||
abstract: >-
|
||||
|
||||
@@ -20,17 +20,16 @@ more details.
|
||||
|
||||
## Requirements
|
||||
|
||||
Volatility 3 requires Python 3.7.0 or later. To install the most minimal set of dependencies (some plugins will not work) use a command such as:
|
||||
Volatility 3 requires Python 3.7.3 or later. To install the most minimal set of dependencies (some plugins will not work) use a command such as:
|
||||
|
||||
```shell
|
||||
pip3 install -r requirements-minimal.txt
|
||||
```
|
||||
|
||||
Alternately, the minimal packages will be installed automatically when Volatility 3 is installed using setup.py. However, as noted in the Quick Start section below, Volatility 3 does not *need* to be installed via setup.py prior to using it.
|
||||
Alternately, the minimal packages will be installed automatically when Volatility 3 is installed using pip. However, as noted in the Quick Start section below, Volatility 3 does not *need* to be installed prior to using it.
|
||||
|
||||
```shell
|
||||
python3 setup.py build
|
||||
python3 setup.py install
|
||||
pip3 install .
|
||||
```
|
||||
|
||||
To enable the full range of Volatility 3 functionality, use a command like the one below. For partial functionality, comment out any unnecessary packages in [requirements.txt](requirements.txt) prior to running the command.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
[project]
|
||||
name = "volatility3"
|
||||
description = "Memory forensics framework"
|
||||
keywords = ["volatility", "memory", "forensics", "framework", "windows", "linux", "volshell"]
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "Volatility Foundation", email = "volatility@volatilityfoundation.org" },
|
||||
]
|
||||
requires-python = ">=3.7.3"
|
||||
license = { text = "VSL" }
|
||||
dynamic = ["dependencies", "optional-dependencies", "version"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/volatilityfoundation/volatility3/"
|
||||
"Bug Tracker" = "https://github.com/volatilityfoundation/volatility3/issues"
|
||||
Documentation = "https://volatility3.readthedocs.io/"
|
||||
"Source Code" = "https://github.com/volatilityfoundation/volatility3"
|
||||
|
||||
[project.scripts]
|
||||
vol = "volatility3.cli:main"
|
||||
volshell = "volatility3.cli.volshell:main"
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = { attr = "volatility3.framework.constants._version.PACKAGE_VERSION" }
|
||||
dependencies = { file = "requirements-minimal.txt" }
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["volatility3*"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=68"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
+1
-1
@@ -15,7 +15,7 @@ capstone>=3.0.5
|
||||
pycryptodome
|
||||
|
||||
# This is required for memory acquisition via leechcore/pcileech.
|
||||
leechcorepyc>=2.4.0
|
||||
leechcorepyc>=2.4.0; sys_platform != 'darwin'
|
||||
|
||||
# This is required for memory analysis on a Amazon/MinIO S3 and Google Cloud object storage
|
||||
gcsfs>=2023.1.0
|
||||
|
||||
@@ -4,50 +4,21 @@
|
||||
|
||||
import setuptools
|
||||
|
||||
from volatility3.framework import constants
|
||||
|
||||
with open("README.md", "r", encoding="utf-8") as fh:
|
||||
long_description = fh.read()
|
||||
|
||||
|
||||
def get_install_requires():
|
||||
def get_requires(filename):
|
||||
requirements = []
|
||||
with open("requirements-minimal.txt", "r", encoding="utf-8") as fh:
|
||||
with open(filename, "r", encoding="utf-8") as fh:
|
||||
for line in fh.readlines():
|
||||
stripped_line = line.strip()
|
||||
if stripped_line == "" or stripped_line.startswith("#"):
|
||||
if stripped_line == "" or stripped_line.startswith(("#", "-r")):
|
||||
continue
|
||||
requirements.append(stripped_line)
|
||||
return requirements
|
||||
|
||||
|
||||
setuptools.setup(
|
||||
name="volatility3",
|
||||
description="Memory forensics framework",
|
||||
version=constants.PACKAGE_VERSION,
|
||||
license="VSL",
|
||||
keywords="volatility memory forensics framework windows linux volshell",
|
||||
author="Volatility Foundation",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
author_email="volatility@volatilityfoundation.org",
|
||||
url="https://github.com/volatilityfoundation/volatility3/",
|
||||
project_urls={
|
||||
"Bug Tracker": "https://github.com/volatilityfoundation/volatility3/issues",
|
||||
"Documentation": "https://volatility3.readthedocs.io/",
|
||||
"Source Code": "https://github.com/volatilityfoundation/volatility3",
|
||||
extras_require={
|
||||
"dev": get_requires("requirements-dev.txt"),
|
||||
"full": get_requires("requirements.txt"),
|
||||
},
|
||||
packages=setuptools.find_namespace_packages(
|
||||
include=["volatility3", "volatility3.*"]
|
||||
),
|
||||
package_dir={"volatility3": "volatility3"},
|
||||
python_requires=">=3.7.0",
|
||||
include_package_data=True,
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"vol = volatility3.cli:main",
|
||||
"volshell = volatility3.cli.volshell:main",
|
||||
],
|
||||
},
|
||||
install_requires=get_install_requires(),
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
|
||||
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
|
||||
@@ -22,6 +22,13 @@ import traceback
|
||||
from typing import Any, Dict, List, Tuple, Type, Union
|
||||
from urllib import parse, request
|
||||
|
||||
try:
|
||||
import argcomplete
|
||||
|
||||
HAS_ARGCOMPLETE = True
|
||||
except ImportError:
|
||||
HAS_ARGCOMPLETE = False
|
||||
|
||||
from volatility3.cli import text_filter
|
||||
import volatility3.plugins
|
||||
import volatility3.symbols
|
||||
@@ -351,6 +358,10 @@ class CommandLine:
|
||||
# Hand the plugin requirements over to the CLI (us) and let it construct the config tree
|
||||
|
||||
# Run the argparser
|
||||
if HAS_ARGCOMPLETE:
|
||||
# The autocompletion line must be after the partial_arg handling, so that it doesn't trip it
|
||||
# before all the plugins have been added
|
||||
argcomplete.autocomplete(parser)
|
||||
args = parser.parse_args()
|
||||
if args.plugin is None:
|
||||
parser.error("Please select a plugin to run")
|
||||
@@ -716,19 +727,17 @@ class CommandLine:
|
||||
"""Gets the final filename"""
|
||||
if output_dir is None:
|
||||
raise TypeError("Output directory is not a string")
|
||||
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
pref_name_array = self.preferred_filename.split(".")
|
||||
filename, extension = (
|
||||
os.path.join(output_dir, ".".join(pref_name_array[:-1])),
|
||||
pref_name_array[-1],
|
||||
)
|
||||
output_filename = f"{filename}.{extension}"
|
||||
output_filename = os.path.join(output_dir, self.preferred_filename)
|
||||
filename, extension = os.path.splitext(output_filename)
|
||||
|
||||
counter = 1
|
||||
while os.path.exists(output_filename):
|
||||
output_filename = f"{filename}-{counter}.{extension}"
|
||||
output_filename = f"{filename}-{counter}{extension}"
|
||||
counter += 1
|
||||
|
||||
return output_filename
|
||||
|
||||
class CLIMemFileHandler(io.BytesIO, CLIFileHandler):
|
||||
@@ -791,8 +800,16 @@ class CommandLine:
|
||||
if self._file.closed:
|
||||
return None
|
||||
|
||||
self._file.close()
|
||||
output_filename = self._get_final_filename()
|
||||
|
||||
# Update the filename, which may have changed if a file with
|
||||
# the same name already existed. This needs to be done before
|
||||
# closing the file, otherwise FileHandlerInterface will raise
|
||||
# an exception. Also, the preferred_filename setter only allows
|
||||
# a specific set of characters, where '/' is not in that list
|
||||
self.preferred_filename = os.path.basename(output_filename)
|
||||
|
||||
self._file.close()
|
||||
os.rename(self._name, output_filename)
|
||||
|
||||
if direct:
|
||||
|
||||
@@ -25,7 +25,7 @@ except ImportError:
|
||||
vollog.debug("Disassembly library capstone not found")
|
||||
|
||||
|
||||
def hex_bytes_as_text(value: bytes) -> str:
|
||||
def hex_bytes_as_text(value: bytes, width: int = 16) -> str:
|
||||
"""Renders HexBytes as text.
|
||||
|
||||
Args:
|
||||
@@ -36,19 +36,24 @@ def hex_bytes_as_text(value: bytes) -> str:
|
||||
"""
|
||||
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
|
||||
|
||||
printables = ""
|
||||
output = "\n"
|
||||
for count, byte in enumerate(value):
|
||||
output += f"{byte:02x} "
|
||||
char = chr(byte)
|
||||
printables += char if 0x20 <= byte <= 0x7E else "."
|
||||
if count % width == width - 1:
|
||||
output += printables
|
||||
if count < len(value) - 1:
|
||||
output += "\n"
|
||||
printables = ""
|
||||
|
||||
# Handle leftovers when the lenght is not mutiple of width
|
||||
if printables:
|
||||
output += " " * (width - len(printables))
|
||||
output += printables
|
||||
|
||||
return output
|
||||
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ class HelpfulSubparserAction(argparse._SubParsersAction):
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
# We don't want the action self-check to kick in, so we remove the choices list, the check happens in __call__
|
||||
self.choices = None
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
@@ -100,3 +98,20 @@ class HelpfulArgParser(argparse.ArgumentParser):
|
||||
|
||||
# return the number of arguments matched
|
||||
return len(match.group(1))
|
||||
|
||||
def _check_value(self, action: argparse.Action, value: Any) -> None:
|
||||
"""This is called to ensure a value is correct/valid
|
||||
|
||||
In normal operation, it would check that a value provided is valid and return None
|
||||
If it was not valid, it would throw an ArgumentError
|
||||
|
||||
When people provide a partial plugin name, we want to look for a matching plugin name
|
||||
which happens in the HelpfulSubparserAction's __call_method
|
||||
|
||||
To get there without tripping the check_value failure, we have to prevent the exception
|
||||
being thrown when the value is a HelpfulSubparserAction. This therefore affects no other
|
||||
checks for normal parameters.
|
||||
"""
|
||||
if not isinstance(action, HelpfulSubparserAction):
|
||||
super()._check_value(action, value)
|
||||
return None
|
||||
|
||||
@@ -21,6 +21,14 @@ from volatility3.framework import (
|
||||
plugins,
|
||||
)
|
||||
|
||||
try:
|
||||
import argcomplete
|
||||
|
||||
HAS_ARGCOMPLETE = True
|
||||
except ImportError:
|
||||
HAS_ARGCOMPLETE = False
|
||||
|
||||
|
||||
# Make sure we log everything
|
||||
|
||||
rootlog = logging.getLogger()
|
||||
@@ -276,6 +284,10 @@ class VolShell(cli.CommandLine):
|
||||
# Hand the plugin requirements over to the CLI (us) and let it construct the config tree
|
||||
|
||||
# Run the argparser
|
||||
if HAS_ARGCOMPLETE:
|
||||
# The autocompletion line must be after the partial_arg handling, so that it doesn't trip it
|
||||
# before all the plugins have been added
|
||||
argcomplete.autocomplete(parser)
|
||||
args = parser.parse_args()
|
||||
|
||||
vollog.log(
|
||||
|
||||
@@ -7,7 +7,7 @@ import glob
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
required_python_version = (3, 7, 0)
|
||||
required_python_version = (3, 7, 3)
|
||||
if (
|
||||
sys.version_info.major != required_python_version[0]
|
||||
or sys.version_info.minor < required_python_version[1]
|
||||
@@ -223,8 +223,14 @@ def list_plugins() -> Dict[str, Type[interfaces.plugins.PluginInterface]]:
|
||||
return plugin_list
|
||||
|
||||
|
||||
def clear_cache(complete=False):
|
||||
def clear_cache(complete=True):
|
||||
try:
|
||||
if complete:
|
||||
glob_pattern = "*.cache"
|
||||
for cache_filename in glob.glob(
|
||||
os.path.join(constants.CACHE_PATH, glob_pattern)
|
||||
):
|
||||
os.unlink(cache_filename)
|
||||
os.unlink(os.path.join(constants.CACHE_PATH, constants.IDENTIFIERS_FILENAME))
|
||||
except FileNotFoundError:
|
||||
vollog.log(constants.LOGLEVEL_VVVV, "Attempting to clear a non-existant cache")
|
||||
|
||||
@@ -159,7 +159,10 @@ class LinuxIntelStacker(interfaces.automagic.StackerLayerInterface):
|
||||
|
||||
# This we get for free
|
||||
aslr_shift = (
|
||||
init_task.files.cast("long unsigned int")
|
||||
int.from_bytes(
|
||||
init_task.files.cast("bytes", length=init_task.files.vol.size),
|
||||
byteorder=init_task.files.vol.data_format.byteorder,
|
||||
)
|
||||
- module.get_symbol("init_files").address
|
||||
)
|
||||
kaslr_shift = init_task_address - cls.virtual_to_physical_address(
|
||||
|
||||
@@ -14,6 +14,13 @@ from typing import Callable, Optional
|
||||
|
||||
import volatility3.framework.constants.linux
|
||||
import volatility3.framework.constants.windows
|
||||
from volatility3.framework.constants._version import (
|
||||
PACKAGE_VERSION,
|
||||
VERSION_MAJOR,
|
||||
VERSION_MINOR,
|
||||
VERSION_PATCH,
|
||||
VERSION_SUFFIX,
|
||||
)
|
||||
|
||||
PLUGINS_PATH = [
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "plugins")),
|
||||
@@ -42,20 +49,6 @@ if hasattr(sys, "frozen") and sys.frozen:
|
||||
BANG = "!"
|
||||
"""Constant used to delimit table names from type names when referring to a symbol"""
|
||||
|
||||
# We use the SemVer 2.0.0 versioning scheme
|
||||
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
|
||||
VERSION_MINOR = 7 # Number of changes that only add to the interface
|
||||
VERSION_PATCH = 0 # Number of changes that do not change the interface
|
||||
VERSION_SUFFIX = ""
|
||||
|
||||
# TODO: At version 2.0.0, remove the symbol_shift feature
|
||||
|
||||
PACKAGE_VERSION = (
|
||||
".".join([str(x) for x in [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH]])
|
||||
+ VERSION_SUFFIX
|
||||
)
|
||||
"""The canonical version of the volatility3 package"""
|
||||
|
||||
AUTOMAGIC_CONFIG_PATH = "automagic"
|
||||
"""The root section within the context configuration for automagic values"""
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# We use the SemVer 2.0.0 versioning scheme
|
||||
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
|
||||
VERSION_MINOR = 8 # Number of changes that only add to the interface
|
||||
VERSION_PATCH = 0 # Number of changes that do not change the interface
|
||||
VERSION_SUFFIX = ""
|
||||
|
||||
PACKAGE_VERSION = (
|
||||
".".join([str(x) for x in [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH]])
|
||||
+ VERSION_SUFFIX
|
||||
)
|
||||
"""The canonical version of the volatility3 package"""
|
||||
@@ -256,12 +256,13 @@ class Module(interfaces.context.ModuleInterface):
|
||||
if not absolute:
|
||||
offset += self._offset
|
||||
|
||||
# Ensure we don't use a layer_name other than the module's, why would anyone do that?
|
||||
if "layer_name" in kwargs:
|
||||
del kwargs["layer_name"]
|
||||
# We have to allow using an alternative layer name due to pool scanners switching
|
||||
# to the memory layer for scanning samples prior to Windows 10.
|
||||
layer_name = kwargs.pop("layer_name", self._layer_name)
|
||||
|
||||
return self._context.object(
|
||||
object_type=object_type,
|
||||
layer_name=self._layer_name,
|
||||
layer_name=layer_name,
|
||||
offset=offset,
|
||||
native_layer_name=native_layer_name or self._native_layer_name,
|
||||
**kwargs,
|
||||
|
||||
@@ -50,7 +50,7 @@ class AutomagicInterface(
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
*args,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(context, config_path)
|
||||
for requirement in self.get_requirements():
|
||||
|
||||
@@ -96,12 +96,13 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer):
|
||||
def get_summary_header(self) -> interfaces.objects.ObjectInterface:
|
||||
return self.context.object(
|
||||
self._crash_common_table_name + constants.BANG + "_SUMMARY_DUMP",
|
||||
offset=0x1000 * self.headerpages,
|
||||
offset=self._page_size * self.headerpages,
|
||||
layer_name=self._base_layer,
|
||||
)
|
||||
|
||||
def _load_segments(self) -> None:
|
||||
"""Loads up the segments from the meta_layer."""
|
||||
"""Loads up the segments from the meta_layer.
|
||||
A segment is a set of contiguous memory pages."""
|
||||
|
||||
segments = []
|
||||
|
||||
@@ -119,70 +120,87 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer):
|
||||
for run in header.PhysicalMemoryBlockBuffer.Run:
|
||||
segments.append(
|
||||
(
|
||||
run.BasePage * 0x1000,
|
||||
offset * 0x1000,
|
||||
run.PageCount * 0x1000,
|
||||
run.PageCount * 0x1000,
|
||||
run.BasePage * self._page_size,
|
||||
offset * self._page_size,
|
||||
run.PageCount * self._page_size,
|
||||
run.PageCount * self._page_size,
|
||||
)
|
||||
)
|
||||
offset += run.PageCount
|
||||
|
||||
elif self.dump_type == 0x05:
|
||||
summary_header = self.get_summary_header()
|
||||
first_bit = None # First bit in a run
|
||||
first_offset = 0 # File offset of first bit
|
||||
last_bit_seen = 0 # Most recent bit processed
|
||||
offset = summary_header.HeaderSize # Size of file headers
|
||||
buffer_char = summary_header.get_buffer_char()
|
||||
buffer_long = summary_header.get_buffer_long()
|
||||
|
||||
for outer_index in range(0, ((summary_header.BitmapSize + 31) // 32)):
|
||||
if buffer_long[outer_index] == 0:
|
||||
if first_bit is not None:
|
||||
last_bit = ((outer_index - 1) * 32) + 31
|
||||
segment_length = (last_bit - first_bit + 1) * 0x1000
|
||||
seg_first_bit = None # First bit in a run
|
||||
seg_first_offset = 0 # File offset of first bit
|
||||
offset = (
|
||||
summary_header.HeaderSize
|
||||
) # Offset to the start of actual memory dump
|
||||
ulong_bitmap_array = summary_header.get_buffer_long()
|
||||
# outer_index points to a 32 bits array inside a list of arrays,
|
||||
# each bit indicating a page mapping state
|
||||
for outer_index in range(0, ulong_bitmap_array.vol.count):
|
||||
ulong_bitmap = ulong_bitmap_array[outer_index]
|
||||
# All pages in this 32 bits array are mapped (speedup iteration process)
|
||||
if ulong_bitmap == 0xFFFFFFFF:
|
||||
# New segment
|
||||
if seg_first_bit is None:
|
||||
seg_first_offset = offset
|
||||
seg_first_bit = outer_index * 32
|
||||
offset += 32 * self._page_size
|
||||
# No pages in this 32 bits array are mapped (speedup iteration process)
|
||||
elif ulong_bitmap == 0:
|
||||
# End of segment
|
||||
if seg_first_bit is not None:
|
||||
last_bit = (outer_index - 1) * 32 + 31
|
||||
segment_length = (
|
||||
last_bit - seg_first_bit + 1
|
||||
) * self._page_size
|
||||
segments.append(
|
||||
(
|
||||
first_bit * 0x1000,
|
||||
first_offset,
|
||||
seg_first_bit * self._page_size,
|
||||
seg_first_offset,
|
||||
segment_length,
|
||||
segment_length,
|
||||
)
|
||||
)
|
||||
first_bit = None
|
||||
elif buffer_long[outer_index] == 0xFFFFFFFF:
|
||||
if first_bit is None:
|
||||
first_offset = offset
|
||||
first_bit = outer_index * 32
|
||||
offset = offset + (32 * 0x1000)
|
||||
seg_first_bit = None
|
||||
# Some pages in this 32 bits array are mapped and some aren't
|
||||
else:
|
||||
for inner_index in range(0, 32):
|
||||
bit_addr = outer_index * 32 + inner_index
|
||||
if (buffer_char[bit_addr >> 3] >> (bit_addr & 0x7)) & 1:
|
||||
if first_bit is None:
|
||||
first_offset = offset
|
||||
first_bit = bit_addr
|
||||
offset = offset + 0x1000
|
||||
for inner_bit_position in range(0, 32):
|
||||
current_bit = outer_index * 32 + inner_bit_position
|
||||
page_mapped = ulong_bitmap & (1 << inner_bit_position)
|
||||
if page_mapped:
|
||||
# New segment
|
||||
if seg_first_bit is None:
|
||||
seg_first_offset = offset
|
||||
seg_first_bit = current_bit
|
||||
offset += self._page_size
|
||||
else:
|
||||
if first_bit is not None:
|
||||
# End of segment
|
||||
if seg_first_bit is not None:
|
||||
segment_length = (
|
||||
(bit_addr - 1) - first_bit + 1
|
||||
) * 0x1000
|
||||
current_bit - 1 - seg_first_bit + 1
|
||||
) * self._page_size
|
||||
segments.append(
|
||||
(
|
||||
first_bit * 0x1000,
|
||||
first_offset,
|
||||
seg_first_bit * self._page_size,
|
||||
seg_first_offset,
|
||||
segment_length,
|
||||
segment_length,
|
||||
)
|
||||
)
|
||||
first_bit = None
|
||||
last_bit_seen = (outer_index * 32) + 31
|
||||
seg_first_bit = None
|
||||
last_bit_seen = outer_index * 32 + 31
|
||||
|
||||
if first_bit is not None:
|
||||
segment_length = (last_bit_seen - first_bit + 1) * 0x1000
|
||||
if seg_first_bit is not None:
|
||||
segment_length = (last_bit_seen - seg_first_bit + 1) * self._page_size
|
||||
segments.append(
|
||||
(first_bit * 0x1000, first_offset, segment_length, segment_length)
|
||||
(
|
||||
seg_first_bit * self._page_size,
|
||||
seg_first_offset,
|
||||
segment_length,
|
||||
segment_length,
|
||||
)
|
||||
)
|
||||
else:
|
||||
vollog.log(
|
||||
|
||||
@@ -180,7 +180,9 @@ class Intel(linear.LinearlyMappedLayer):
|
||||
position = self._initial_position
|
||||
entry = self._initial_entry
|
||||
|
||||
if self.minimum_address > offset > self.maximum_address:
|
||||
if not (
|
||||
self.minimum_address <= (offset & self.address_mask) <= self.maximum_address
|
||||
):
|
||||
raise exceptions.PagedInvalidAddressException(
|
||||
self.name,
|
||||
offset,
|
||||
|
||||
@@ -151,6 +151,12 @@ class ResourceAccessor(object):
|
||||
raise excp
|
||||
else:
|
||||
raise excp
|
||||
except ValueError as excp:
|
||||
# Reraise errors such as proxy auth errors as offline exception errors
|
||||
# Example Proxy auth error - ValueError: AbstractDigestAuthHandler does not support the following scheme: 'Negotiate'
|
||||
vollog.info(f"Cannot access {url} due to {excp} - Setting OFFLINE")
|
||||
constants.OFFLINE = True
|
||||
raise exceptions.OfflineException(url)
|
||||
except exceptions.OfflineException:
|
||||
vollog.info(f"Not accessing {url} in offline mode")
|
||||
raise
|
||||
|
||||
@@ -152,7 +152,7 @@ class NonLinearlySegmentedLayer(
|
||||
raise ValueError("SegmentedLayer must contain some segments")
|
||||
if self._maxaddr is None:
|
||||
mapped, _, length, _ = self._segments[-1]
|
||||
self._maxaddr = mapped + length
|
||||
self._maxaddr = mapped + length - 1
|
||||
return self._maxaddr
|
||||
|
||||
@property
|
||||
|
||||
@@ -7,6 +7,27 @@ from typing import Optional, Union
|
||||
from volatility3.framework import interfaces, objects, constants
|
||||
|
||||
|
||||
def rol(value: int, count: int, max_bits: int = 64) -> int:
|
||||
"""A rotate-left instruction in Python"""
|
||||
max_bits_mask = (1 << max_bits) - 1
|
||||
return (value << count % max_bits) & max_bits_mask | (
|
||||
(value & max_bits_mask) >> (max_bits - (count % max_bits))
|
||||
)
|
||||
|
||||
|
||||
def bswap_32(value: int) -> int:
|
||||
value = ((value << 8) & 0xFF00FF00) | ((value >> 8) & 0x00FF00FF)
|
||||
|
||||
return ((value << 16) | (value >> 16)) & 0xFFFFFFFF
|
||||
|
||||
|
||||
def bswap_64(value: int) -> int:
|
||||
low = bswap_32((value >> 32))
|
||||
high = bswap_32((value & 0xFFFFFFFF))
|
||||
|
||||
return ((high << 32) | low) & 0xFFFFFFFFFFFFFFFF
|
||||
|
||||
|
||||
def array_to_string(
|
||||
array: "objects.Array", count: Optional[int] = None, errors: str = "replace"
|
||||
) -> interfaces.objects.ObjectInterface:
|
||||
|
||||
@@ -18,7 +18,7 @@ class LayerWriter(plugins.PluginInterface):
|
||||
default_block_size = 0x500000
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
_version = (2, 0, 1)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -77,7 +77,7 @@ class LayerWriter(plugins.PluginInterface):
|
||||
|
||||
file_handle = open_method(preferred_name)
|
||||
for i in range(0, layer.maximum_address, chunk_size):
|
||||
current_chunk_size = min(chunk_size, layer.maximum_address - i)
|
||||
current_chunk_size = min(chunk_size, layer.maximum_address + 1 - i)
|
||||
data = layer.read(i, current_chunk_size, pad=True)
|
||||
file_handle.write(data)
|
||||
if progress_callback:
|
||||
@@ -95,7 +95,7 @@ class LayerWriter(plugins.PluginInterface):
|
||||
if not self.config["layers"]:
|
||||
self.config["layers"] = []
|
||||
for name in self.context.layers:
|
||||
if not self.context.layers[name].metadata.get("mapped", False):
|
||||
if "mapped" not in self.context.layers[name].metadata:
|
||||
self.config["layers"] = [name]
|
||||
|
||||
for name in self.config["layers"]:
|
||||
@@ -103,7 +103,8 @@ class LayerWriter(plugins.PluginInterface):
|
||||
if name not in self.context.layers:
|
||||
yield 0, (f"Layer Name {name} does not exist",)
|
||||
else:
|
||||
output_name = self.config.get("output", ".".join([name, "raw"]))
|
||||
default_output_name = f"{name}.raw"
|
||||
output_name = self.config.get("output", default_output_name)
|
||||
try:
|
||||
file_handle = self.write_layer(
|
||||
self.context,
|
||||
@@ -114,10 +115,12 @@ class LayerWriter(plugins.PluginInterface):
|
||||
progress_callback=self._progress_callback,
|
||||
)
|
||||
file_handle.close()
|
||||
|
||||
# Update the filename, which may have changed if a file
|
||||
# with the same name already existed.
|
||||
output_name = file_handle.preferred_filename
|
||||
except IOError as excp:
|
||||
yield 0, (
|
||||
f"Layer cannot be written to {self.config['output_name']}: {excp}",
|
||||
)
|
||||
yield 0, (f"Layer cannot be written to {output_name}: {excp}",)
|
||||
|
||||
yield 0, (f"Layer has been written to {output_name}",)
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class ABCKmsg(ABC):
|
||||
self._config = config
|
||||
self.vmlinux = context.modules[self._config["kernel"]]
|
||||
self.layer_name = self.vmlinux.layer_name # type: ignore
|
||||
self.long_unsigned_int_size = self.vmlinux.get_type("long unsigned int").size
|
||||
self.long_unsigned_int_size = self.vmlinux.get_type("pointer").size
|
||||
|
||||
@classmethod
|
||||
def run_all(
|
||||
|
||||
@@ -0,0 +1,738 @@
|
||||
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
from dataclasses import dataclass, field
|
||||
from abc import ABC, abstractmethod
|
||||
import logging
|
||||
|
||||
from typing import Iterator, List, Tuple
|
||||
from volatility3 import framework
|
||||
from volatility3.framework import (
|
||||
constants,
|
||||
interfaces,
|
||||
renderers,
|
||||
exceptions,
|
||||
)
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.symbols import linux
|
||||
from volatility3.plugins.linux import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Proto:
|
||||
name: str
|
||||
hooks: Tuple[str] = field(default_factory=tuple)
|
||||
|
||||
|
||||
PROTO_NOT_IMPLEMENTED = Proto(name="UNSPEC")
|
||||
|
||||
NF_INET_HOOKS = ("PRE_ROUTING", "LOCAL_IN", "FORWARD", "LOCAL_OUT", "POST_ROUTING")
|
||||
NF_DEC_HOOKS = (
|
||||
"PRE_ROUTING",
|
||||
"LOCAL_IN",
|
||||
"FORWARD",
|
||||
"LOCAL_OUT",
|
||||
"POST_ROUTING",
|
||||
"HELLO",
|
||||
"ROUTE",
|
||||
)
|
||||
NF_ARP_HOOKS = ("IN", "OUT", "FORWARD")
|
||||
NF_NETDEV_HOOKS = ("INGRESS", "EGRESS")
|
||||
LARGEST_HOOK_NUMBER = max(
|
||||
len(NF_INET_HOOKS), len(NF_DEC_HOOKS), len(NF_ARP_HOOKS), len(NF_NETDEV_HOOKS)
|
||||
)
|
||||
|
||||
|
||||
class AbstractNetfilter(ABC):
|
||||
"""Netfilter Abstract Base Classes handling details across various
|
||||
Netfilter implementations, including constants, helpers, and common
|
||||
routines.
|
||||
"""
|
||||
|
||||
PROTO_HOOKS = (
|
||||
PROTO_NOT_IMPLEMENTED, # NFPROTO_UNSPEC
|
||||
Proto(name="INET", hooks=NF_INET_HOOKS), # From kernels 3.14
|
||||
Proto(name="IPV4", hooks=NF_INET_HOOKS),
|
||||
Proto(name="ARP", hooks=NF_ARP_HOOKS),
|
||||
PROTO_NOT_IMPLEMENTED,
|
||||
Proto(name="NETDEV", hooks=NF_NETDEV_HOOKS),
|
||||
PROTO_NOT_IMPLEMENTED,
|
||||
Proto(name="BRIDGE", hooks=NF_INET_HOOKS),
|
||||
PROTO_NOT_IMPLEMENTED,
|
||||
PROTO_NOT_IMPLEMENTED,
|
||||
Proto(name="IPV6", hooks=NF_INET_HOOKS),
|
||||
PROTO_NOT_IMPLEMENTED,
|
||||
Proto(name="DECNET", hooks=NF_DEC_HOOKS), # Removed in kernel 6.1
|
||||
)
|
||||
NF_MAX_HOOKS = LARGEST_HOOK_NUMBER + 1
|
||||
|
||||
def __init__(
|
||||
self, context: interfaces.context.ContextInterface, kernel_module_name: str
|
||||
):
|
||||
self._context = context
|
||||
self.vmlinux = context.modules[kernel_module_name]
|
||||
self.layer_name = self.vmlinux.layer_name
|
||||
|
||||
# Set data sizes
|
||||
self.ptr_size = self.vmlinux.get_type("pointer").size
|
||||
self.list_head_size = self.vmlinux.get_type("list_head").size
|
||||
|
||||
lsmod_required_version = Netfilter._required_lsmod_version
|
||||
lsmod_current_version = lsmod.Lsmod._version
|
||||
if not requirements.VersionRequirement.matches_required(
|
||||
lsmod_required_version, lsmod_current_version
|
||||
):
|
||||
raise exceptions.PluginRequirementException(
|
||||
f"linux.lsmod.Lsmod version not suitable: required {lsmod_required_version} found {lsmod_current_version}"
|
||||
)
|
||||
|
||||
linuxutils_required_version = Netfilter._required_linuxutils_version
|
||||
linuxutils_current_version = linux.LinuxUtilities._version
|
||||
if not requirements.VersionRequirement.matches_required(
|
||||
linuxutils_required_version, linuxutils_current_version
|
||||
):
|
||||
raise exceptions.PluginRequirementException(
|
||||
f"linux.LinuxUtilities version not suitable: required {linuxutils_required_version} found {linuxutils_current_version}"
|
||||
)
|
||||
|
||||
modules = lsmod.Lsmod.list_modules(context, kernel_module_name)
|
||||
self.handlers = linux.LinuxUtilities.generate_kernel_handler_info(
|
||||
context, kernel_module_name, modules
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def run_all(
|
||||
cls, context: interfaces.context.ContextInterface, kernel_module_name: str
|
||||
) -> Iterator[Tuple[int, str, str, int, int, str, bool]]:
|
||||
"""It calls each subclass symtab_checks() to test the required
|
||||
conditions to that specific kernel implementation.
|
||||
|
||||
Args:
|
||||
context: The volatility3 context on which to operate
|
||||
kernel_module_name: The name of the table containing the kernel symbols
|
||||
|
||||
Yields:
|
||||
The kmsg records. Same as _run()
|
||||
"""
|
||||
vmlinux = context.modules[kernel_module_name]
|
||||
|
||||
implementation_inst = None # type: ignore
|
||||
for subclass in framework.class_subclasses(cls):
|
||||
if not subclass.symtab_checks(vmlinux=vmlinux):
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVVV,
|
||||
"Netfilter implementation '%s' doesn't match this memory dump",
|
||||
subclass.__name__,
|
||||
)
|
||||
continue
|
||||
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVVV,
|
||||
"Netfilter implementation '%s' matches!",
|
||||
subclass.__name__,
|
||||
)
|
||||
implementation_inst = subclass(
|
||||
context=context, kernel_module_name=kernel_module_name
|
||||
)
|
||||
# More than one class could be executed for an specific kernel version
|
||||
# For instance: Netfilter Ingress hooks
|
||||
yield from implementation_inst._run()
|
||||
|
||||
if implementation_inst is None:
|
||||
vollog.error("Unsupported Netfilter kernel implementation")
|
||||
|
||||
def _run(self) -> Iterator[Tuple[int, str, str, int, int, str, bool]]:
|
||||
"""Iterates over namespaces and protocols, executing various callbacks that
|
||||
allow customization of the code to the specific data structure used in a
|
||||
particular kernel implementation
|
||||
|
||||
get_hooks_container(net, proto_name, hook_name)
|
||||
It returns the data structure used in a specific kernel implementation
|
||||
to store the hooks for a respective namespace and protocol, basically:
|
||||
For Ingress hooks:
|
||||
network_namespace[] -> net_device[] -> nf_hooks_ingress[]
|
||||
For egress hooks:
|
||||
network_namespace[] -> net_device[] -> nf_hooks_egress[]
|
||||
For all the other Netfilter hooks:
|
||||
<= 4.2.8
|
||||
nf_hooks[]
|
||||
>= 4.3
|
||||
network_namespace[] -> nf.hooks[]
|
||||
|
||||
get_hook_ops(hook_container, proto_idx, hook_idx)
|
||||
Give the 'hook_container' got in get_hooks_container(), it
|
||||
returns an iterable of 'nf_hook_ops' elements for a respective protocol
|
||||
and hook type.
|
||||
|
||||
Returns:
|
||||
netns [int]: Network namespace id
|
||||
proto_name [str]: Protocol name
|
||||
hook_name [str]: Hook name
|
||||
priority [int]: Priority
|
||||
hook_ops_hook [int]: Hook address
|
||||
module_name [str]: Linux kernel module name
|
||||
hooked [bool]: hooked?
|
||||
"""
|
||||
for netns, net in self.get_net_namespaces():
|
||||
for proto_idx, proto_name, hook_idx, hook_name in self._proto_hook_loop():
|
||||
hooks_container = self.get_hooks_container(net, proto_name, hook_name)
|
||||
|
||||
for hook_container in hooks_container:
|
||||
for hook_ops in self.get_hook_ops(
|
||||
hook_container, proto_idx, hook_idx
|
||||
):
|
||||
if not hook_ops:
|
||||
continue
|
||||
|
||||
priority = int(hook_ops.priority)
|
||||
hook_ops_hook = hook_ops.hook
|
||||
module_name = self.get_module_name_for_address(hook_ops_hook)
|
||||
hooked = module_name is not None
|
||||
|
||||
yield netns, proto_name, hook_name, priority, hook_ops_hook, module_name, hooked
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def symtab_checks(cls, vmlinux: interfaces.context.ModuleInterface) -> bool:
|
||||
"""This method on each sublasss will be called to evaluate if the kernel
|
||||
being analyzed fulfill the type & symbols requirements for the implementation.
|
||||
The first class returning True will be instantiated and called via the
|
||||
run() method.
|
||||
|
||||
Returns:
|
||||
bool: True if the kernel being analyzed fulfill the class requirements.
|
||||
"""
|
||||
|
||||
def _proto_hook_loop(self) -> Iterator[Tuple[int, str, int, str]]:
|
||||
"""Flattens the protocol families and hooks"""
|
||||
for proto_idx, proto in enumerate(AbstractNetfilter.PROTO_HOOKS):
|
||||
if proto == PROTO_NOT_IMPLEMENTED:
|
||||
continue
|
||||
if proto.name not in self.subscribed_protocols():
|
||||
# This protocol is not managed in this object
|
||||
continue
|
||||
for hook_idx, hook_name in enumerate(proto.hooks):
|
||||
yield proto_idx, proto.name, hook_idx, hook_name
|
||||
|
||||
def build_nf_hook_ops_array(self, nf_hook_entries):
|
||||
"""Function helper to build the nf_hook_ops array when it is not part of the
|
||||
struct 'nf_hook_entries' definition.
|
||||
|
||||
nf_hook_ops was stored adjacent in memory to the nf_hook_entry array, in the
|
||||
new struct 'nf_hook_entries'. However, this 'nf_hooks_ops' array 'orig_ops' is
|
||||
not part of the 'nf_hook_entries' struct. So, we need to calculate the offset.
|
||||
|
||||
struct nf_hook_entries {
|
||||
u16 num_hook_entries; /* plus padding */
|
||||
struct nf_hook_entry hooks[];
|
||||
//const struct nf_hook_ops *orig_ops[];
|
||||
}
|
||||
"""
|
||||
nf_hook_entry_size = self.vmlinux.get_type("nf_hook_entry").size
|
||||
orig_ops_addr = (
|
||||
nf_hook_entries.hooks.vol.offset
|
||||
+ nf_hook_entry_size * nf_hook_entries.num_hook_entries
|
||||
)
|
||||
orig_ops = self._context.object(
|
||||
object_type=self.get_symbol_fullname("array"),
|
||||
offset=orig_ops_addr,
|
||||
subtype=self.vmlinux.get_type("pointer"),
|
||||
layer_name=self.layer_name,
|
||||
count=nf_hook_entries.num_hook_entries,
|
||||
)
|
||||
|
||||
return orig_ops
|
||||
|
||||
def subscribed_protocols(self) -> Tuple[str]:
|
||||
"""Allows to select which PROTO_HOOKS protocols will be processed by the
|
||||
Netfiler subclass.
|
||||
"""
|
||||
|
||||
# Most implementation handlers respond to these protocols, except for
|
||||
# the ingress hook, which specifically handles the 'NETDEV' protocol.
|
||||
# However, there is no corresponding Netfilter hook implementation for
|
||||
# the INET protocol in the kernel. AFAIU, this is used as
|
||||
# 'NFPROTO_INET = NFPROTO_IPV4 || NFPROTO_IPV6'
|
||||
# in other parts of the kernel source code.
|
||||
return ("IPV4", "ARP", "BRIDGE", "IPV6", "DECNET")
|
||||
|
||||
def get_module_name_for_address(self, addr) -> str:
|
||||
"""Helper to obtain the module and symbol name in the format needed for the
|
||||
output of this plugin.
|
||||
"""
|
||||
module_name, symbol_name = linux.LinuxUtilities.lookup_module_address(
|
||||
self.vmlinux, self.handlers, addr
|
||||
)
|
||||
|
||||
if module_name == "UNKNOWN":
|
||||
module_name = None
|
||||
|
||||
if symbol_name != "N/A":
|
||||
module_name = f"[{symbol_name}]"
|
||||
|
||||
return module_name
|
||||
|
||||
def get_net_namespaces(self):
|
||||
"""Common function to retrieve the different namespaces.
|
||||
From 4.3 on, all the implementations use network namespaces.
|
||||
"""
|
||||
nethead = self.vmlinux.object_from_symbol("net_namespace_list")
|
||||
symbol_net_name = self.get_symbol_fullname("net")
|
||||
for net in nethead.to_list(symbol_net_name, "list"):
|
||||
net_ns_id = net.ns.inum
|
||||
yield net_ns_id, net
|
||||
|
||||
def get_hooks_container(self, net, proto_name, hook_name):
|
||||
"""Returns the data structure used in a specific kernel implementation to store
|
||||
the hooks for a respective namespace and protocol.
|
||||
|
||||
Except for kernels < 4.3, all the implementations use network namespaces.
|
||||
Also the data structure which contains the hooks, even though it changes its
|
||||
implementation and/or data type, it is always in this location.
|
||||
"""
|
||||
yield net.nf.hooks
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
"""Given the hook_container obtained from get_hooks_container(), it
|
||||
returns an iterable of 'nf_hook_ops' elements for a corresponding protocol
|
||||
and hook type.
|
||||
|
||||
This is the most variable/unstable part of all Netfilter hook designs, it
|
||||
changes almost in every single implementation.
|
||||
"""
|
||||
raise NotImplementedError("You must implement this method")
|
||||
|
||||
def get_symbol_fullname(self, symbol_basename: str) -> str:
|
||||
"""Given a short symbol or type name, it returns its full name"""
|
||||
return self.vmlinux.symbol_table_name + constants.BANG + symbol_basename
|
||||
|
||||
@staticmethod
|
||||
def get_member_type(
|
||||
vol_type: interfaces.objects.Template, member_name: str
|
||||
) -> List[str]:
|
||||
"""Returns a list of types/subtypes belonging to the given type member.
|
||||
|
||||
Args:
|
||||
vol_type (interfaces.objects.Template): A vol3 type object
|
||||
member_name (str): The member name
|
||||
|
||||
Returns:
|
||||
list: A list of types/subtypes
|
||||
"""
|
||||
_size, vol_obj = vol_type.vol.members[member_name]
|
||||
type_name = vol_obj.type_name
|
||||
type_basename = type_name.split(constants.BANG)[1]
|
||||
member_type = [type_basename]
|
||||
cur_type = vol_obj
|
||||
while hasattr(cur_type, "subtype"):
|
||||
subtype_name = cur_type.subtype.type_name
|
||||
subtype_basename = subtype_name.split(constants.BANG)[1]
|
||||
member_type.append(subtype_basename)
|
||||
cur_type = cur_type.subtype
|
||||
|
||||
return member_type
|
||||
|
||||
|
||||
class NetfilterImp_to_4_3(AbstractNetfilter):
|
||||
"""At this point, Netfilter hooks were implemented as a linked list of struct
|
||||
'nf_hook_ops' type. One linked list per protocol per hook type.
|
||||
It was like that until 4.2.8.
|
||||
|
||||
struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
return vmlinux.has_symbol("nf_hooks")
|
||||
|
||||
def get_net_namespaces(self):
|
||||
# In kernels <= 4.2.8 netfilter hooks are not implemented per namespaces
|
||||
netns, net = renderers.NotAvailableValue(), renderers.NotAvailableValue()
|
||||
yield netns, net
|
||||
|
||||
def get_hooks_container(self, net, proto_name, hook_name):
|
||||
nf_hooks = self.vmlinux.object_from_symbol("nf_hooks")
|
||||
if not nf_hooks:
|
||||
return
|
||||
|
||||
yield nf_hooks
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
list_head = hook_container[proto_idx][hook_idx]
|
||||
nf_hooks_ops_name = self.get_symbol_fullname("nf_hook_ops")
|
||||
return list_head.to_list(nf_hooks_ops_name, "list")
|
||||
|
||||
|
||||
class NetfilterImp_4_3_to_4_9(AbstractNetfilter):
|
||||
"""Netfilter hooks were added to network namepaces in 4.3.
|
||||
It is still implemented as a linked list of 'struct nf_hook_ops' type but inside a
|
||||
network namespace. One linked list per protocol per hook type.
|
||||
|
||||
struct net { ... struct netns_nf nf; ... }
|
||||
struct netns_nf { ...
|
||||
struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; ... }
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("netns_nf")
|
||||
and vmlinux.get_type("netns_nf").has_member("hooks")
|
||||
and cls.get_member_type(vmlinux.get_type("netns_nf"), "hooks")
|
||||
== ["array", "array", "list_head"]
|
||||
)
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
list_head = hook_container[proto_idx][hook_idx]
|
||||
nf_hooks_ops_name = self.get_symbol_fullname("nf_hook_ops")
|
||||
return list_head.to_list(nf_hooks_ops_name, "list")
|
||||
|
||||
|
||||
class NetfilterImp_4_9_to_4_14(AbstractNetfilter):
|
||||
"""In this range of kernel versions, the doubly-linked lists of netfilter hooks were
|
||||
replaced by an array of arrays of 'nf_hook_entry' pointers in a singly-linked lists.
|
||||
struct net { ... struct netns_nf nf; ... }
|
||||
struct netns_nf { ..
|
||||
struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; ... }
|
||||
|
||||
Also in v4.10 the struct nf_hook_entry changed, a hook function pointer was added to
|
||||
it. However, for simplicity of this design, we will still take the hook address from
|
||||
the 'nf_hook_ops'. As per v5.0-rc2, the hook address is duplicated in both sides.
|
||||
- v4.9:
|
||||
struct nf_hook_entry {
|
||||
struct nf_hook_entry *next;
|
||||
struct nf_hook_ops ops;
|
||||
const struct nf_hook_ops *orig_ops; };
|
||||
- v4.10:
|
||||
struct nf_hook_entry {
|
||||
struct nf_hook_entry *next;
|
||||
nf_hookfn *hook;
|
||||
void *priv;
|
||||
const struct nf_hook_ops *orig_ops; };
|
||||
(*) Even though the hook address is in the struct 'nf_hook_entry', we use the
|
||||
original 'nf_hook_ops' hook address value, the one which was filled by the user, to
|
||||
make it uniform to all the implementations.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
hooks_type = ["array", "array", "pointer", "nf_hook_entry"]
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("netns_nf")
|
||||
and vmlinux.get_type("netns_nf").has_member("hooks")
|
||||
and cls.get_member_type(vmlinux.get_type("netns_nf"), "hooks") == hooks_type
|
||||
)
|
||||
|
||||
def _get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
list_head = hook_container[proto_idx][hook_idx]
|
||||
nf_hooks_ops_name = self.get_symbol_fullname("nf_hook_ops")
|
||||
return list_head.to_list(nf_hooks_ops_name, "list")
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
nf_hook_entry_list = hook_container[proto_idx][hook_idx]
|
||||
while nf_hook_entry_list:
|
||||
yield nf_hook_entry_list.orig_ops
|
||||
nf_hook_entry_list = nf_hook_entry_list.next
|
||||
|
||||
|
||||
class NetfilterImp_4_14_to_4_16(AbstractNetfilter):
|
||||
"""'nf_hook_ops' was removed from struct 'nf_hook_entry'. Instead, it was stored
|
||||
adjacent in memory to the 'nf_hook_entry' array, in the new struct 'nf_hook_entries'
|
||||
However, 'orig_ops' is not part of the 'nf_hook_entries' struct definition. So, we
|
||||
have to craft it by hand.
|
||||
|
||||
struct net { ... struct netns_nf nf; ... }
|
||||
struct netns_nf {
|
||||
struct nf_hook_entries *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; ... }
|
||||
struct nf_hook_entries {
|
||||
u16 num_hook_entries; /* plus padding */
|
||||
struct nf_hook_entry hooks[];
|
||||
//const struct nf_hook_ops *orig_ops[]; }
|
||||
struct nf_hook_entry {
|
||||
nf_hookfn *hook;
|
||||
void *priv; }
|
||||
|
||||
(*) Even though the hook address is in the struct 'nf_hook_entry', we use the
|
||||
original 'nf_hook_ops' hook address value, the one which was filled by the user, to
|
||||
make it uniform to all the implementations.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
hooks_type = ["array", "array", "pointer", "nf_hook_entries"]
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("netns_nf")
|
||||
and vmlinux.get_type("netns_nf").has_member("hooks")
|
||||
and cls.get_member_type(vmlinux.get_type("netns_nf"), "hooks") == hooks_type
|
||||
)
|
||||
|
||||
def get_nf_hook_entries(self, nf_hooks_addr, proto_idx, hook_idx):
|
||||
"""This allows to support different hook array implementations from this version
|
||||
on. For instance, in kernels >= 4.16 this multi-dimensional array is split in
|
||||
one-dimensional array of pointers to 'nf_hooks_entries' per each protocol."""
|
||||
return nf_hooks_addr[proto_idx][hook_idx]
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
nf_hook_entries = self.get_nf_hook_entries(hook_container, proto_idx, hook_idx)
|
||||
if not nf_hook_entries:
|
||||
return
|
||||
|
||||
nf_hook_ops_name = self.get_symbol_fullname("nf_hook_ops")
|
||||
nf_hook_ops_ptr_arr = self.build_nf_hook_ops_array(nf_hook_entries)
|
||||
for nf_hook_ops_ptr in nf_hook_ops_ptr_arr:
|
||||
nf_hook_ops = nf_hook_ops_ptr.dereference().cast(nf_hook_ops_name)
|
||||
yield nf_hook_ops
|
||||
|
||||
|
||||
class NetfilterImp_4_16_to_latest(NetfilterImp_4_14_to_4_16):
|
||||
"""The multidimensional array of nf_hook_entries was split in a one-dimensional
|
||||
array per each protocol.
|
||||
|
||||
struct net {
|
||||
struct netns_nf nf; ... }
|
||||
struct netns_nf {
|
||||
struct nf_hook_entries * hooks_ipv4[NF_INET_NUMHOOKS];
|
||||
struct nf_hook_entries * hooks_ipv6[NF_INET_NUMHOOKS];
|
||||
struct nf_hook_entries * hooks_arp[NF_ARP_NUMHOOKS];
|
||||
struct nf_hook_entries * hooks_bridge[NF_INET_NUMHOOKS];
|
||||
struct nf_hook_entries * hooks_decnet[NF_DN_NUMHOOKS]; ... }
|
||||
struct nf_hook_entries {
|
||||
u16 num_hook_entries; /* plus padding */
|
||||
struct nf_hook_entry hooks[];
|
||||
//const struct nf_hook_ops *orig_ops[]; }
|
||||
struct nf_hook_entry {
|
||||
nf_hookfn *hook;
|
||||
void *priv; }
|
||||
|
||||
(*) Even though the hook address is in the struct nf_hook_entry, we use the original
|
||||
nf_hook_ops hook address value, the one which was filled by the user, to make it
|
||||
uniform to all the implementations.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("netns_nf")
|
||||
and vmlinux.get_type("netns_nf").has_member("hooks_ipv4")
|
||||
)
|
||||
|
||||
def get_hooks_container(self, net, proto_name, hook_name):
|
||||
try:
|
||||
if proto_name == "IPV4":
|
||||
net_nf_hooks = net.nf.hooks_ipv4
|
||||
elif proto_name == "ARP":
|
||||
net_nf_hooks = net.nf.hooks_arp
|
||||
elif proto_name == "BRIDGE":
|
||||
net_nf_hooks = net.nf.hooks_bridge
|
||||
elif proto_name == "IPV6":
|
||||
net_nf_hooks = net.nf.hooks_ipv6
|
||||
elif proto_name == "DECNET":
|
||||
net_nf_hooks = net.nf.hooks_decnet
|
||||
else:
|
||||
return
|
||||
|
||||
yield net_nf_hooks
|
||||
|
||||
except AttributeError:
|
||||
# Protocol family disabled at kernel compilation
|
||||
# CONFIG_NETFILTER_FAMILY_ARP=n ||
|
||||
# CONFIG_NETFILTER_FAMILY_BRIDGE=n ||
|
||||
# CONFIG_DECNET=n
|
||||
pass
|
||||
|
||||
def _get_nf_hook_entries_ptr(self, nf_hooks_addr, proto_idx, hook_idx):
|
||||
nf_hook_entries_ptr = nf_hooks_addr[hook_idx]
|
||||
return nf_hook_entries_ptr
|
||||
|
||||
def get_nf_hook_entries(self, nf_hooks_addr, proto_idx, hook_idx):
|
||||
return nf_hooks_addr[hook_idx]
|
||||
|
||||
|
||||
class AbstractNetfilterNetDev(AbstractNetfilter):
|
||||
"""Base class to handle the Netfilter NetDev hooks.
|
||||
It won't be executed. It has some common functions to all Netfilter NetDev hook
|
||||
implementions.
|
||||
|
||||
Netfilter NetDev hooks are set per network device which belongs to a network
|
||||
namespace.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
return False
|
||||
|
||||
def subscribed_protocols(self):
|
||||
return ("NETDEV",)
|
||||
|
||||
def get_hooks_container(self, net, proto_name, hook_name):
|
||||
net_device_type = self.vmlinux.get_type("net_device")
|
||||
net_device_name = self.get_symbol_fullname("net_device")
|
||||
for net_device in net.dev_base_head.to_list(net_device_name, "dev_list"):
|
||||
if hook_name == "INGRESS":
|
||||
if net_device_type.has_member("nf_hooks_ingress"):
|
||||
# CONFIG_NETFILTER_INGRESS=y
|
||||
yield net_device.nf_hooks_ingress
|
||||
|
||||
elif hook_name == "EGRESS":
|
||||
if net_device_type.has_member("nf_hooks_egress"):
|
||||
# CONFIG_NETFILTER_EGRESS=y
|
||||
yield net_device.nf_hooks_egress
|
||||
|
||||
|
||||
class NetfilterNetDevImp_4_2_to_4_9(AbstractNetfilterNetDev):
|
||||
"""This is the first version of Netfilter Ingress hooks which was implemented using
|
||||
a doubly-linked list of 'nf_hook_ops'.
|
||||
struct list_head nf_hooks_ingress;
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
hooks_type = ["list_head"]
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("net_device")
|
||||
and vmlinux.get_type("net_device").has_member("nf_hooks_ingress")
|
||||
and cls.get_member_type(vmlinux.get_type("net_device"), "nf_hooks_ingress")
|
||||
== hooks_type
|
||||
)
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
nf_hooks_ingress = hook_container
|
||||
nf_hook_ops_name = self.get_symbol_fullname("nf_hook_ops")
|
||||
return nf_hooks_ingress.to_list(nf_hook_ops_name, "list")
|
||||
|
||||
|
||||
class NetfilterNetDevImp_4_9_to_4_14(AbstractNetfilterNetDev):
|
||||
"""In 4.9 it was changed to a simple singly-linked list.
|
||||
struct nf_hook_entry * nf_hooks_ingress;
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
hooks_type = ["pointer", "nf_hook_entry"]
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("net_device")
|
||||
and vmlinux.get_type("net_device").has_member("nf_hooks_ingress")
|
||||
and cls.get_member_type(vmlinux.get_type("net_device"), "nf_hooks_ingress")
|
||||
== hooks_type
|
||||
)
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
nf_hooks_ingress_ptr = hook_container
|
||||
if not nf_hooks_ingress_ptr:
|
||||
return
|
||||
|
||||
while nf_hooks_ingress_ptr:
|
||||
nf_hook_entry = nf_hooks_ingress_ptr.dereference()
|
||||
orig_ops = nf_hook_entry.orig_ops.dereference()
|
||||
yield orig_ops
|
||||
nf_hooks_ingress_ptr = nf_hooks_ingress_ptr.next
|
||||
|
||||
|
||||
class NetfilterNetDevImp_4_14_to_latest(AbstractNetfilterNetDev):
|
||||
"""In 4.14 the hook list was converted to an array of pointers inside the struct
|
||||
'nf_hook_entries':
|
||||
struct nf_hook_entries * nf_hooks_ingress;
|
||||
struct nf_hook_entries {
|
||||
u16 num_hook_entries;
|
||||
struct nf_hook_entry hooks[];
|
||||
//const struct nf_hook_ops *orig_ops[]; }
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def symtab_checks(cls, vmlinux) -> bool:
|
||||
hooks_type = ["pointer", "nf_hook_entries"]
|
||||
return (
|
||||
vmlinux.has_symbol("net_namespace_list")
|
||||
and vmlinux.has_type("net_device")
|
||||
and vmlinux.get_type("net_device").has_member("nf_hooks_ingress")
|
||||
and cls.get_member_type(vmlinux.get_type("net_device"), "nf_hooks_ingress")
|
||||
== hooks_type
|
||||
)
|
||||
|
||||
def get_hook_ops(self, hook_container, proto_idx, hook_idx):
|
||||
nf_hook_entries = hook_container
|
||||
if not nf_hook_entries:
|
||||
return
|
||||
|
||||
nf_hook_ops_name = self.get_symbol_fullname("nf_hook_ops")
|
||||
nf_hook_ops_ptr_arr = self.build_nf_hook_ops_array(nf_hook_entries)
|
||||
for nf_hook_ops_ptr in nf_hook_ops_ptr_arr:
|
||||
nf_hook_ops = nf_hook_ops_ptr.dereference().cast(nf_hook_ops_name)
|
||||
yield nf_hook_ops
|
||||
|
||||
|
||||
class Netfilter(interfaces.plugins.PluginInterface):
|
||||
"""Lists Netfilter hooks."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
_required_linuxutils_version = (2, 1, 0)
|
||||
_required_lsmod_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Linux kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="lsmod", plugin=lsmod.Lsmod, version=cls._required_lsmod_version
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="linuxutils",
|
||||
component=linux.LinuxUtilities,
|
||||
version=cls._required_linuxutils_version,
|
||||
),
|
||||
]
|
||||
|
||||
def _format_fields(self, fields):
|
||||
(
|
||||
netns,
|
||||
proto_name,
|
||||
hook_name,
|
||||
priority,
|
||||
hook_func,
|
||||
module_name,
|
||||
hooked,
|
||||
) = fields
|
||||
return (
|
||||
netns,
|
||||
proto_name,
|
||||
hook_name,
|
||||
priority,
|
||||
format_hints.Hex(hook_func),
|
||||
module_name,
|
||||
str(hooked),
|
||||
)
|
||||
|
||||
def _generator(self):
|
||||
kernel_module_name = self.config["kernel"]
|
||||
for fields in AbstractNetfilter.run_all(
|
||||
context=self.context, kernel_module_name=kernel_module_name
|
||||
):
|
||||
yield (0, self._format_fields(fields))
|
||||
|
||||
def run(self):
|
||||
headers = [
|
||||
("Net NS", int),
|
||||
("Proto", str),
|
||||
("Hook", str),
|
||||
("Priority", int),
|
||||
("Handler", format_hints.Hex),
|
||||
("Module", str),
|
||||
("Is Hooked", str),
|
||||
]
|
||||
return renderers.TreeGrid(headers, self._generator())
|
||||
@@ -105,7 +105,9 @@ class Timeliner(interfaces.plugins.PluginInterface):
|
||||
data = item[1]
|
||||
|
||||
def sortable(timestamp):
|
||||
max_date = datetime.datetime(day=1, month=12, year=datetime.MAXYEAR)
|
||||
max_date = datetime.datetime(
|
||||
day=1, month=12, year=datetime.MAXYEAR, tzinfo=datetime.timezone.utc
|
||||
)
|
||||
if isinstance(timestamp, interfaces.renderers.BaseAbsentValue):
|
||||
return max_date
|
||||
return timestamp
|
||||
@@ -183,16 +185,16 @@ class Timeliner(interfaces.plugins.PluginInterface):
|
||||
plugin_name,
|
||||
self._sanitize_body_format(item),
|
||||
self._text_format(
|
||||
times.get(TimeLinerType.ACCESSED, "")
|
||||
times.get(TimeLinerType.ACCESSED, "0")
|
||||
),
|
||||
self._text_format(
|
||||
times.get(TimeLinerType.MODIFIED, "")
|
||||
times.get(TimeLinerType.MODIFIED, "0")
|
||||
),
|
||||
self._text_format(
|
||||
times.get(TimeLinerType.CHANGED, "")
|
||||
times.get(TimeLinerType.CHANGED, "0")
|
||||
),
|
||||
self._text_format(
|
||||
times.get(TimeLinerType.CREATED, "")
|
||||
times.get(TimeLinerType.CREATED, "0")
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
# This file is Copyright 2023 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
import enum
|
||||
import logging
|
||||
import os
|
||||
import struct
|
||||
from typing import Dict, List
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import configuration, plugins
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VMCSTest(enum.IntFlag):
|
||||
VMCS_ABORT_INVALID = enum.auto()
|
||||
VMCS_LINK_PTR_IS_NOT_FS = enum.auto()
|
||||
VMCS_HOST_CR4_NO_VTX = enum.auto()
|
||||
VMCS_CR3_IS_ZERO = enum.auto()
|
||||
VMCS_GUEST_CR4_RESERVED = enum.auto()
|
||||
|
||||
|
||||
class PageStartScanner(interfaces.layers.ScannerInterface):
|
||||
def __init__(self, signatures: List[bytes], page_size: int = 0x1000):
|
||||
super().__init__()
|
||||
if not len(signatures):
|
||||
raise ValueError("No signatures passed to constructor")
|
||||
self._siglen = len(signatures[0])
|
||||
for item in signatures:
|
||||
if len(item) != self._siglen:
|
||||
raise ValueError(
|
||||
"Signatures of different lengths passed to PageStartScanner"
|
||||
)
|
||||
self._signatures = signatures
|
||||
self._page_size = page_size
|
||||
|
||||
def __call__(self, data: bytes, data_offset: int):
|
||||
"""Scans only the start of every page, to see whether a signature is present or not"""
|
||||
for page_start in range(
|
||||
data_offset % self._page_size, len(data), self._page_size
|
||||
):
|
||||
if data[page_start : page_start + self._siglen] in self._signatures:
|
||||
yield (
|
||||
page_start + data_offset,
|
||||
data[page_start : page_start + self._siglen],
|
||||
)
|
||||
|
||||
|
||||
class Vmscan(plugins.PluginInterface):
|
||||
"""Scans for Intel VT-d structues and generates VM volatility configs for them"""
|
||||
|
||||
_required_framework_version = (2, 2, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
STRICTLY_REQUIRED_TESTS = {
|
||||
VMCSTest.VMCS_ABORT_INVALID,
|
||||
VMCSTest.VMCS_LINK_PTR_IS_NOT_FS,
|
||||
VMCSTest.VMCS_HOST_CR4_NO_VTX,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(
|
||||
name="primary", description="Physical base memory layer"
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="log-threshold",
|
||||
description="Number of criteria failed to log to debug output",
|
||||
default=2,
|
||||
optional=True,
|
||||
),
|
||||
]
|
||||
|
||||
# Scan for VMCS structures based on the known VMCS structures
|
||||
# found in symbols/vmcs directory
|
||||
|
||||
def _gather_vmcs_structures(
|
||||
self, context: interfaces.context.ContextInterface, config_path: str
|
||||
) -> Dict[bytes, str]:
|
||||
"""Enumerate all JSON files containing VMCS information and return the structures
|
||||
Signatures can be generated using data extracted using the vmcs_layout tool at
|
||||
https://github.com/google/rekall/tree/master/tools/linux/vmcs_layout
|
||||
|
||||
Args:
|
||||
context: The volatility context to work against
|
||||
config_path: The location to store symbol table configurations under
|
||||
|
||||
Returns:
|
||||
A dictionary of pattern bytes to the string representation of the architecture
|
||||
"""
|
||||
filenames = intermed.IntermediateSymbolTable.file_symbol_url(
|
||||
os.path.join("generic", "vmcs")
|
||||
)
|
||||
table_names = []
|
||||
for filename in filenames:
|
||||
base_name = os.path.basename(filename).split(".")[0]
|
||||
table_name = intermed.IntermediateSymbolTable.create(
|
||||
context,
|
||||
configuration.path_join(config_path, "vmcs"),
|
||||
os.path.join("generic", "vmcs"),
|
||||
filename=base_name,
|
||||
)
|
||||
table_names.append(table_name)
|
||||
|
||||
result = {}
|
||||
for table_name in table_names:
|
||||
symbol_table = context.symbol_space[table_name]
|
||||
revision_id = struct.pack(
|
||||
"<I", int(symbol_table.get_symbol("revision_id").constant_data)
|
||||
)
|
||||
result[revision_id] = table_name
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _verify_vmcs_page(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
vmcs: interfaces.objects.ObjectInterface,
|
||||
) -> List[str]:
|
||||
"""Runs tests to verify whether a block of data is a VMCS page
|
||||
Some tests based on the Hypervisor Memory Forensics paper by
|
||||
Mariano Graziano, Andrea Lanzi and Davide Balzarotti
|
||||
|
||||
Args:
|
||||
context: The volatility context to be used for this call
|
||||
vmcs: The instantiated VMCS object to verify
|
||||
|
||||
Returns:
|
||||
The list of failed criteria that the VMCS did not meet
|
||||
"""
|
||||
|
||||
# The VMCS should have been constructed on the physical layer (even a nested VMCS)
|
||||
physical_layer_name = vmcs.vol.layer_name
|
||||
|
||||
failed_tests: VMCSTest = VMCSTest(0)
|
||||
# The abort field must be valid (generally 0, although other abort codes may exist)
|
||||
if context.layers[physical_layer_name].read(vmcs.vol.offset + 4, 4) not in [
|
||||
b"\x00\x00\x00\x00"
|
||||
]:
|
||||
failed_tests |= VMCSTest.VMCS_ABORT_INVALID
|
||||
# The vmcs link pointer is supposed to always be set
|
||||
if vmcs.vmcs_link_ptr != 0xFFFFFFFFFFFFFFFF:
|
||||
failed_tests |= VMCSTest.VMCS_LINK_PTR_IS_NOT_FS
|
||||
# To have a VMCS the host needs the VTx bit set in CR4, this can false positive often when all bits are set
|
||||
if (vmcs.host_cr4 & 1 << 13) == 0:
|
||||
failed_tests |= VMCSTest.VMCS_HOST_CR4_NO_VTX
|
||||
# The guest CR3 is *exceptionally* unlikely to be 0 and the guest cr4 is likely to have some bits unset
|
||||
if (vmcs.guest_cr3 == 0) or (vmcs.host_cr3 == 0):
|
||||
failed_tests |= VMCSTest.VMCS_CR3_IS_ZERO
|
||||
# CR4 registers have certain bits reserved that should not be set
|
||||
if vmcs.guest_cr4 & 0xFFFFFFFFFF889000:
|
||||
failed_tests |= VMCSTest.VMCS_GUEST_CR4_RESERVED
|
||||
|
||||
if failed_tests and failed_tests.name:
|
||||
failed_list = failed_tests.name.split("|")
|
||||
return failed_list
|
||||
|
||||
return []
|
||||
|
||||
def _generator(self):
|
||||
# Gather VMCS structures
|
||||
structures = self._gather_vmcs_structures(self.context, self.config_path)
|
||||
# Scan memory for them
|
||||
layer = self.context.layers[self.config["primary"]]
|
||||
|
||||
# Try to move down to the highest physical layer
|
||||
if layer.config.get("memory_layer"):
|
||||
layer = self.context.layers[layer.config["memory_layer"]]
|
||||
|
||||
# Run the scan
|
||||
for offset, match in layer.scan(
|
||||
self.context,
|
||||
PageStartScanner(list(structures.keys())),
|
||||
self._progress_callback,
|
||||
):
|
||||
try:
|
||||
vmcs = self.context.object(
|
||||
structures[match] + constants.BANG + "_VMCS",
|
||||
layer.name,
|
||||
offset=offset,
|
||||
)
|
||||
failed_list = self._verify_vmcs_page(self.context, vmcs)
|
||||
if not failed_list:
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
structures[match],
|
||||
format_hints.Hex(vmcs.vol.offset),
|
||||
format_hints.Hex(vmcs.ept),
|
||||
format_hints.Hex(vmcs.guest_cr3),
|
||||
),
|
||||
)
|
||||
if len(failed_list) <= self.config["log-threshold"]:
|
||||
vollog.debug(
|
||||
f"Potential {structures[match]} VMCS found at {vmcs.vol.offset:x} with failed criteria: {failed_list}"
|
||||
)
|
||||
except (exceptions.InvalidAddressException, AttributeError):
|
||||
# Not what we're looking for
|
||||
continue
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Architecture", str),
|
||||
("VMCS Physical offset", format_hints.Hex),
|
||||
("EPT", format_hints.Hex),
|
||||
("Guest CR3", format_hints.Hex),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -2,16 +2,24 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
import logging
|
||||
import contextlib
|
||||
from typing import List, Iterable, Tuple, Optional, Union
|
||||
import logging
|
||||
from typing import Dict, Iterable, List, Optional, Tuple, Union, cast
|
||||
|
||||
from volatility3.framework import constants, exceptions, renderers, interfaces, symbols
|
||||
from volatility3.framework import (
|
||||
constants,
|
||||
exceptions,
|
||||
interfaces,
|
||||
objects,
|
||||
renderers,
|
||||
symbols,
|
||||
)
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows import versions
|
||||
from volatility3.plugins.windows import ssdt
|
||||
from volatility3.framework.symbols.windows.extensions import callbacks
|
||||
from volatility3.plugins.windows import driverirp, handles, poolscanner, ssdt
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,7 +28,7 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
"""Lists kernel callbacks and notification routines."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -33,27 +41,154 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="poolscanner", plugin=poolscanner.PoolScanner, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="driverirp", plugin=driverirp.DriverIrp, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="handles", plugin=handles.Handles, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def create_callback_table(
|
||||
@classmethod
|
||||
def create_callback_scan_constraints(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Creates a symbol table for a set of callbacks.
|
||||
is_vista_or_above: bool,
|
||||
) -> List[poolscanner.PoolConstraint]:
|
||||
"""Creates a list of Pool Tag Constraints for callback objects.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
config_path: The configuration path within the context of the symbol table to create
|
||||
symbol_table: The name of an existing symbol table containing the symbols / types
|
||||
is_vista_or_above: A boolean indicating whether the OS version is Vista or newer.
|
||||
|
||||
Returns:
|
||||
The name of the constructed callback table
|
||||
The list containing the built constraints.
|
||||
"""
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
constraints = cls._create_default_scan_constraints(context, symbol_table)
|
||||
if is_vista_or_above:
|
||||
constraints += cls._create_scan_constraints_vista_and_above(symbol_table)
|
||||
return constraints
|
||||
|
||||
@staticmethod
|
||||
def _create_default_scan_constraints(
|
||||
context: interfaces.context.ContextInterface, symbol_table: str
|
||||
) -> List[poolscanner.PoolConstraint]:
|
||||
|
||||
shutdown_packet_size = context.symbol_space.get_type(
|
||||
symbol_table + constants.BANG + "_SHUTDOWN_PACKET"
|
||||
).size
|
||||
generic_callback_size = context.symbol_space.get_type(
|
||||
symbol_table + constants.BANG + "_GENERIC_CALLBACK"
|
||||
).size
|
||||
notification_packet_size = context.symbol_space.get_type(
|
||||
symbol_table + constants.BANG + "_NOTIFICATION_PACKET"
|
||||
).size
|
||||
|
||||
return [
|
||||
poolscanner.PoolConstraint(
|
||||
b"IoFs",
|
||||
type_name=symbol_table + constants.BANG + "_NOTIFICATION_PACKET",
|
||||
size=(notification_packet_size, None),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
),
|
||||
poolscanner.PoolConstraint(
|
||||
b"IoSh",
|
||||
type_name=symbol_table + constants.BANG + "_SHUTDOWN_PACKET",
|
||||
size=(shutdown_packet_size, None),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
index=(0, 0),
|
||||
),
|
||||
poolscanner.PoolConstraint(
|
||||
b"Cbrb",
|
||||
type_name=symbol_table + constants.BANG + "_GENERIC_CALLBACK",
|
||||
size=(generic_callback_size, None),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _create_scan_constraints_vista_and_above(
|
||||
symbol_table: str,
|
||||
) -> List[poolscanner.PoolConstraint]:
|
||||
"""Creates a list of Pool Tag Constraints for callback objects.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
symbol_table: The name of an existing symbol table containing the symbols / types
|
||||
|
||||
Returns:
|
||||
The list containing the built constraints.
|
||||
"""
|
||||
|
||||
return [
|
||||
poolscanner.PoolConstraint(
|
||||
b"DbCb",
|
||||
type_name=symbol_table + constants.BANG + "_DBGPRINT_CALLBACK",
|
||||
size=(0x20, 0x40),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
),
|
||||
poolscanner.PoolConstraint(
|
||||
b"Pnp9",
|
||||
type_name=symbol_table + constants.BANG + "_NOTIFY_ENTRY_HEADER",
|
||||
size=(0x30, None),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
index=(1, 1),
|
||||
),
|
||||
poolscanner.PoolConstraint(
|
||||
b"PnpD",
|
||||
type_name=symbol_table + constants.BANG + "_NOTIFY_ENTRY_HEADER",
|
||||
size=(0x40, None),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
index=(1, 1),
|
||||
),
|
||||
poolscanner.PoolConstraint(
|
||||
b"PnpC",
|
||||
type_name=symbol_table + constants.BANG + "_NOTIFY_ENTRY_HEADER",
|
||||
size=(0x38, None),
|
||||
page_type=poolscanner.PoolType.NONPAGED
|
||||
| poolscanner.PoolType.PAGED
|
||||
| poolscanner.PoolType.FREE,
|
||||
index=(1, 1),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def create_callback_symbol_table(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
nt_symbol_table: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Creates a symbol table for kernel callback objects.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
nt_symbol_table: The name of the table containing the kernel symbols
|
||||
config_path: The config path where to find symbol files
|
||||
|
||||
Returns:
|
||||
The name of the constructed symbol table
|
||||
"""
|
||||
native_types = context.symbol_space[nt_symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, nt_symbol_table)
|
||||
table_mapping = {"nt_symbols": nt_symbol_table}
|
||||
|
||||
if is_64bit:
|
||||
symbol_filename = "callbacks-x64"
|
||||
@@ -65,10 +200,143 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
config_path,
|
||||
"windows",
|
||||
symbol_filename,
|
||||
class_types=callbacks.class_types_x86 if not is_64bit else None,
|
||||
native_types=native_types,
|
||||
table_mapping=table_mapping,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def scan(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
nt_symbol_table: str,
|
||||
callback_symbol_table: str,
|
||||
) -> Iterable[
|
||||
Tuple[
|
||||
Union[str, interfaces.renderers.BaseAbsentValue],
|
||||
int,
|
||||
Union[str, interfaces.renderers.BaseAbsentValue],
|
||||
]
|
||||
]:
|
||||
"""Scans for callback objects using the poolscanner module and constraints.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
nt_symbol_table: The name of the table containing the kernel symbols
|
||||
callback_symbol_table: The name of the table containing the callback object symbols (_SHUTDOWN_PACKET etc.)
|
||||
|
||||
Returns:
|
||||
A list of callback objects found by scanning the `layer_name` layer for callback pool signatures
|
||||
"""
|
||||
is_vista_or_later = versions.is_vista_or_later(
|
||||
context=context, symbol_table=nt_symbol_table
|
||||
)
|
||||
|
||||
type_map = handles.Handles.get_type_map(context, layer_name, nt_symbol_table)
|
||||
|
||||
constraints = cls.create_callback_scan_constraints(
|
||||
context, callback_symbol_table, is_vista_or_later
|
||||
)
|
||||
|
||||
for (
|
||||
_constraint,
|
||||
mem_object,
|
||||
_header,
|
||||
) in poolscanner.PoolScanner.generate_pool_scan(
|
||||
context, layer_name, nt_symbol_table, constraints
|
||||
):
|
||||
try:
|
||||
if hasattr(mem_object, "is_valid") and not mem_object.is_valid():
|
||||
continue
|
||||
|
||||
yield cls._process_scanned_callback(mem_object, type_map)
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
@classmethod
|
||||
def _process_scanned_callback(
|
||||
cls, memory_object: objects.StructType, type_map: Dict[int, str]
|
||||
) -> Tuple[
|
||||
Union[str, interfaces.renderers.BaseAbsentValue],
|
||||
int,
|
||||
Union[str, interfaces.renderers.BaseAbsentValue],
|
||||
]:
|
||||
symbol_table = memory_object.get_symbol_table_name()
|
||||
type_name = memory_object.vol.type_name
|
||||
|
||||
if isinstance(memory_object, callbacks._SHUTDOWN_PACKET) or (
|
||||
type_name == symbol_table + constants.BANG + "_SHUTDOWN_PACKET"
|
||||
):
|
||||
callback_type = "IoRegisterShutdownNotification"
|
||||
|
||||
try:
|
||||
driver = memory_object.DeviceObject.DriverObject
|
||||
index = driverirp.MAJOR_FUNCTIONS.index("IRP_MJ_SHUTDOWN")
|
||||
callback_address = driver.MajorFunction[index]
|
||||
details = driver.DriverName.String or renderers.UnparsableValue()
|
||||
except exceptions.InvalidAddressException:
|
||||
callback_address = memory_object.vol.offset
|
||||
details = renderers.NotApplicableValue()
|
||||
|
||||
elif type_name == symbol_table + constants.BANG + "_NOTIFICATION_PACKET":
|
||||
callback_type = "IoRegisterFsRegistrationChange"
|
||||
callback_address = memory_object.NotificationRoutine
|
||||
details = renderers.NotApplicableValue()
|
||||
|
||||
elif type_name == symbol_table + constants.BANG + "_NOTIFY_ENTRY_HEADER":
|
||||
driver = (
|
||||
memory_object.DriverObject.dereference()
|
||||
if memory_object.DriverObject.is_readable()
|
||||
else None
|
||||
)
|
||||
|
||||
if driver:
|
||||
# Instantiate an object header for the driver name
|
||||
header = driver.get_object_header()
|
||||
try:
|
||||
if header.get_object_type(type_map) == "Driver":
|
||||
# Grab the object name
|
||||
details = header.NameInfo.Name.String
|
||||
else:
|
||||
details = renderers.NotApplicableValue()
|
||||
except exceptions.InvalidAddressException:
|
||||
details = renderers.UnreadableValue()
|
||||
else:
|
||||
details = renderers.UnreadableValue()
|
||||
|
||||
callback_type = (
|
||||
memory_object.EventCategory.description
|
||||
if memory_object.EventCategory.is_valid_choice
|
||||
else renderers.UnparsableValue()
|
||||
)
|
||||
|
||||
callback_address = memory_object.CallbackRoutine
|
||||
|
||||
elif type_name == symbol_table + constants.BANG + "_GENERIC_CALLBACK":
|
||||
callback_type = "GenericKernelCallback"
|
||||
callback_address = memory_object.Callback
|
||||
details = renderers.NotApplicableValue()
|
||||
|
||||
elif type_name == symbol_table + constants.BANG + "_DBGPRINT_CALLBACK":
|
||||
callback_type = "DbgSetDebugPrintCallback"
|
||||
callback_address = memory_object.Function
|
||||
details = renderers.NotApplicableValue()
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unexpected object type {type_name}")
|
||||
|
||||
return (
|
||||
callback_type,
|
||||
callback_address,
|
||||
(
|
||||
details
|
||||
if not isinstance(details, interfaces.renderers.BaseAbsentValue)
|
||||
else details
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def list_notify_routines(
|
||||
cls,
|
||||
@@ -115,11 +383,14 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
else:
|
||||
count = 8
|
||||
|
||||
fast_refs = ntkrnlmp.object(
|
||||
object_type="array",
|
||||
offset=symbol_offset,
|
||||
subtype=ntkrnlmp.get_type("_EX_FAST_REF"),
|
||||
count=count,
|
||||
fast_refs = cast(
|
||||
List[objects.Pointer],
|
||||
ntkrnlmp.object(
|
||||
object_type="array",
|
||||
offset=symbol_offset,
|
||||
subtype=ntkrnlmp.get_type("_EX_FAST_REF"),
|
||||
count=count,
|
||||
),
|
||||
)
|
||||
|
||||
for fast_ref in fast_refs:
|
||||
@@ -159,11 +430,14 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
if callback_count == 0:
|
||||
return None
|
||||
|
||||
fast_refs = ntkrnlmp.object(
|
||||
object_type="array",
|
||||
offset=symbol_offset,
|
||||
subtype=ntkrnlmp.get_type("_EX_FAST_REF"),
|
||||
count=callback_count,
|
||||
fast_refs = cast(
|
||||
List[objects.Pointer],
|
||||
ntkrnlmp.object(
|
||||
object_type="array",
|
||||
offset=symbol_offset,
|
||||
subtype=ntkrnlmp.get_type("_EX_FAST_REF"),
|
||||
count=callback_count,
|
||||
),
|
||||
)
|
||||
|
||||
for fast_ref in fast_refs:
|
||||
@@ -265,7 +539,13 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
callback_table_name: str,
|
||||
) -> Iterable[Tuple[str, int, str]]:
|
||||
) -> Iterable[
|
||||
Tuple[
|
||||
str,
|
||||
int,
|
||||
interfaces.renderers.BaseAbsentValue,
|
||||
]
|
||||
]:
|
||||
"""Lists all kernel bugcheck reason callbacks.
|
||||
|
||||
Args:
|
||||
@@ -323,7 +603,13 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
callback_table_name: str,
|
||||
) -> Iterable[Tuple[str, int, str]]:
|
||||
) -> Iterable[
|
||||
Tuple[
|
||||
str,
|
||||
int,
|
||||
Union[interfaces.objects.ObjectInterface, renderers.UnreadableValue],
|
||||
]
|
||||
]:
|
||||
"""Lists all kernel bugcheck callbacks.
|
||||
|
||||
Args:
|
||||
@@ -372,7 +658,7 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
callback_table_name = self.create_callback_table(
|
||||
callback_symbol_table = self.create_callback_symbol_table(
|
||||
self.context, kernel.symbol_table_name, self.config_path
|
||||
)
|
||||
|
||||
@@ -385,6 +671,7 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
self.list_bugcheck_callbacks,
|
||||
self.list_bugcheck_reason_callbacks,
|
||||
self.list_registry_callbacks,
|
||||
self.scan,
|
||||
)
|
||||
|
||||
for callback_method in callback_methods:
|
||||
@@ -392,7 +679,7 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
callback_table_name,
|
||||
callback_symbol_table,
|
||||
):
|
||||
if callback_detail is None:
|
||||
detail = renderers.NotApplicableValue()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import contextlib
|
||||
import datetime
|
||||
import logging
|
||||
import ntpath
|
||||
import re
|
||||
from typing import List, Optional, Type
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers
|
||||
@@ -13,7 +13,7 @@ from volatility3.framework.renderers import conversion, format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
from volatility3.plugins import timeliner
|
||||
from volatility3.plugins.windows import info, pslist, psscan
|
||||
from volatility3.plugins.windows import info, pslist, psscan, pedump
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -22,7 +22,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Lists the loaded modules in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
_version = (3, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -53,73 +53,33 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
description="Process offset in the physical address space",
|
||||
optional=True,
|
||||
),
|
||||
requirements.StringRequirement(
|
||||
name="name",
|
||||
description="Specify a regular expression to match dll name(s)",
|
||||
optional=True,
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="base",
|
||||
description="Specify a base virtual address in process memory",
|
||||
optional=True,
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="ignore-case",
|
||||
description="Specify case insensitivity for the regular expression name matching",
|
||||
default=False,
|
||||
optional=True,
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="dump",
|
||||
description="Extract listed DLLs",
|
||||
default=False,
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def dump_pe(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
pe_table_name: str,
|
||||
dll_entry: interfaces.objects.ObjectInterface,
|
||||
open_method: Type[interfaces.plugins.FileHandlerInterface],
|
||||
layer_name: str = None,
|
||||
prefix: str = "",
|
||||
) -> Optional[interfaces.plugins.FileHandlerInterface]:
|
||||
"""Extracts the complete data for a process as a FileInterface
|
||||
|
||||
Args:
|
||||
context: the context to operate upon
|
||||
pe_table_name: the name for the symbol table containing the PE format symbols
|
||||
dll_entry: the object representing the module
|
||||
layer_name: the layer that the DLL lives within
|
||||
open_method: class for constructing output files
|
||||
|
||||
Returns:
|
||||
An open FileHandlerInterface object containing the complete data for the DLL or None in the case of failure
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
name = dll_entry.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
name = "UnreadableDLLName"
|
||||
|
||||
if layer_name is None:
|
||||
layer_name = dll_entry.vol.layer_name
|
||||
|
||||
file_handle = open_method(
|
||||
"{}{}.{:#x}.{:#x}.dmp".format(
|
||||
prefix,
|
||||
ntpath.basename(name),
|
||||
dll_entry.vol.offset,
|
||||
dll_entry.DllBase,
|
||||
)
|
||||
)
|
||||
|
||||
dos_header = context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
offset=dll_entry.DllBase,
|
||||
layer_name=layer_name,
|
||||
)
|
||||
|
||||
for offset, data in dos_header.reconstruct():
|
||||
file_handle.seek(offset)
|
||||
file_handle.write(data)
|
||||
except (
|
||||
IOError,
|
||||
exceptions.VolatilityException,
|
||||
OverflowError,
|
||||
ValueError,
|
||||
) as excp:
|
||||
vollog.debug(f"Unable to dump dll at offset {dll_entry.DllBase}: {excp}")
|
||||
return None
|
||||
return file_handle
|
||||
|
||||
def _generator(self, procs):
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self.config_path, "windows", "pe", class_types=pe.class_types
|
||||
@@ -144,9 +104,35 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
BaseDllName = FullDllName = renderers.UnreadableValue()
|
||||
with contextlib.suppress(exceptions.InvalidAddressException):
|
||||
BaseDllName = entry.BaseDllName.get_string()
|
||||
# We assume that if the BaseDllName points to an invalid buffer, so will FullDllName
|
||||
# We assume that if BaseDllName points to invalid buffer, so will FullDllName
|
||||
FullDllName = entry.FullDllName.get_string()
|
||||
|
||||
# Check if a name regex was passed and apply it to only show matches
|
||||
if self.config["name"]:
|
||||
try:
|
||||
flags = re.I if self.config["ignore-case"] else 0
|
||||
mod_re = re.compile(self.config["name"], flags)
|
||||
except re.error:
|
||||
vollog.debug(
|
||||
"Error parsing regular expression: %s", self.config["name"]
|
||||
)
|
||||
return None
|
||||
|
||||
# If Base or Full Dll Name are invalid, move on
|
||||
if isinstance(BaseDllName, renderers.UnreadableValue) or isinstance(
|
||||
FullDllName, renderers.UnreadableValue
|
||||
):
|
||||
continue
|
||||
|
||||
# If regex does not match, move on
|
||||
if not mod_re.search(BaseDllName) and not mod_re.search(
|
||||
FullDllName
|
||||
):
|
||||
continue
|
||||
|
||||
if self.config["base"] and self.config["base"] != entry.DllBase:
|
||||
continue
|
||||
|
||||
if dll_load_time_field:
|
||||
# Versions prior to 6.1 won't have the LoadTime attribute
|
||||
# and 32bit version shouldn't have the Quadpart according to MSDN
|
||||
@@ -161,7 +147,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
|
||||
file_output = "Disabled"
|
||||
if self.config["dump"]:
|
||||
file_handle = self.dump_pe(
|
||||
file_output = pedump.PEDump.dump_ldr_entry(
|
||||
self.context,
|
||||
pe_table_name,
|
||||
entry,
|
||||
@@ -169,10 +155,10 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
proc_layer_name,
|
||||
prefix=f"pid.{proc_id}.",
|
||||
)
|
||||
file_output = "Error outputting file"
|
||||
if file_handle:
|
||||
file_handle.close()
|
||||
file_output = file_handle.preferred_filename
|
||||
|
||||
if not file_output:
|
||||
file_output = "Error outputting file"
|
||||
|
||||
try:
|
||||
dllbase = format_hints.Hex(entry.DllBase)
|
||||
except exceptions.InvalidAddressException:
|
||||
|
||||
@@ -44,6 +44,7 @@ class DriverIrp(interfaces.plugins.PluginInterface):
|
||||
"""List IRPs for drivers in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
|
||||
@@ -244,6 +244,8 @@ class DumpFiles(interfaces.plugins.PluginInterface):
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
)
|
||||
|
||||
dumped_files = set()
|
||||
|
||||
for proc in procs:
|
||||
try:
|
||||
object_table = proc.ObjectTable
|
||||
@@ -267,6 +269,10 @@ class DumpFiles(interfaces.plugins.PluginInterface):
|
||||
if not file_re.search(name):
|
||||
continue
|
||||
|
||||
if file_obj.vol.offset in dumped_files:
|
||||
continue
|
||||
dumped_files.add(file_obj.vol.offset)
|
||||
|
||||
for result in self.process_file_object(
|
||||
self.context, kernel.layer_name, self.open, file_obj
|
||||
):
|
||||
@@ -303,6 +309,10 @@ class DumpFiles(interfaces.plugins.PluginInterface):
|
||||
if not file_re.search(name):
|
||||
continue
|
||||
|
||||
if file_obj.vol.offset in dumped_files:
|
||||
continue
|
||||
dumped_files.add(file_obj.vol.offset)
|
||||
|
||||
for result in self.process_file_object(
|
||||
self.context, kernel.layer_name, self.open, file_obj
|
||||
):
|
||||
|
||||
@@ -14,6 +14,7 @@ class FileScan(interfaces.plugins.PluginInterface):
|
||||
"""Scans for file objects present in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 1)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
@@ -67,10 +68,10 @@ class FileScan(interfaces.plugins.PluginInterface):
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
yield (0, (format_hints.Hex(fileobj.vol.offset), file_name, fileobj.Size))
|
||||
yield (0, (format_hints.Hex(fileobj.vol.offset), file_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[("Offset", format_hints.Hex), ("Name", str), ("Size", int)],
|
||||
[("Offset", format_hints.Hex), ("Name", str)],
|
||||
self._generator(),
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
"""Lists process open handles."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (1, 0, 2)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -142,9 +142,13 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
pointers in the _HANDLE_TABLE_ENTRY which allows us to find the
|
||||
associated _OBJECT_HEADER.
|
||||
"""
|
||||
DEFAULT_SAR_VALUE = 0x10 # to be used only when decoding fails
|
||||
|
||||
if self._sar_value is None:
|
||||
if not has_capstone:
|
||||
vollog.debug(
|
||||
"capstone module is missing, unable to create disassembly of ObpCaptureHandleInformationEx"
|
||||
)
|
||||
return None
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
@@ -159,25 +163,48 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
try:
|
||||
func_addr = ntkrnlmp.get_symbol("ObpCaptureHandleInformationEx").address
|
||||
except exceptions.SymbolError:
|
||||
vollog.debug("Unable to locate ObpCaptureHandleInformationEx symbol")
|
||||
return None
|
||||
|
||||
data = self.context.layers.read(virtual_layer_name, kvo + func_addr, 0x200)
|
||||
if data is None:
|
||||
return None
|
||||
try:
|
||||
func_addr_to_read = kvo + func_addr
|
||||
num_bytes_to_read = 0x200
|
||||
vollog.debug(
|
||||
f"ObpCaptureHandleInformationEx symbol located at {hex(func_addr_to_read)}"
|
||||
)
|
||||
data = self.context.layers.read(
|
||||
virtual_layer_name, func_addr_to_read, num_bytes_to_read
|
||||
)
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.warning(
|
||||
f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}. Unable to decode SAR value. Failing back to a common value of {hex(DEFAULT_SAR_VALUE)}"
|
||||
)
|
||||
self._sar_value = DEFAULT_SAR_VALUE
|
||||
return self._sar_value
|
||||
|
||||
md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
|
||||
|
||||
instruction_count = 0
|
||||
for address, size, mnemonic, op_str in md.disasm_lite(
|
||||
data, kvo + func_addr
|
||||
):
|
||||
# print("{} {} {} {}".format(address, size, mnemonic, op_str))
|
||||
|
||||
instruction_count += 1
|
||||
if mnemonic.startswith("sar"):
|
||||
# if we don't want to parse op strings, we can disasm the
|
||||
# single sar instruction again, but we use disasm_lite for speed
|
||||
self._sar_value = int(op_str.split(",")[1].strip(), 16)
|
||||
vollog.debug(
|
||||
f"SAR located at {hex(address)} with value of {hex(self._sar_value)}"
|
||||
)
|
||||
break
|
||||
|
||||
if self._sar_value is None:
|
||||
vollog.warning(
|
||||
f"Failed to to locate SAR value having parsed {instruction_count} instructions, failing back to a common value of {hex(DEFAULT_SAR_VALUE)}"
|
||||
)
|
||||
self._sar_value = DEFAULT_SAR_VALUE
|
||||
|
||||
return self._sar_value
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
# This file is Copyright 2024 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 typing import NamedTuple, Dict, Generator
|
||||
|
||||
from volatility3.framework import interfaces, exceptions, constants
|
||||
from volatility3.framework import renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.plugins.windows import pslist, vadinfo
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
VadData = NamedTuple(
|
||||
"VadData",
|
||||
[
|
||||
("protection", str),
|
||||
("path", str),
|
||||
],
|
||||
)
|
||||
|
||||
DLLData = NamedTuple(
|
||||
"DLLData",
|
||||
[
|
||||
("path", str),
|
||||
],
|
||||
)
|
||||
|
||||
### Useful references on process hollowing
|
||||
# https://cysinfo.com/detecting-deceptive-hollowing-techniques/
|
||||
# https://github.com/m0n0ph1/Process-Hollowing
|
||||
|
||||
|
||||
class HollowProcesses(interfaces.plugins.PluginInterface):
|
||||
"""Lists hollowed processes"""
|
||||
|
||||
_required_framework_version = (2, 4, 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="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
element_type=int,
|
||||
description="Process IDs to include (all other processes are excluded)",
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _get_vads_data(
|
||||
self, proc: interfaces.objects.ObjectInterface
|
||||
) -> Dict[int, VadData]:
|
||||
"""
|
||||
Returns a dictionary of:
|
||||
base address -> (protection string, file name)
|
||||
For each mapped VAD in the process. This is used
|
||||
for quick lookups of data and matching the DLL
|
||||
at the same base address as the VAD
|
||||
"""
|
||||
vads = {}
|
||||
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for vad in proc.get_vad_root().traverse():
|
||||
protection_string = vad.get_protection(
|
||||
vadinfo.VadInfo.protect_values(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
),
|
||||
vadinfo.winnt_protections,
|
||||
)
|
||||
|
||||
fn = vad.get_file_name()
|
||||
if not fn or not isinstance(fn, str):
|
||||
fn = "<Non-File Backed Region>"
|
||||
|
||||
vads[vad.get_start()] = VadData(protection_string, fn)
|
||||
|
||||
return vads
|
||||
|
||||
def _get_dlls_map(
|
||||
self, proc: interfaces.objects.ObjectInterface
|
||||
) -> Dict[int, DLLData]:
|
||||
"""
|
||||
Returns a dictionary of:
|
||||
base address -> path
|
||||
for each DLL loaded in the process
|
||||
|
||||
This is used to cross compare with
|
||||
the corresponding VAD and to have a
|
||||
backup path source in case of smear
|
||||
in the VAD
|
||||
"""
|
||||
dlls = {}
|
||||
|
||||
for entry in proc.load_order_modules():
|
||||
try:
|
||||
base = entry.DllBase
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
try:
|
||||
FullDllName = entry.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
FullDllName = renderers.UnreadableValue()
|
||||
|
||||
dlls[base] = DLLData(FullDllName)
|
||||
|
||||
return dlls
|
||||
|
||||
def _get_image_base(self, proc: interfaces.objects.ObjectInterface) -> int:
|
||||
"""
|
||||
Uses the PEB to get the image base of the process
|
||||
"""
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
try:
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
peb = self.context.object(
|
||||
kernel.symbol_table_name + constants.BANG + "_PEB",
|
||||
layer_name=proc_layer_name,
|
||||
offset=proc.Peb,
|
||||
)
|
||||
return peb.ImageBaseAddress
|
||||
except exceptions.InvalidAddressException:
|
||||
return None
|
||||
|
||||
def _check_load_address(self, proc, _, __) -> Generator[str, None, None]:
|
||||
"""
|
||||
Detects when the image base in the PEB, which is writable by process malware,
|
||||
does not match the section base address - whose value lives in kernel memory.
|
||||
Many malware samples will manipulate their image base to fool AVs/EDRs and
|
||||
as a necessary part of certain hollowing techniques
|
||||
"""
|
||||
image_base = self._get_image_base(proc)
|
||||
if image_base is not None and image_base != proc.SectionBaseAddress:
|
||||
yield "The ImageBaseAddress reported from the PEB ({:#x}) does not match the process SectionBaseAddress ({:#x})".format(
|
||||
image_base, proc.SectionBaseAddress
|
||||
)
|
||||
|
||||
def _check_exe_protection(
|
||||
self, proc, vads: Dict[int, VadData], __
|
||||
) -> Generator[str, None, None]:
|
||||
"""
|
||||
Legitimately mapped application executables and DLLs
|
||||
will have a VAD present and its initial protection will be
|
||||
PAGE_EXECUTE_WRITECOPY.
|
||||
Many process hollowing and code injection techniques will
|
||||
unmap the real executable and/or map in executables with
|
||||
incorrect permissions.
|
||||
This check verifies the VAD for the application exe.
|
||||
`_check_dlls_protection` checks for DLLs mapped in the process.
|
||||
"""
|
||||
base = proc.SectionBaseAddress
|
||||
|
||||
if base not in vads:
|
||||
yield "There is no VAD starting at the base address of the process executable ({:#x})".format(
|
||||
base
|
||||
)
|
||||
elif vads[base].protection != "PAGE_EXECUTE_WRITECOPY":
|
||||
yield "Unexpected protection ({}) for VAD hosting the process executable ({:#x}) with path {}".format(
|
||||
vads[base].protection, base, vads[base].path
|
||||
)
|
||||
|
||||
def _check_dlls_protection(
|
||||
self, _, vads: Dict[int, VadData], dlls: Dict[int, DLLData]
|
||||
) -> Generator[str, None, None]:
|
||||
for dll_base in dlls:
|
||||
# could be malicious but triggers too many FPs from smear
|
||||
if dll_base not in vads:
|
||||
continue
|
||||
|
||||
# PAGE_EXECUTE_WRITECOPY is the only valid permission for mapped DLLs and .exe files
|
||||
if vads[dll_base].protection != "PAGE_EXECUTE_WRITECOPY":
|
||||
yield "Unexpected protection ({}) for DLL in the PEB's load order list ({:#x}) with path {}".format(
|
||||
vads[dll_base].protection, dll_base, dlls[dll_base].path
|
||||
)
|
||||
|
||||
def _generator(self, procs):
|
||||
checks = [
|
||||
self._check_load_address,
|
||||
self._check_exe_protection,
|
||||
self._check_dlls_protection,
|
||||
]
|
||||
|
||||
for proc in procs:
|
||||
# smear and/or terminated process
|
||||
dlls = self._get_dlls_map(proc)
|
||||
if len(dlls) < 3:
|
||||
continue
|
||||
|
||||
vads = self._get_vads_data(proc)
|
||||
if len(vads) < 5:
|
||||
continue
|
||||
|
||||
proc_name = utility.array_to_string(proc.ImageFileName)
|
||||
pid = proc.UniqueProcessId
|
||||
|
||||
for check in checks:
|
||||
for note in check(proc, vads, dlls):
|
||||
yield 0, (
|
||||
pid,
|
||||
proc_name,
|
||||
note,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
("Process", str),
|
||||
("Notes", str),
|
||||
],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -10,7 +10,7 @@ from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.renderers import TreeGrid
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows import extensions
|
||||
from volatility3.framework.symbols.windows.extensions import kdbg, pe
|
||||
|
||||
|
||||
class Info(plugins.PluginInterface):
|
||||
@@ -94,16 +94,16 @@ class Info(plugins.PluginInterface):
|
||||
"windows",
|
||||
"kdbg",
|
||||
native_types=native_types,
|
||||
class_types=extensions.kdbg.class_types,
|
||||
class_types=kdbg.class_types,
|
||||
)
|
||||
|
||||
kdbg = context.object(
|
||||
kdbg_obj = context.object(
|
||||
kdbg_table_name + constants.BANG + "_KDDEBUGGER_DATA64",
|
||||
offset=ntkrnlmp.offset + kdbg_offset,
|
||||
layer_name=layer_name,
|
||||
)
|
||||
|
||||
return kdbg
|
||||
return kdbg_obj
|
||||
|
||||
@classmethod
|
||||
def get_kuser_structure(
|
||||
@@ -173,7 +173,7 @@ class Info(plugins.PluginInterface):
|
||||
interfaces.configuration.path_join(config_path, "pe"),
|
||||
"windows",
|
||||
"pe",
|
||||
class_types=extensions.pe.class_types,
|
||||
class_types=pe.class_types,
|
||||
)
|
||||
|
||||
dos_header = context.object(
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# This file is Copyright 2024 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 typing import Iterator, List, Tuple
|
||||
|
||||
from volatility3.framework import (
|
||||
renderers,
|
||||
interfaces,
|
||||
constants,
|
||||
)
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KPCRs(interfaces.plugins.PluginInterface):
|
||||
"""Print KPCR structure for each processor"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_kpcrs(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
) -> interfaces.objects.ObjectInterface:
|
||||
"""Returns the KPCR structure for each processor
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
kernel_module_name: The name of the kernel module on which to operate
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
|
||||
Returns:
|
||||
The _KPCR structure for each processor
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
cpu_count_offset = kernel.get_symbol("KeNumberProcessors").address
|
||||
cpu_count = kernel.object(
|
||||
object_type="unsigned int", layer_name=layer_name, offset=cpu_count_offset
|
||||
)
|
||||
processor_block = kernel.object(
|
||||
object_type="pointer",
|
||||
layer_name=layer_name,
|
||||
offset=kernel.get_symbol("KiProcessorBlock").address,
|
||||
)
|
||||
processor_pointers = utility.array_of_pointers(
|
||||
context=context,
|
||||
array=processor_block,
|
||||
count=cpu_count,
|
||||
subtype=symbol_table + constants.BANG + "_KPRCB",
|
||||
)
|
||||
for pointer in processor_pointers:
|
||||
kprcb = pointer.dereference()
|
||||
reloff = kernel.get_type("_KPCR").relative_child_offset("Prcb")
|
||||
kpcr = context.object(
|
||||
symbol_table + constants.BANG + "_KPCR",
|
||||
offset=kprcb.vol.offset - reloff,
|
||||
layer_name=layer_name,
|
||||
)
|
||||
yield kpcr
|
||||
|
||||
def _generator(self) -> Iterator[Tuple]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
layer_name = kernel.layer_name
|
||||
symbol_table = kernel.symbol_table_name
|
||||
|
||||
for kpcr in self.list_kpcrs(
|
||||
self.context, self.config["kernel"], layer_name, symbol_table
|
||||
):
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(kpcr.vol.offset),
|
||||
format_hints.Hex(kpcr.CurrentPrcb),
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
("PRCB Offset", format_hints.Hex),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -1,3 +1,9 @@
|
||||
# This file is Copyright 2024 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 constants, exceptions, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
@@ -5,12 +11,14 @@ from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
from volatility3.plugins.windows import pslist, vadinfo
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LdrModules(interfaces.plugins.PluginInterface):
|
||||
"""Lists the loaded modules in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (1, 0, 1)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
@@ -39,14 +47,6 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
self.context, self.config_path, "windows", "pe", class_types=pe.class_types
|
||||
)
|
||||
|
||||
def filter_function(x: interfaces.objects.ObjectInterface) -> bool:
|
||||
try:
|
||||
return not (x.get_private_memory() == 0 and x.ControlArea)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
filter_func = filter_function
|
||||
|
||||
for proc in procs:
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
|
||||
@@ -61,7 +61,7 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
|
||||
# Build dictionary of mapped files, where the VAD start address is the key and value is the file name of the mapped file
|
||||
mapped_files = {}
|
||||
for vad in vadinfo.VadInfo.list_vads(proc, filter_func=filter_func):
|
||||
for vad in vadinfo.VadInfo.list_vads(proc):
|
||||
dos_header = self.context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
offset=vad.get_start(),
|
||||
@@ -71,7 +71,11 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
# Filter out VADs that do not start with a MZ header
|
||||
if dos_header.e_magic != 0x5A4D:
|
||||
continue
|
||||
except exceptions.PagedInvalidAddressException:
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVVV,
|
||||
f"Skipping vad at {hex(dos_header.vol.offset)} due to InvalidAddressException",
|
||||
)
|
||||
continue
|
||||
|
||||
mapped_files[vad.get_start()] = vad.get_file_name()
|
||||
|
||||
@@ -155,12 +155,12 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
|
||||
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
|
||||
):
|
||||
notes = renderers.NotApplicableValue()
|
||||
# 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]]
|
||||
|
||||
@@ -264,9 +264,10 @@ class ADS(interfaces.plugins.PluginInterface):
|
||||
disasm = interfaces.renderers.Disassembly(
|
||||
content, 0, architecture.lower()
|
||||
)
|
||||
content = format_hints.HexBytes(content)
|
||||
else:
|
||||
content = renderers.NotAvailableValue
|
||||
disasm = interfaces.renderers.BaseAbsentValue
|
||||
content = renderers.NotAvailableValue()
|
||||
disasm = interfaces.renderers.BaseAbsentValue()
|
||||
|
||||
yield 0, (
|
||||
format_hints.Hex(attr_data.vol.offset),
|
||||
@@ -275,7 +276,7 @@ class ADS(interfaces.plugins.PluginInterface):
|
||||
attr.Attr_Header.AttrType.lookup(),
|
||||
file_name,
|
||||
ads_name,
|
||||
format_hints.HexBytes(content),
|
||||
content,
|
||||
disasm,
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -2,23 +2,24 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import logging
|
||||
from typing import Iterable, List, Generator
|
||||
from typing import Iterable
|
||||
|
||||
from volatility3.framework import renderers, interfaces, exceptions, constants
|
||||
from volatility3.framework import interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
from volatility3.plugins.windows import poolscanner, dlllist, pslist
|
||||
from volatility3.plugins.windows import poolscanner, modules, pedump
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ModScan(interfaces.plugins.PluginInterface):
|
||||
class ModScan(modules.Modules):
|
||||
"""Scans for modules present in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enumeration_method = self.scan_modules
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
@@ -32,10 +33,7 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
name="poolscanner", component=poolscanner.PoolScanner, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="dlllist", component=dlllist.DllList, version=(2, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="dump",
|
||||
@@ -43,6 +41,20 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
default=False,
|
||||
optional=True,
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="base",
|
||||
description="Extract a single module with BASE address",
|
||||
optional=True,
|
||||
),
|
||||
requirements.StringRequirement(
|
||||
name="name",
|
||||
description="module name/sub string",
|
||||
optional=True,
|
||||
default=None,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@@ -72,157 +84,3 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
):
|
||||
_constraint, mem_object, _header = result
|
||||
yield mem_object
|
||||
|
||||
@classmethod
|
||||
def get_session_layers(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
pids: List[int] = None,
|
||||
) -> Generator[str, None, None]:
|
||||
"""Build a cache of possible virtual layers, in priority starting with
|
||||
the primary/kernel layer. Then keep one layer per session by cycling
|
||||
through the process list.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
pids: A list of process identifiers to include exclusively or None for no filter
|
||||
|
||||
Returns:
|
||||
A list of session layer names
|
||||
"""
|
||||
seen_ids: List[interfaces.objects.ObjectInterface] = []
|
||||
filter_func = pslist.PsList.create_pid_filter(pids or [])
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
proc_id = "Unknown"
|
||||
try:
|
||||
proc_id = proc.UniqueProcessId
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
|
||||
# create the session space object in the process' own layer.
|
||||
# not all processes have a valid session pointer.
|
||||
session_space = context.object(
|
||||
symbol_table + constants.BANG + "_MM_SESSION_SPACE",
|
||||
layer_name=layer_name,
|
||||
offset=proc.Session,
|
||||
)
|
||||
|
||||
if session_space.SessionId in seen_ids:
|
||||
continue
|
||||
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVV,
|
||||
"Process {} does not have a valid Session or a layer could not be constructed for it".format(
|
||||
proc_id
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
||||
# save the layer if we haven't seen the session yet
|
||||
seen_ids.append(session_space.SessionId)
|
||||
yield proc_layer_name
|
||||
|
||||
@classmethod
|
||||
def find_session_layer(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
session_layers: Iterable[str],
|
||||
base_address: int,
|
||||
):
|
||||
"""Given a base address and a list of layer names, find a layer that
|
||||
can access the specified address.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
session_layers: A list of session layer names
|
||||
base_address: The base address to identify the layers that can access it
|
||||
|
||||
Returns:
|
||||
Layer name or None if no layers that contain the base address can be found
|
||||
"""
|
||||
|
||||
for layer_name in session_layers:
|
||||
if context.layers[layer_name].is_valid(base_address):
|
||||
return layer_name
|
||||
|
||||
return None
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
session_layers = list(
|
||||
self.get_session_layers(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
)
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self.config_path, "windows", "pe", class_types=pe.class_types
|
||||
)
|
||||
|
||||
for mod in self.scan_modules(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
):
|
||||
try:
|
||||
BaseDllName = mod.BaseDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
BaseDllName = ""
|
||||
|
||||
try:
|
||||
FullDllName = mod.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
FullDllName = ""
|
||||
|
||||
file_output = "Disabled"
|
||||
if self.config["dump"]:
|
||||
session_layer_name = self.find_session_layer(
|
||||
self.context, session_layers, mod.DllBase
|
||||
)
|
||||
file_output = f"Cannot find a viable session layer for {mod.DllBase:#x}"
|
||||
if session_layer_name:
|
||||
file_handle = dlllist.DllList.dump_pe(
|
||||
self.context,
|
||||
pe_table_name,
|
||||
mod,
|
||||
self.open,
|
||||
layer_name=session_layer_name,
|
||||
)
|
||||
file_output = "Error outputting file"
|
||||
if file_handle:
|
||||
file_output = file_handle.preferred_filename
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(mod.vol.offset),
|
||||
format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage),
|
||||
BaseDllName,
|
||||
FullDllName,
|
||||
file_output,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
("Base", format_hints.Hex),
|
||||
("Size", format_hints.Hex),
|
||||
("Name", str),
|
||||
("Path", str),
|
||||
("File output", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
import logging
|
||||
from typing import List, Iterable, Generator
|
||||
|
||||
from volatility3.framework import constants
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers
|
||||
from volatility3.framework import exceptions, interfaces, constants, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
from volatility3.plugins.windows import pslist, dlllist
|
||||
from volatility3.plugins.windows import pslist, pedump
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,7 +18,11 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
"""Lists the loaded kernel modules."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 1, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enumeration_method = self.list_modules
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -33,65 +35,97 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="dlllist", component=dlllist.DllList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="dump",
|
||||
description="Extract listed modules",
|
||||
default=False,
|
||||
optional=True,
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="base",
|
||||
description="Extract a single module with BASE address",
|
||||
optional=True,
|
||||
),
|
||||
requirements.StringRequirement(
|
||||
name="name",
|
||||
description="module name/sub string",
|
||||
optional=True,
|
||||
default=None,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def dump_module(self, session_layers, pe_table_name, mod):
|
||||
session_layer_name = self.find_session_layer(
|
||||
self.context, session_layers, mod.DllBase
|
||||
)
|
||||
file_output = f"Cannot find a viable session layer for {mod.DllBase:#x}"
|
||||
if session_layer_name:
|
||||
file_output = pedump.PEDump.dump_ldr_entry(
|
||||
self.context,
|
||||
pe_table_name,
|
||||
mod,
|
||||
self.open,
|
||||
layer_name=session_layer_name,
|
||||
)
|
||||
if not file_output:
|
||||
file_output = "Error outputting file"
|
||||
|
||||
return file_output
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self.config_path, "windows", "pe", class_types=pe.class_types
|
||||
)
|
||||
|
||||
for mod in self.list_modules(
|
||||
pe_table_name = None
|
||||
session_layers = None
|
||||
|
||||
if self.config["dump"]:
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context,
|
||||
self.config_path,
|
||||
"windows",
|
||||
"pe",
|
||||
class_types=pe.class_types,
|
||||
)
|
||||
|
||||
session_layers = list(
|
||||
self.get_session_layers(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
)
|
||||
|
||||
for mod in self._enumeration_method(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
):
|
||||
if self.config["base"] and self.config["base"] != mod.DllBase:
|
||||
continue
|
||||
|
||||
try:
|
||||
BaseDllName = mod.BaseDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
BaseDllName = ""
|
||||
|
||||
try:
|
||||
FullDllName = mod.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
FullDllName = ""
|
||||
BaseDllName = interfaces.renderers.BaseAbsentValue()
|
||||
|
||||
if self.config["name"] and self.config["name"] not in BaseDllName:
|
||||
continue
|
||||
|
||||
try:
|
||||
FullDllName = mod.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
FullDllName = interfaces.renderers.BaseAbsentValue()
|
||||
|
||||
file_output = "Disabled"
|
||||
if self.config["dump"]:
|
||||
file_handle = dlllist.DllList.dump_pe(
|
||||
self.context, pe_table_name, mod, self.open
|
||||
)
|
||||
file_output = "Error outputting file"
|
||||
if file_handle:
|
||||
file_handle.close()
|
||||
file_output = file_handle.preferred_filename
|
||||
file_output = self.dump_module(session_layers, pe_table_name, mod)
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(mod.vol.offset),
|
||||
format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage),
|
||||
BaseDllName,
|
||||
FullDllName,
|
||||
file_output,
|
||||
),
|
||||
yield 0, (
|
||||
format_hints.Hex(mod.vol.offset),
|
||||
format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage),
|
||||
BaseDllName,
|
||||
FullDllName,
|
||||
file_output,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -218,6 +218,7 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
(10, 0, 18362, 0): "netscan-win10-18362-x64",
|
||||
(10, 0, 18363, 0): "netscan-win10-18363-x64",
|
||||
(10, 0, 19041, 0): "netscan-win10-19041-x64",
|
||||
(10, 0, 20348, 0): "netscan-win10-20348-x64",
|
||||
}
|
||||
|
||||
# we do not need to check for tcpip's specific FileVersion in every case
|
||||
|
||||
@@ -35,7 +35,7 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
name="netscan", component=netscan.NetScan, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(1, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pdbutil", component=pdbutil.PDBUtility, version=(1, 0, 0)
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
# This file is Copyright 2024 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
|
||||
import ntpath
|
||||
from typing import List, Type, Optional
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
from volatility3.plugins.windows import pslist, modules
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PEDump(interfaces.plugins.PluginInterface):
|
||||
"""Allows extracting PE Files from a specific address in a specific address space"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
element_type=int,
|
||||
description="Process IDs to include (all other processes are excluded)",
|
||||
optional=True,
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="base",
|
||||
description="Base address to reconstruct a PE file",
|
||||
optional=False,
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="kernel_module",
|
||||
description="Extract from kernel address space.",
|
||||
default=False,
|
||||
optional=True,
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def dump_pe(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
pe_table_name: str,
|
||||
layer_name: str,
|
||||
open_method: Type[interfaces.plugins.FileHandlerInterface],
|
||||
file_name: str,
|
||||
base: int,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Returns the filename of the dump file or None
|
||||
"""
|
||||
try:
|
||||
file_handle = open_method(file_name)
|
||||
|
||||
dos_header = context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
offset=base,
|
||||
layer_name=layer_name,
|
||||
)
|
||||
|
||||
for offset, data in dos_header.reconstruct():
|
||||
file_handle.seek(offset)
|
||||
file_handle.write(data)
|
||||
except (
|
||||
IOError,
|
||||
exceptions.VolatilityException,
|
||||
OverflowError,
|
||||
ValueError,
|
||||
) as excp:
|
||||
vollog.debug(f"Unable to dump PE file at offset {base}: {excp}")
|
||||
return None
|
||||
finally:
|
||||
file_handle.close()
|
||||
|
||||
return file_handle.preferred_filename
|
||||
|
||||
@classmethod
|
||||
def dump_ldr_entry(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
pe_table_name: str,
|
||||
ldr_entry: interfaces.objects.ObjectInterface,
|
||||
open_method: Type[interfaces.plugins.FileHandlerInterface],
|
||||
layer_name: str = None,
|
||||
prefix: str = "",
|
||||
) -> Optional[str]:
|
||||
"""Extracts the PE file referenced an LDR_DATA_TABLE_ENTRY (DLL, kernel module) instance
|
||||
|
||||
Args:
|
||||
context: the context to operate upon
|
||||
pe_table_name: the name for the symbol table containing the PE format symbols
|
||||
ldr_entry: the object representing the module
|
||||
open_method: class for constructing output files
|
||||
layer_name: the layer that the DLL lives within
|
||||
prefix: optional string to prepend to filename
|
||||
Returns:
|
||||
The output file name or None in the case of failure
|
||||
"""
|
||||
try:
|
||||
name = ldr_entry.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
name = "UnreadableDLLName"
|
||||
|
||||
if layer_name is None:
|
||||
layer_name = ldr_entry.vol.layer_name
|
||||
|
||||
file_name = "{}{}.{:#x}.{:#x}.dmp".format(
|
||||
prefix,
|
||||
ntpath.basename(name),
|
||||
ldr_entry.vol.offset,
|
||||
ldr_entry.DllBase,
|
||||
)
|
||||
|
||||
return cls.dump_pe(
|
||||
context,
|
||||
pe_table_name,
|
||||
layer_name,
|
||||
open_method,
|
||||
file_name,
|
||||
ldr_entry.DllBase,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def dump_pe_at_base(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
pe_table_name: str,
|
||||
layer_name: str,
|
||||
open_method: Type[interfaces.plugins.FileHandlerInterface],
|
||||
proc_offset: int,
|
||||
pid: int,
|
||||
base: int,
|
||||
) -> Optional[str]:
|
||||
file_name = "PE.{:#x}.{:d}.{:#x}.dmp".format(
|
||||
proc_offset,
|
||||
pid,
|
||||
base,
|
||||
)
|
||||
|
||||
return PEDump.dump_pe(
|
||||
context, pe_table_name, layer_name, open_method, file_name, base
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def dump_kernel_pe_at_base(cls, context, kernel, pe_table_name, open_method, base):
|
||||
session_layers = modules.Modules.get_session_layers(
|
||||
context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
|
||||
session_layer_name = modules.Modules.find_session_layer(
|
||||
context, session_layers, base
|
||||
)
|
||||
|
||||
if session_layer_name:
|
||||
system_pid = 4
|
||||
|
||||
file_output = PEDump.dump_pe_at_base(
|
||||
context,
|
||||
pe_table_name,
|
||||
session_layer_name,
|
||||
open_method,
|
||||
0,
|
||||
system_pid,
|
||||
base,
|
||||
)
|
||||
|
||||
if file_output:
|
||||
yield system_pid, "Kernel", file_output
|
||||
else:
|
||||
vollog.warning(
|
||||
"Unable to find a session layer with the provided base address mapped in the kernel."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def dump_processes(
|
||||
cls, context, kernel, pe_table_name, open_method, filter_func, base
|
||||
):
|
||||
""" """
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
pid = proc.UniqueProcessId
|
||||
proc_name = proc.ImageFileName.cast(
|
||||
"string",
|
||||
max_length=proc.ImageFileName.vol.count,
|
||||
errors="replace",
|
||||
)
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
|
||||
file_output = PEDump.dump_pe_at_base(
|
||||
context,
|
||||
pe_table_name,
|
||||
proc_layer_name,
|
||||
open_method,
|
||||
proc.vol.offset,
|
||||
pid,
|
||||
base,
|
||||
)
|
||||
|
||||
if file_output:
|
||||
yield pid, proc_name, file_output
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self.config_path, "windows", "pe", class_types=pe.class_types
|
||||
)
|
||||
|
||||
if self.config["kernel_module"] and self.config["pid"]:
|
||||
vollog.error("Only --kernel_module or --pid should be set. Not both")
|
||||
return
|
||||
|
||||
if not self.config["kernel_module"] and not self.config["pid"]:
|
||||
vollog.error("--kernel_module or --pid must be set")
|
||||
return
|
||||
|
||||
if self.config["kernel_module"]:
|
||||
pe_files = self.dump_kernel_pe_at_base(
|
||||
self.context, kernel, pe_table_name, self.open, self.config["base"]
|
||||
)
|
||||
else:
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
pe_files = self.dump_processes(
|
||||
self.context,
|
||||
kernel,
|
||||
pe_table_name,
|
||||
self.open,
|
||||
filter_func,
|
||||
self.config["base"],
|
||||
)
|
||||
|
||||
for pid, proc_name, file_output in pe_files:
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
pid,
|
||||
proc_name,
|
||||
file_output,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
("Process", str),
|
||||
("File output", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
# This file is Copyright 2024 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
|
||||
import contextlib
|
||||
|
||||
from volatility3.framework import interfaces, exceptions
|
||||
from volatility3.framework import renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.windows import pslist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
"""Lists processes whose DeletePending bit is set or whose FILE_OBJECT is set to 0"""
|
||||
|
||||
_required_framework_version = (2, 4, 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="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self, procs):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
if not kernel.get_type("_EPROCESS").has_member("ImageFilePointer"):
|
||||
vollog.warning(
|
||||
"This plugin only supports Windows 10 builds when the ImageFilePointer member of _EPROCESS is present"
|
||||
)
|
||||
return
|
||||
|
||||
for proc in procs:
|
||||
delete_pending = renderers.UnreadableValue()
|
||||
process_name = utility.array_to_string(proc.ImageFileName)
|
||||
|
||||
# if it is 0 then its a side effect of process ghosting
|
||||
if proc.ImageFilePointer.vol.offset != 0:
|
||||
try:
|
||||
file_object = proc.ImageFilePointer
|
||||
delete_pending = file_object.DeletePending
|
||||
except exceptions.InvalidAddressException:
|
||||
file_object = 0
|
||||
|
||||
# ImageFilePointer equal to 0 means process ghosting or similar techniques were used
|
||||
else:
|
||||
file_object = 0
|
||||
|
||||
if isinstance(delete_pending, int) and delete_pending not in [0, 1]:
|
||||
vollog.debug(
|
||||
f"Invalid delete_pending value {delete_pending} found for {process_name} {proc.UniqueProcessId}"
|
||||
)
|
||||
|
||||
# delete_pending besides 0 or 1 = smear
|
||||
if file_object == 0 or delete_pending == 1:
|
||||
path = renderers.UnreadableValue()
|
||||
if file_object:
|
||||
with contextlib.suppress(exceptions.InvalidAddressException):
|
||||
path = file_object.FileName.String
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
proc.UniqueProcessId,
|
||||
process_name,
|
||||
format_hints.Hex(file_object),
|
||||
delete_pending,
|
||||
path,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_active_process_filter()
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
("Process", str),
|
||||
("FILE_OBJECT", format_hints.Hex),
|
||||
("DeletePending", str),
|
||||
("Path", str),
|
||||
],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -136,6 +136,29 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
filter_func = lambda x: x.UniqueProcessId not in filter_list
|
||||
return filter_func
|
||||
|
||||
@classmethod
|
||||
def create_active_process_filter(
|
||||
cls,
|
||||
) -> Callable[[interfaces.objects.ObjectInterface], bool]:
|
||||
"""A factory for producing a filter function that only returns
|
||||
active, userland processes. This prevents plugins from operating on terminated
|
||||
processes that are still in the process list due to smear or handle leaks as well
|
||||
as kernel processes (System, Registry, etc.). Use of this filter for plugins searching
|
||||
for system state anomalies significantly reduces false positive in smeared and terminated
|
||||
processes.
|
||||
Returns:
|
||||
Filter function for passing to the `list_processes` method
|
||||
"""
|
||||
|
||||
return lambda x: not (
|
||||
x.is_valid()
|
||||
and x.ActiveThreads > 0
|
||||
and x.UniqueProcessId != 4
|
||||
and x.InheritedFromUniqueProcessId != 4
|
||||
and x.ExitTime.QuadPart == 0
|
||||
and x.get_handle_count() != renderers.UnreadableValue()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create_name_filter(
|
||||
cls, name_list: List[str] = None, exclude: bool = False
|
||||
|
||||
@@ -266,20 +266,28 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
if proc.vol.layer_name == kernel.layer_name:
|
||||
vproc = proc
|
||||
else:
|
||||
vproc = self.virtual_process_from_physical(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name, proc
|
||||
try:
|
||||
vproc = self.virtual_process_from_physical(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
proc,
|
||||
)
|
||||
except exceptions.PagedInvalidAddressException:
|
||||
vproc = None
|
||||
|
||||
file_output = "Error outputting file"
|
||||
if vproc:
|
||||
file_handle = pslist.PsList.process_dump(
|
||||
self.context,
|
||||
kernel.symbol_table_name,
|
||||
pe_table_name,
|
||||
vproc,
|
||||
self.open,
|
||||
)
|
||||
|
||||
file_handle = pslist.PsList.process_dump(
|
||||
self.context,
|
||||
kernel.symbol_table_name,
|
||||
pe_table_name,
|
||||
vproc,
|
||||
self.open,
|
||||
)
|
||||
file_output = "Error outputting file"
|
||||
if file_handle:
|
||||
file_output = file_handle.preferred_filename
|
||||
if file_handle:
|
||||
file_output = file_handle.preferred_filename
|
||||
|
||||
if not self.config["physical"]:
|
||||
offset = proc.vol.offset
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
import datetime, logging, string
|
||||
|
||||
from volatility3.framework import constants, exceptions
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints, TreeGrid
|
||||
from volatility3.plugins.windows import (
|
||||
handles,
|
||||
info,
|
||||
pslist,
|
||||
psscan,
|
||||
sessions,
|
||||
thrdscan,
|
||||
)
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PsXView(plugins.PluginInterface):
|
||||
"""Lists all processes found via four of the methods described in \"The Art of Memory Forensics,\" which may help
|
||||
identify processes that are trying to hide themselves. I recommend using -r pretty if you are looking at this
|
||||
plugin's output in a terminal."""
|
||||
|
||||
# I've omitted the desktop thread scanning method because Volatility3 doesn't appear to have the funcitonality
|
||||
# which the original plugin used to do it.
|
||||
|
||||
# The sessions method is omitted because it begins with the list of processes found by Pslist anyway.
|
||||
|
||||
# Lastly, I've omitted the pspcid method because I could not for the life of me get it to work. I saved the
|
||||
# code I do have from it, and will happily share it if anyone else wants to add it.
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
valid_proc_name_chars = set(
|
||||
string.ascii_lowercase + string.ascii_uppercase + "." + " "
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="info", component=info.Info, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="psscan", component=psscan.PsScan, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="thrdscan", component=thrdscan.ThrdScan, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="handles", component=handles.Handles, version=(1, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="physical-offsets",
|
||||
description="List processes with physical offsets instead of virtual offsets.",
|
||||
optional=True,
|
||||
),
|
||||
]
|
||||
|
||||
def _proc_name_to_string(self, proc):
|
||||
return proc.ImageFileName.cast(
|
||||
"string", max_length=proc.ImageFileName.vol.count, errors="replace"
|
||||
)
|
||||
|
||||
def _is_valid_proc_name(self, str):
|
||||
for c in str:
|
||||
if not c in self.valid_proc_name_chars:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _filter_garbage_procs(self, proc_list):
|
||||
return [
|
||||
p
|
||||
for p in proc_list
|
||||
if p.is_valid() and self._is_valid_proc_name(self._proc_name_to_string(p))
|
||||
]
|
||||
|
||||
def _translate_offset(self, offset):
|
||||
if not self.config["physical-offsets"]:
|
||||
return offset
|
||||
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
layer_name = kernel.layer_name
|
||||
|
||||
try:
|
||||
_original_offset, _original_length, offset, _length, _layer_name = list(
|
||||
self.context.layers[layer_name].mapping(offset=offset, length=0)
|
||||
)[0]
|
||||
except exceptions.PagedInvalidAddressException:
|
||||
vollog.debug(f"Page fault: unable to translate {offset:0x}")
|
||||
|
||||
return offset
|
||||
|
||||
def _proc_list_to_dict(self, tasks):
|
||||
tasks = self._filter_garbage_procs(tasks)
|
||||
return {self._translate_offset(proc.vol.offset): proc for proc in tasks}
|
||||
|
||||
def _check_pslist(self, tasks):
|
||||
return self._proc_list_to_dict(tasks)
|
||||
|
||||
def _check_psscan(self, layer_name, symbol_table):
|
||||
res = psscan.PsScan.scan_processes(
|
||||
context=self.context, layer_name=layer_name, symbol_table=symbol_table
|
||||
)
|
||||
|
||||
return self._proc_list_to_dict(res)
|
||||
|
||||
def _check_thrdscan(self):
|
||||
ret = []
|
||||
|
||||
for ethread in thrdscan.ThrdScan.scan_threads(
|
||||
self.context, module_name="kernel"
|
||||
):
|
||||
process = None
|
||||
try:
|
||||
process = ethread.owning_process()
|
||||
if not process.is_valid():
|
||||
continue
|
||||
|
||||
ret.append(process)
|
||||
except AttributeError:
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVV,
|
||||
"Unable to find the owning process of ethread",
|
||||
)
|
||||
|
||||
return self._proc_list_to_dict(ret)
|
||||
|
||||
def _check_csrss_handles(self, tasks, layer_name, symbol_table):
|
||||
ret = []
|
||||
|
||||
for p in tasks:
|
||||
name = self._proc_name_to_string(p)
|
||||
if name == "csrss.exe":
|
||||
try:
|
||||
if p.has_member("ObjectTable"):
|
||||
handles_plugin = handles.Handles(
|
||||
context=self.context, config_path=self.config_path
|
||||
)
|
||||
hndls = list(handles_plugin.handles(p.ObjectTable))
|
||||
for h in hndls:
|
||||
if (
|
||||
h.get_object_type(
|
||||
handles_plugin.get_type_map(
|
||||
self.context, layer_name, symbol_table
|
||||
)
|
||||
)
|
||||
== "Process"
|
||||
):
|
||||
ret.append(h.Body.cast("_EPROCESS"))
|
||||
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVV, "Cannot access eprocess object table"
|
||||
)
|
||||
|
||||
return self._proc_list_to_dict(ret)
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
layer_name = kernel.layer_name
|
||||
symbol_table = kernel.symbol_table_name
|
||||
|
||||
kdbg_list_processes = list(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context, layer_name=layer_name, symbol_table=symbol_table
|
||||
)
|
||||
)
|
||||
|
||||
# get processes from each source
|
||||
processes = {}
|
||||
|
||||
processes["pslist"] = self._check_pslist(kdbg_list_processes)
|
||||
processes["psscan"] = self._check_psscan(layer_name, symbol_table)
|
||||
processes["thrdscan"] = self._check_thrdscan()
|
||||
processes["csrss"] = self._check_csrss_handles(
|
||||
kdbg_list_processes, layer_name, symbol_table
|
||||
)
|
||||
|
||||
# print results
|
||||
|
||||
# list of lists of offsets
|
||||
offsets = [list(processes[source].keys()) for source in processes]
|
||||
|
||||
# flatten to one list
|
||||
offsets = sum(offsets, [])
|
||||
|
||||
# remove duplicates
|
||||
offsets = set(offsets)
|
||||
|
||||
for offset in offsets:
|
||||
proc = None
|
||||
|
||||
in_sources = {src: False for src in processes}
|
||||
|
||||
for source in processes:
|
||||
if offset in processes[source]:
|
||||
in_sources[source] = True
|
||||
if not proc:
|
||||
proc = processes[source][offset]
|
||||
|
||||
pid = proc.UniqueProcessId
|
||||
name = self._proc_name_to_string(proc)
|
||||
|
||||
exit_time = proc.get_exit_time()
|
||||
if type(exit_time) != datetime.datetime:
|
||||
exit_time = ""
|
||||
else:
|
||||
exit_time = str(exit_time)
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(offset),
|
||||
name,
|
||||
pid,
|
||||
in_sources["pslist"],
|
||||
in_sources["psscan"],
|
||||
in_sources["thrdscan"],
|
||||
in_sources["csrss"],
|
||||
exit_time,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
offset_type = "(Physical)" if self.config["physical-offsets"] else "(Virtual)"
|
||||
offset_str = "Offset" + offset_type
|
||||
|
||||
return TreeGrid(
|
||||
[
|
||||
(offset_str, format_hints.Hex),
|
||||
("Name", str),
|
||||
("PID", int),
|
||||
("pslist", bool),
|
||||
("psscan", bool),
|
||||
("thrdscan", bool),
|
||||
("csrss", bool),
|
||||
("Exit Time", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -0,0 +1,98 @@
|
||||
# This file is Copyright 2024 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 typing import List
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.windows import ssdt
|
||||
from volatility3.plugins.windows.registry import hivelist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GetCellRoutine(interfaces.plugins.PluginInterface):
|
||||
"""Reports registry hives with a hooked GetCellRoutine handler"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
|
||||
# walk each hive and validate that the GetCellRoutine handler
|
||||
# is inside of the kernel (ntoskrnl)
|
||||
for hive_object in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
):
|
||||
hive = hive_object.hive
|
||||
|
||||
try:
|
||||
cellroutine = hive.GetCellRoutine
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
module_symbols = list(
|
||||
collection.get_module_symbols_by_absolute_location(cellroutine)
|
||||
)
|
||||
|
||||
if module_symbols:
|
||||
for module_name, _ in module_symbols:
|
||||
# GetCellRoutine handlers should only be in the kernel
|
||||
if module_name not in constants.windows.KERNEL_MODULE_NAMES:
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(hive.vol.offset),
|
||||
hive_object.get_name() or "",
|
||||
module_name,
|
||||
format_hints.Hex(cellroutine),
|
||||
),
|
||||
)
|
||||
# Doesn't map to any module...
|
||||
else:
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(hive.vol.offset),
|
||||
hive_object.get_name() or "",
|
||||
renderers.NotAvailableValue(),
|
||||
format_hints.Hex(cellroutine),
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Hive Offset", renderers.format_hints.Hex),
|
||||
("Hive Name", str),
|
||||
("GetCellRoutine Module", str),
|
||||
("GetCellRoutine Handler", renderers.format_hints.Hex),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -17,11 +17,12 @@ from volatility3.framework.layers.registry import RegistryHive
|
||||
from volatility3.framework.renderers import conversion, format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.plugins.windows.registry import hivelist
|
||||
from volatility3.plugins import timeliner
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserAssist(interfaces.plugins.PluginInterface):
|
||||
class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Print userassist registry keys and information."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
@@ -285,6 +286,10 @@ class UserAssist(interfaces.plugins.PluginInterface):
|
||||
hive_offsets = [self.config.get("offset", None)]
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
self._reg_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self._config_path, "windows", "registry"
|
||||
)
|
||||
|
||||
# get all the user hive offsets or use the one specified
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
@@ -335,11 +340,17 @@ class UserAssist(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
yield result
|
||||
|
||||
def run(self):
|
||||
self._reg_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self._config_path, "windows", "registry"
|
||||
)
|
||||
def generate_timeline(self):
|
||||
for row in self._generator():
|
||||
_depth, row_data = row
|
||||
# check the name and the timestamp to not be empty
|
||||
if isinstance(row_data[5], str) and not isinstance(
|
||||
row_data[10], renderers.NotApplicableValue
|
||||
):
|
||||
description = f"UserAssist: {row_data[5]} {row_data[2]} ({row_data[7]})"
|
||||
yield (description, timeliner.TimeLinerType.MODIFIED, row_data[10])
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Hive Offset", renderers.format_hints.Hex),
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
# This file is Copyright 2020 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
|
||||
import os
|
||||
from datetime import datetime
|
||||
from itertools import count
|
||||
from typing import Iterator, List, Optional, Tuple
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers, symbols
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects.utility import array_to_string
|
||||
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 pe, shimcache
|
||||
from volatility3.plugins import timeliner
|
||||
from volatility3.plugins.windows import modules, pslist, vadinfo
|
||||
|
||||
# from volatility3.plugins.windows import pslist, vadinfo, modules
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Reads Shimcache entries from the ahcache.sys AVL tree"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
# These checks must be completed from newest -> oldest OS version.
|
||||
_win_version_file_map: List[Tuple[versions.OsDistinguisher, bool, str]] = [
|
||||
(versions.is_win10, True, "shimcache-win10-x64"),
|
||||
(versions.is_win10, False, "shimcache-win10-x86"),
|
||||
(versions.is_windows_8_or_later, True, "shimcache-win8-x64"),
|
||||
(versions.is_windows_8_or_later, False, "shimcache-win8-x86"),
|
||||
(versions.is_windows_7, True, "shimcache-win7-x64"),
|
||||
(versions.is_windows_7, False, "shimcache-win7-x86"),
|
||||
(versions.is_vista_or_later, True, "shimcache-vista-x64"),
|
||||
(versions.is_vista_or_later, False, "shimcache-vista-x86"),
|
||||
(versions.is_2003, False, "shimcache-2003-x86"),
|
||||
(versions.is_2003, True, "shimcache-2003-x64"),
|
||||
(versions.is_windows_xp_sp3, False, "shimcache-xp-sp3-x86"),
|
||||
(versions.is_windows_xp_sp2, False, "shimcache-xp-sp2-x86"),
|
||||
(versions.is_xp_or_2003, True, "shimcache-xp-2003-x64"),
|
||||
(versions.is_xp_or_2003, False, "shimcache-xp-2003-x86"),
|
||||
]
|
||||
|
||||
NT_KRNL_MODS = ["ntoskrnl.exe", "ntkrnlpa.exe", "ntkrnlmp.exe", "ntkrpamp.exe"]
|
||||
|
||||
def generate_timeline(
|
||||
self,
|
||||
) -> Iterator[Tuple[str, timeliner.TimeLinerType, datetime]]:
|
||||
for _, (_, last_modified, last_update, _, _, file_path) in self._generator():
|
||||
if isinstance(last_update, datetime):
|
||||
yield f"Shimcache: File {file_path} executed", timeliner.TimeLinerType.ACCESSED, last_update
|
||||
if isinstance(last_modified, datetime):
|
||||
yield f"Shimcache: File {file_path} modified", timeliner.TimeLinerType.MODIFIED, last_modified
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def create_shimcache_table(
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Creates a shimcache symbol table
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
config_path: The configuration path within the context of the symbol table to create
|
||||
|
||||
Returns:
|
||||
The name of the constructed shimcache table
|
||||
"""
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
|
||||
try:
|
||||
symbol_filename = next(
|
||||
filename
|
||||
for version_check, for_64bit, filename in ShimcacheMem._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!")
|
||||
|
||||
vollog.debug(f"Using shimcache table {symbol_filename}")
|
||||
|
||||
return intermed.IntermediateSymbolTable.create(
|
||||
context,
|
||||
config_path,
|
||||
os.path.join("windows", "shimcache"),
|
||||
symbol_filename,
|
||||
class_types=shimcache.class_types,
|
||||
native_types=native_types,
|
||||
table_mapping=table_mapping,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def find_shimcache_win_xp(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
kernel_symbol_table: str,
|
||||
shimcache_symbol_table: str,
|
||||
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Attempts to find the shimcache in a Windows XP memory image
|
||||
|
||||
:param context: The context to retrieve required elements (layers, symbol tables) from
|
||||
:param layer_name: The name of the memory layer on which to operate.
|
||||
:param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
|
||||
"""
|
||||
|
||||
SHIM_NUM_ENTRIES_OFFSET = 0x8
|
||||
SHIM_MAX_ENTRIES = 0x60 # 96 max entries in XP shim cache
|
||||
SHIM_LRU_OFFSET = 0x10
|
||||
SHIM_HEADER_SIZE = 0x190
|
||||
SHIM_CACHE_ENTRY_SIZE = 0x228
|
||||
|
||||
seen = set()
|
||||
|
||||
for process in pslist.PsList.list_processes(
|
||||
context, layer_name, kernel_symbol_table
|
||||
):
|
||||
pid = process.UniqueProcessId
|
||||
vollog.debug("checking process %d" % pid)
|
||||
for vad in vadinfo.VadInfo.list_vads(
|
||||
process, lambda x: x.get_tag() == b"Vad " and x.Protection == 4
|
||||
):
|
||||
try:
|
||||
proc_layer_name = process.add_process_layer()
|
||||
proc_layer = context.layers[proc_layer_name]
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
try:
|
||||
if proc_layer.read(vad.get_start(), 4) != b"\xEF\xBE\xAD\xDE":
|
||||
if pid == 624:
|
||||
vollog.debug("VAD magic bytes don't match DEADBEEF")
|
||||
continue
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
num_entries = context.object(
|
||||
shimcache_symbol_table + constants.BANG + "unsigned int",
|
||||
proc_layer_name,
|
||||
vad.get_start() + SHIM_NUM_ENTRIES_OFFSET,
|
||||
)
|
||||
|
||||
if num_entries > SHIM_MAX_ENTRIES:
|
||||
continue
|
||||
|
||||
cache_idx_ptr = vad.get_start() + SHIM_LRU_OFFSET
|
||||
|
||||
for _ in range(num_entries):
|
||||
cache_idx_val = proc_layer.context.object(
|
||||
shimcache_symbol_table + constants.BANG + "unsigned long",
|
||||
proc_layer_name,
|
||||
cache_idx_ptr,
|
||||
)
|
||||
|
||||
cache_idx_ptr += 4
|
||||
|
||||
if cache_idx_val > SHIM_MAX_ENTRIES - 1:
|
||||
continue
|
||||
|
||||
shim_entry_offset = (
|
||||
vad.get_start()
|
||||
+ SHIM_HEADER_SIZE
|
||||
+ (SHIM_CACHE_ENTRY_SIZE * cache_idx_val)
|
||||
)
|
||||
|
||||
if not proc_layer.is_valid(shim_entry_offset):
|
||||
continue
|
||||
|
||||
physical_addr = proc_layer.translate(shim_entry_offset)
|
||||
|
||||
if physical_addr in seen:
|
||||
continue
|
||||
seen.add(physical_addr)
|
||||
|
||||
shim_entry = proc_layer.context.object(
|
||||
shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY",
|
||||
proc_layer_name,
|
||||
shim_entry_offset,
|
||||
)
|
||||
if not proc_layer.is_valid(shim_entry.vol.offset):
|
||||
continue
|
||||
if not shim_entry.is_valid():
|
||||
continue
|
||||
|
||||
yield shim_entry
|
||||
|
||||
@classmethod
|
||||
def find_shimcache_win_2k3_to_7(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_layer_name: str,
|
||||
nt_symbol_table: str,
|
||||
shimcache_symbol_table: str,
|
||||
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Implements the algorithm to search for the shim cache on Windows 2000
|
||||
(x64) through Windows 7 / 2008 R2. The algorithm consists of the following:
|
||||
|
||||
1) Find the NT kernel module's .data and PAGE sections
|
||||
2) Iterate over every 4/8 bytes (depending on OS bitness) in the .data
|
||||
section and test for the following:
|
||||
a) offset represents a valid RTL_AVL_TABLE object
|
||||
b) RTL_AVL_TABLE is preceeded by an ERESOURCE object
|
||||
c) RTL_AVL_TABLE is followed by the beginning of the SHIM LRU list
|
||||
|
||||
:param context: The context to retrieve required elements (layers, symbol tables) from
|
||||
:param layer_name: The name of the memory layer on which to operate.
|
||||
:param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
|
||||
"""
|
||||
|
||||
data_sec = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
cls.NT_KRNL_MODS,
|
||||
".data",
|
||||
)
|
||||
mod_page = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
cls.NT_KRNL_MODS,
|
||||
"PAGE",
|
||||
)
|
||||
|
||||
# We require both in order to accurately handle AVL table
|
||||
if not (data_sec and mod_page):
|
||||
return None
|
||||
|
||||
data_sec_offset, data_sec_size = data_sec
|
||||
mod_page_offset, mod_page_size = mod_page
|
||||
|
||||
addr_size = 8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4
|
||||
|
||||
shim_head = None
|
||||
for offset in range(
|
||||
data_sec_offset, data_sec_offset + data_sec_size, addr_size
|
||||
):
|
||||
shim_head = cls.try_get_shim_head_at_offset(
|
||||
context,
|
||||
shimcache_symbol_table,
|
||||
nt_symbol_table,
|
||||
kernel_layer_name,
|
||||
mod_page_offset,
|
||||
mod_page_offset + mod_page_size,
|
||||
offset,
|
||||
)
|
||||
|
||||
if shim_head:
|
||||
break
|
||||
|
||||
if not shim_head:
|
||||
return
|
||||
|
||||
for shim_entry in shim_head.ListEntry.to_list(
|
||||
shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY", "ListEntry"
|
||||
):
|
||||
yield shim_entry
|
||||
|
||||
@classmethod
|
||||
def try_get_shim_head_at_offset(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
kernel_symbol_table: str,
|
||||
layer_name: str,
|
||||
mod_page_start: int,
|
||||
mod_page_end: int,
|
||||
offset: int,
|
||||
) -> Optional[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Attempts to construct a SHIM_CACHE_HEAD within a layer of the given context,
|
||||
using the provided offset within that layer, as well as the start and end offsets
|
||||
of the kernel module's `PAGE` section start and end offsets.
|
||||
|
||||
If a number of validity checks are passed, this method will return the `SHIM_CACHE_HEAD`
|
||||
object. Otherwise, `None` is returned.
|
||||
"""
|
||||
# print("checking RTL_AVL_TABLE at offset %s" % hex(offset))
|
||||
rtl_avl_table = context.object(
|
||||
symbol_table + constants.BANG + "_RTL_AVL_TABLE", layer_name, offset
|
||||
)
|
||||
if not rtl_avl_table.is_valid(mod_page_start, mod_page_end):
|
||||
return None
|
||||
|
||||
vollog.debug(f"Candidate RTL_AVL_TABLE found at offset {hex(offset)}")
|
||||
|
||||
ersrc_size = context.symbol_space.get_type(
|
||||
kernel_symbol_table + constants.BANG + "_ERESOURCE"
|
||||
).size
|
||||
ersrc_alignment = (
|
||||
0x20
|
||||
if symbols.symbol_table_is_64bit(context, kernel_symbol_table)
|
||||
else 0x10
|
||||
# 0x20 if context.symbol_space.get_type("pointer").size == 8 else 0x10
|
||||
)
|
||||
vollog.debug(
|
||||
f"ERESOURCE size: {hex(ersrc_size)}, ERESOURCE alignment: {hex(ersrc_alignment)}"
|
||||
)
|
||||
|
||||
eresource_rel_off = ersrc_size + ((offset - ersrc_size) % ersrc_alignment)
|
||||
eresource_offset = offset - eresource_rel_off
|
||||
|
||||
vollog.debug("Constructing ERESOURCE at %s" % hex(eresource_offset))
|
||||
eresource = context.object(
|
||||
kernel_symbol_table + constants.BANG + "_ERESOURCE",
|
||||
layer_name,
|
||||
eresource_offset,
|
||||
)
|
||||
if not eresource.is_valid():
|
||||
vollog.debug("ERESOURCE Invalid")
|
||||
return None
|
||||
|
||||
shim_head_offset = offset + rtl_avl_table.vol.size
|
||||
|
||||
if not context.layers[layer_name].is_valid(shim_head_offset):
|
||||
return None
|
||||
|
||||
shim_head = context.object(
|
||||
symbol_table + constants.BANG + "SHIM_CACHE_ENTRY",
|
||||
layer_name,
|
||||
shim_head_offset,
|
||||
)
|
||||
|
||||
if not shim_head.is_valid():
|
||||
vollog.debug("shim head invalid")
|
||||
return None
|
||||
else:
|
||||
vollog.debug("returning shim head")
|
||||
return shim_head
|
||||
|
||||
@classmethod
|
||||
def find_shimcache_win_8_or_later(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_layer_name: str,
|
||||
nt_symbol_table: str,
|
||||
shimcache_symbol_table: str,
|
||||
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Attempts to locate and yield shimcache entries from a Windows 8 or later memory image.
|
||||
|
||||
:param context: The context to retrieve required elements (layers, symbol tables) from
|
||||
:param layer_name: The name of the memory layer on which to operate.
|
||||
:param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
|
||||
"""
|
||||
|
||||
is_8_1_or_later = versions.is_windows_8_1_or_later(
|
||||
context, nt_symbol_table
|
||||
) or versions.is_win10(context, nt_symbol_table)
|
||||
|
||||
module_names = ["ahcache.sys"] if is_8_1_or_later else cls.NT_KRNL_MODS
|
||||
vollog.debug(f"Searching for modules {module_names}")
|
||||
|
||||
data_sec = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
module_names,
|
||||
".data",
|
||||
)
|
||||
mod_page = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
module_names,
|
||||
"PAGE",
|
||||
)
|
||||
|
||||
if not (data_sec and mod_page):
|
||||
return None
|
||||
|
||||
mod_page_offset, mod_page_size = mod_page
|
||||
data_sec_offset, data_sec_size = data_sec
|
||||
|
||||
# iterate over ahcache kernel module's .data section in search of *two* SHIM handles
|
||||
shim_heads = []
|
||||
|
||||
vollog.debug(f"PAGE offset: {hex(mod_page_offset)}")
|
||||
vollog.debug(f".data offset: {hex(data_sec_offset)}")
|
||||
|
||||
handle_type = context.symbol_space.get_type(
|
||||
shimcache_symbol_table + constants.BANG + "SHIM_CACHE_HANDLE"
|
||||
)
|
||||
for offset in range(
|
||||
data_sec_offset,
|
||||
data_sec_offset + data_sec_size,
|
||||
8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4,
|
||||
):
|
||||
vollog.debug(f"Building shim handle pointer at {hex(offset)}")
|
||||
shim_handle = context.object(
|
||||
object_type=shimcache_symbol_table + constants.BANG + "pointer",
|
||||
layer_name=kernel_layer_name,
|
||||
subtype=handle_type,
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
if shim_handle.is_valid(mod_page_offset, mod_page_offset + mod_page_size):
|
||||
if shim_handle.head is not None:
|
||||
vollog.debug(
|
||||
f"Found valid shim handle @ {hex(shim_handle.vol.offset)}"
|
||||
)
|
||||
shim_heads.append(shim_handle.head)
|
||||
if len(shim_heads) == 2:
|
||||
break
|
||||
|
||||
if len(shim_heads) != 2:
|
||||
vollog.debug("Failed to identify two valid SHIM_CACHE_HANDLE structures")
|
||||
return
|
||||
|
||||
# On Windows 8 x64, the frist cache contains the shim cache
|
||||
# On Windows 8 x86, 8.1 x86/x64, and 10, the second cache contains the shim cache.
|
||||
if (
|
||||
not symbols.symbol_table_is_64bit(context, nt_symbol_table)
|
||||
and not is_8_1_or_later
|
||||
):
|
||||
valid_head = shim_heads[1]
|
||||
elif not is_8_1_or_later:
|
||||
valid_head = shim_heads[0]
|
||||
else:
|
||||
valid_head = shim_heads[1]
|
||||
|
||||
for shim_entry in valid_head.ListEntry.to_list(
|
||||
shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY", "ListEntry"
|
||||
):
|
||||
if shim_entry.is_valid():
|
||||
yield shim_entry
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
shimcache_table_name = self.create_shimcache_table(
|
||||
self.context, kernel.symbol_table_name, self.config_path
|
||||
)
|
||||
|
||||
c = count()
|
||||
|
||||
if versions.is_windows_8_or_later(self._context, kernel.symbol_table_name):
|
||||
vollog.info("Finding shimcache entries for Windows 8.0+")
|
||||
entries = self.find_shimcache_win_8_or_later(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
shimcache_table_name,
|
||||
)
|
||||
|
||||
elif (
|
||||
versions.is_2003(self.context, kernel.symbol_table_name)
|
||||
or versions.is_vista_or_later(self.context, kernel.symbol_table_name)
|
||||
or versions.is_windows_7(self.context, kernel.symbol_table_name)
|
||||
):
|
||||
vollog.info("Finding shimcache entries for Windows 2k3/Vista/7")
|
||||
entries = self.find_shimcache_win_2k3_to_7(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
shimcache_table_name,
|
||||
)
|
||||
|
||||
elif versions.is_windows_xp_sp2(
|
||||
self._context, kernel.symbol_table_name
|
||||
) or versions.is_windows_xp_sp3(self.context, kernel.symbol_table_name):
|
||||
vollog.info("Finding shimcache entries for WinXP")
|
||||
entries = self.find_shimcache_win_xp(
|
||||
self._context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
shimcache_table_name,
|
||||
)
|
||||
else:
|
||||
vollog.warn("Cannot parse shimcache entries for this version of Windows")
|
||||
return
|
||||
|
||||
for entry in entries:
|
||||
try:
|
||||
vollog.debug(f"SHIM_CACHE_ENTRY type: {entry.__class__}")
|
||||
shim_entry = (
|
||||
entry.last_modified,
|
||||
entry.last_update,
|
||||
entry.exec_flag,
|
||||
(
|
||||
format_hints.Hex(entry.file_size)
|
||||
if isinstance(entry.file_size, int)
|
||||
else entry.file_size
|
||||
),
|
||||
entry.file_path,
|
||||
)
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
yield (
|
||||
0,
|
||||
(next(c), *shim_entry),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Order", int),
|
||||
("Last Modified", datetime),
|
||||
("Last Update", datetime),
|
||||
("Exec Flag", bool),
|
||||
("File Size", format_hints.Hex),
|
||||
("File Path", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_module_section_range(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
module_list: List[str],
|
||||
section_name: str,
|
||||
) -> Optional[Tuple[int, int]]:
|
||||
"""Locates the size and offset of the first found module section
|
||||
specified by name from the list of modules.
|
||||
|
||||
:param context: The context to operate on
|
||||
:param layer_name: The memory layer to read from
|
||||
:param module_list: A list of module names to search for the given section
|
||||
:param section_name: The name of the section to search for.
|
||||
|
||||
:return: The offset and size of the module, if found; Otherwise, returns `None`
|
||||
"""
|
||||
|
||||
try:
|
||||
krnl_mod = next(
|
||||
module
|
||||
for module in modules.Modules.list_modules(
|
||||
context, layer_name, symbol_table
|
||||
)
|
||||
if module.BaseDllName.String in module_list
|
||||
)
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
context,
|
||||
interfaces.configuration.path_join(config_path, "pe"),
|
||||
"windows",
|
||||
"pe",
|
||||
class_types=pe.class_types,
|
||||
)
|
||||
|
||||
# code taken from Win32KBase._section_chunks (win32_core.py)
|
||||
dos_header = context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
layer_name,
|
||||
offset=krnl_mod.DllBase,
|
||||
)
|
||||
|
||||
if not dos_header:
|
||||
return None
|
||||
|
||||
nt_header = dos_header.get_nt_header()
|
||||
|
||||
try:
|
||||
section = next(
|
||||
sec
|
||||
for sec in nt_header.get_sections()
|
||||
if section_name.lower() == array_to_string(sec.Name).lower()
|
||||
)
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
section_offset = krnl_mod.DllBase + section.VirtualAddress
|
||||
section_size = section.Misc.VirtualSize
|
||||
|
||||
return section_offset, section_size
|
||||
@@ -0,0 +1,214 @@
|
||||
# This file is Copyright 2024 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 typing import List, Dict, Tuple, Generator
|
||||
from volatility3.framework import renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.windows import pslist, threads, vadinfo, thrdscan
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SupsiciousThreads(interfaces.plugins.PluginInterface):
|
||||
"""Lists suspicious userland process threads"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
description="Filter on specific process IDs",
|
||||
element_type=int,
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="threads", plugin=threads.Threads, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _get_ranges(
|
||||
self,
|
||||
kernel: interfaces.context.ModuleInterface,
|
||||
all_ranges: Dict[int, List[Tuple[int, int, str, str]]],
|
||||
proc,
|
||||
) -> Tuple[int, int, str, str]:
|
||||
"""
|
||||
Maintains a hash table so each process' VADs
|
||||
are only enumerated once per plugin run
|
||||
"""
|
||||
key = proc.vol.offset
|
||||
|
||||
if key not in all_ranges:
|
||||
all_ranges[key] = []
|
||||
|
||||
for vad in proc.get_vad_root().traverse():
|
||||
fn = vad.get_file_name()
|
||||
if not isinstance(fn, str) or not fn:
|
||||
fn = None
|
||||
|
||||
protection_string = vad.get_protection(
|
||||
vadinfo.VadInfo.protect_values(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
),
|
||||
vadinfo.winnt_protections,
|
||||
)
|
||||
|
||||
all_ranges[key].append(
|
||||
(vad.get_start(), vad.get_end(), protection_string, fn)
|
||||
)
|
||||
|
||||
return all_ranges[key]
|
||||
|
||||
def _get_range(
|
||||
self, ranges: Dict[int, List[Tuple[int, int, str, str]]], address: int
|
||||
) -> Tuple[int, str, str]:
|
||||
"""
|
||||
Walks a process' VADs looking for the one
|
||||
containing `address`
|
||||
|
||||
Returns its base address, protection string, and mapped file, if any
|
||||
"""
|
||||
for start, end, protection_string, fn in ranges:
|
||||
if start <= address < end:
|
||||
return start, protection_string, fn
|
||||
|
||||
return None, None, None
|
||||
|
||||
def _check_thread_address(
|
||||
self, exe_path: str, ranges, thread_address: int
|
||||
) -> Generator[Tuple[str, str], None, None]:
|
||||
vad_base, prot, vad_path = self._get_range(ranges, thread_address)
|
||||
|
||||
# threads outside of a VAD means either smear from this thread or this process' VAD tree
|
||||
if vad_base is None:
|
||||
return
|
||||
|
||||
if vad_path is None:
|
||||
# set this so checks after report the non file backed region in the path column
|
||||
vad_path = "<Non-File Backed Region>"
|
||||
|
||||
yield (
|
||||
vad_path,
|
||||
f"This thread started execution in the VAD starting at base address ({vad_base:#x}), which is not backed by a file",
|
||||
)
|
||||
|
||||
# All threads should point to PAGE_EXECUTE_WRITECOPY mapped regions
|
||||
if prot != "PAGE_EXECUTE_WRITECOPY":
|
||||
yield (
|
||||
vad_path,
|
||||
f"VAD at base address ({vad_base:#x}) hosting this thread has an unexpected starting protection {prot}",
|
||||
)
|
||||
|
||||
# check for process hollowing type techniques that mapped in a second, malicious exe file
|
||||
if (
|
||||
exe_path
|
||||
and vad_path.lower().endswith(".exe")
|
||||
and (vad_path.lower() != exe_path.lower())
|
||||
):
|
||||
yield (
|
||||
vad_path,
|
||||
"VAD at base address ({vad_base:#x}) hosting this thread maps an application executable that is not the process exectuable",
|
||||
)
|
||||
|
||||
def _enumerate_processes(
|
||||
self, kernel: interfaces.context.ModuleInterface, all_ranges
|
||||
):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
ranges = self._get_ranges(kernel, all_ranges, proc)
|
||||
|
||||
# smeared vads or process is terminating
|
||||
if len(all_ranges[proc.vol.offset]) < 5:
|
||||
continue
|
||||
|
||||
pid = proc.UniqueProcessId
|
||||
proc_name = utility.array_to_string(proc.ImageFileName)
|
||||
|
||||
_, __, exe_path = self._get_range(ranges, proc.SectionBaseAddress)
|
||||
if not isinstance(exe_path, str):
|
||||
exe_path = None
|
||||
|
||||
yield proc, pid, proc_name, exe_path, ranges
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
all_ranges = {}
|
||||
|
||||
for proc, pid, proc_name, exe_path, ranges in self._enumerate_processes(
|
||||
kernel, all_ranges
|
||||
):
|
||||
# processes often create multiple threads at the same address
|
||||
# there is no benefit to checking the same address more than once per process
|
||||
checked = set()
|
||||
|
||||
for thread in threads.Threads.list_threads(kernel, proc):
|
||||
# do not process if a thread is exited or terminated (4 = Terminated)
|
||||
if thread.ExitTime.QuadPart > 0 or thread.Tcb.State == 4:
|
||||
continue
|
||||
|
||||
# bail if accessing the threads members causes a page fault
|
||||
info = thrdscan.ThrdScan.gather_thread_info(thread)
|
||||
if not info:
|
||||
continue
|
||||
|
||||
_, _, tid, start_address, _, _ = info
|
||||
|
||||
addresses = [
|
||||
(start_address, "Start"),
|
||||
(thread.Win32StartAddress, "Win32Start"),
|
||||
]
|
||||
|
||||
for address, context in addresses:
|
||||
if address in checked:
|
||||
continue
|
||||
checked.add(address)
|
||||
|
||||
for vad_path, note in self._check_thread_address(
|
||||
exe_path, ranges, address
|
||||
):
|
||||
yield 0, (
|
||||
proc_name,
|
||||
pid,
|
||||
tid,
|
||||
context,
|
||||
format_hints.Hex(address),
|
||||
vad_path,
|
||||
note,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Process", str),
|
||||
("PID", int),
|
||||
("TID", int),
|
||||
("Context", str),
|
||||
("Address", format_hints.Hex),
|
||||
("VAD Path", str),
|
||||
("Note", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -0,0 +1,103 @@
|
||||
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
# This module attempts to locate skeleton-key like function hooks.
|
||||
# It does this by locating the CSystems array through a variety of methods,
|
||||
# and then validating the entry for RC4 HMAC (0x17 / 23)
|
||||
#
|
||||
# For a thorough walkthrough on how the R&D was performed to develop this plugin,
|
||||
# please see our blogpost here:
|
||||
#
|
||||
# https://volatility-labs.blogspot.com/2021/10/memory-forensics-r-illustrated.html
|
||||
|
||||
import logging
|
||||
|
||||
from volatility3.framework import symbols, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.plugins.windows import svclist, svcscan
|
||||
from volatility3.framework.symbols.windows import versions
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SvcDiff(svcscan.SvcScan):
|
||||
"""Compares services found through list walking versus scanning to find rootkits"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enumeration_method = self.service_diff
|
||||
|
||||
@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="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="svclist", component=svclist.SvcList, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="svcscan", component=svcscan.SvcScan, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def service_diff(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
service_table_name: str,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
"""
|
||||
On Windows 10 version 15063+ 64bit Windows memory samples, walk the services list
|
||||
and scan for services then report differences
|
||||
"""
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context, symbol_table
|
||||
) or not versions.is_win10_15063_or_later(
|
||||
context=context, symbol_table=symbol_table
|
||||
):
|
||||
vollog.warning(
|
||||
"This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples"
|
||||
)
|
||||
return
|
||||
|
||||
from_scan = set()
|
||||
from_list = set()
|
||||
records = {}
|
||||
|
||||
# collect unique service names from scanning
|
||||
for service in svcscan.SvcScan.service_scan(
|
||||
context,
|
||||
layer_name,
|
||||
symbol_table,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
from_scan.add(service[6])
|
||||
records[service[6]] = service
|
||||
|
||||
# collect services from listing walking
|
||||
for service in svclist.SvcList.service_list(
|
||||
context,
|
||||
layer_name,
|
||||
symbol_table,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
from_list.add(service[6])
|
||||
|
||||
# report services found from scanning but not list walking
|
||||
for hidden_service in from_scan - from_list:
|
||||
yield records[hidden_service]
|
||||
@@ -0,0 +1,115 @@
|
||||
# This file is Copyright 2019 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 typing import List, Optional, Tuple
|
||||
|
||||
from volatility3.framework import interfaces, exceptions, symbols
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.symbols.windows import versions
|
||||
from volatility3.plugins.windows import svcscan, pslist
|
||||
from volatility3.framework.layers import scanners
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SvcList(svcscan.SvcScan):
|
||||
"""Lists services contained with the services.exe doubly linked list of services"""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enumeration_method = self.service_list
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.PluginRequirement(
|
||||
name="svcscan", plugin=svcscan.SvcScan, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_exe_range(cls, proc) -> Optional[Tuple[int, int]]:
|
||||
"""
|
||||
Returns a tuple of starting,ending address for
|
||||
the VAD containing services.exe
|
||||
"""
|
||||
|
||||
vad_root = proc.get_vad_root()
|
||||
for vad in vad_root.traverse():
|
||||
filename = vad.get_file_name()
|
||||
if isinstance(filename, str) and filename.lower().endswith(
|
||||
"\\services.exe"
|
||||
):
|
||||
return [(vad.get_start(), vad.get_size())]
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def service_list(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
service_table_name: str,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context, symbol_table
|
||||
) or not versions.is_win10_15063_or_later(
|
||||
context=context, symbol_table=symbol_table
|
||||
):
|
||||
vollog.warning(
|
||||
"This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples"
|
||||
)
|
||||
return
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
try:
|
||||
layer_name = proc.add_process_layer()
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.warning(
|
||||
"Unable to access memory of services.exe running with PID: {}".format(
|
||||
proc.UniqueProcessId
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
layer = context.layers[layer_name]
|
||||
|
||||
exe_range = cls._get_exe_range(proc)
|
||||
if not exe_range:
|
||||
vollog.warning(
|
||||
"Could not find the application executable VAD for services.exe. Unable to proceed."
|
||||
)
|
||||
continue
|
||||
|
||||
for offset in layer.scan(
|
||||
context=context,
|
||||
scanner=scanners.BytesScanner(needle=b"Sc27"),
|
||||
sections=exe_range,
|
||||
):
|
||||
for record in cls.enumerate_vista_or_later_header(
|
||||
context,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
layer_name,
|
||||
offset,
|
||||
):
|
||||
yield record
|
||||
@@ -19,7 +19,7 @@ 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.framework.symbols.windows.extensions import services as services_types
|
||||
from volatility3.plugins.windows import poolscanner, pslist, vadyarascan
|
||||
from volatility3.plugins.windows.registry import hivelist
|
||||
|
||||
@@ -39,7 +39,11 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
"""Scans for windows services."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
_version = (3, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enumeration_method = self.service_scan
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -106,7 +110,7 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def create_service_table(
|
||||
def _create_service_table(
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
config_path: str,
|
||||
@@ -140,18 +144,21 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
config_path,
|
||||
os.path.join("windows", "services"),
|
||||
symbol_filename,
|
||||
class_types=services.class_types,
|
||||
class_types=services_types.class_types,
|
||||
native_types=native_types,
|
||||
)
|
||||
|
||||
def _get_service_key(self, kernel) -> Optional[objects.StructType]:
|
||||
@staticmethod
|
||||
def _get_service_key(
|
||||
context, config_path: str, layer_name: str, symbol_table: str
|
||||
) -> Optional[objects.StructType]:
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
context=context,
|
||||
base_config_path=interfaces.configuration.path_join(
|
||||
self.config_path, "hivelist"
|
||||
config_path, "hivelist"
|
||||
),
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
filter_string="machine\\system",
|
||||
):
|
||||
# Get ControlSet\Services.
|
||||
@@ -232,30 +239,55 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
for service_key in services
|
||||
}
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
@classmethod
|
||||
def enumerate_vista_or_later_header(
|
||||
cls,
|
||||
context,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
proc_layer_name,
|
||||
offset,
|
||||
):
|
||||
if offset % 8:
|
||||
return
|
||||
|
||||
service_table_name = self.create_service_table(
|
||||
self.context, kernel.symbol_table_name, self.config_path
|
||||
service_header = context.object(
|
||||
service_table_name + constants.BANG + "_SERVICE_HEADER",
|
||||
offset=offset,
|
||||
layer_name=proc_layer_name,
|
||||
)
|
||||
|
||||
# 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 {}
|
||||
)
|
||||
if not service_header.is_valid():
|
||||
return
|
||||
|
||||
relative_tag_offset = self.context.symbol_space.get_type(
|
||||
# since we walk the s-list backwards, if we've seen
|
||||
# an object, then we've also seen all objects that
|
||||
# exist before it, thus we can break at that time.
|
||||
for service_record in service_header.ServiceRecord.traverse():
|
||||
service_info = service_binary_dll_map.get(
|
||||
service_record.get_name(),
|
||||
ServiceBinaryInfo(
|
||||
renderers.UnreadableValue(), renderers.UnreadableValue()
|
||||
),
|
||||
)
|
||||
yield cls.get_record_tuple(service_record, service_info)
|
||||
|
||||
@classmethod
|
||||
def service_scan(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
service_table_name: str,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
relative_tag_offset = context.symbol_space.get_type(
|
||||
service_table_name + constants.BANG + "_SERVICE_RECORD"
|
||||
).relative_child_offset("Tag")
|
||||
|
||||
filter_func = pslist.PsList.create_name_filter(["services.exe"])
|
||||
|
||||
is_vista_or_later = versions.is_vista_or_later(
|
||||
context=self.context, symbol_table=kernel.symbol_table_name
|
||||
context=context, symbol_table=symbol_table
|
||||
)
|
||||
|
||||
if is_vista_or_later:
|
||||
@@ -266,9 +298,9 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
seen = []
|
||||
|
||||
for task in pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
proc_id = "Unknown"
|
||||
@@ -283,15 +315,15 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
continue
|
||||
|
||||
layer = self.context.layers[proc_layer_name]
|
||||
layer = context.layers[proc_layer_name]
|
||||
|
||||
for offset in layer.scan(
|
||||
context=self.context,
|
||||
context=context,
|
||||
scanner=scanners.BytesScanner(needle=service_tag),
|
||||
sections=vadyarascan.VadYaraScan.get_vad_maps(task),
|
||||
):
|
||||
if not is_vista_or_later:
|
||||
service_record = self.context.object(
|
||||
service_record = context.object(
|
||||
service_table_name + constants.BANG + "_SERVICE_RECORD",
|
||||
offset=offset - relative_tag_offset,
|
||||
layer_name=proc_layer_name,
|
||||
@@ -306,37 +338,60 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
renderers.UnreadableValue(), renderers.UnreadableValue()
|
||||
),
|
||||
)
|
||||
yield (
|
||||
0,
|
||||
self.get_record_tuple(service_record, service_info),
|
||||
)
|
||||
yield cls.get_record_tuple(service_record, service_info)
|
||||
else:
|
||||
service_header = self.context.object(
|
||||
service_table_name + constants.BANG + "_SERVICE_HEADER",
|
||||
offset=offset,
|
||||
layer_name=proc_layer_name,
|
||||
)
|
||||
|
||||
if not service_header.is_valid():
|
||||
continue
|
||||
|
||||
# since we walk the s-list backwards, if we've seen
|
||||
# an object, then we've also seen all objects that
|
||||
# exist before it, thus we can break at that time.
|
||||
for service_record in service_header.ServiceRecord.traverse():
|
||||
for service_record in cls.enumerate_vista_or_later_header(
|
||||
context,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
proc_layer_name,
|
||||
offset,
|
||||
):
|
||||
if service_record in seen:
|
||||
break
|
||||
seen.append(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),
|
||||
)
|
||||
yield service_record
|
||||
|
||||
@classmethod
|
||||
def get_prereq_info(cls, context, config_path, layer_name: str, symbol_table: str):
|
||||
"""
|
||||
Data structures and information needed to analyze service information
|
||||
"""
|
||||
|
||||
service_table_name = cls._create_service_table(
|
||||
context, symbol_table, config_path
|
||||
)
|
||||
|
||||
services_key = cls._get_service_key(
|
||||
context, config_path, layer_name, symbol_table
|
||||
)
|
||||
|
||||
service_binary_dll_map = (
|
||||
cls._get_service_binary_map(services_key)
|
||||
if services_key is not None
|
||||
else {}
|
||||
)
|
||||
|
||||
filter_func = pslist.PsList.create_name_filter(["services.exe"])
|
||||
|
||||
return service_table_name, service_binary_dll_map, filter_func
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info(
|
||||
self.context, self.config_path, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
|
||||
for record in self._enumeration_method(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
yield (0, record)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
##
|
||||
import logging
|
||||
import datetime
|
||||
from typing import Iterable
|
||||
from typing import Callable, Iterable
|
||||
|
||||
from volatility3.framework import renderers, interfaces, exceptions
|
||||
from volatility3.framework.configuration import requirements
|
||||
@@ -19,7 +19,11 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
|
||||
# version 2.6.0 adds support for scanning for 'Ethread' structures by pool tags
|
||||
_required_framework_version = (2, 6, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (1, 1, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.implementation = self.scan_threads
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
@@ -38,8 +42,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
def scan_threads(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
module_name: str,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Scans for threads using the poolscanner module and constraints.
|
||||
|
||||
@@ -52,6 +55,10 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
A list of _ETHREAD objects found by scanning memory for the "Thre" / "Thr\\xE5" pool signatures
|
||||
"""
|
||||
|
||||
module = context.modules[module_name]
|
||||
layer_name = module.layer_name
|
||||
symbol_table = module.symbol_table_name
|
||||
|
||||
constraints = poolscanner.PoolScanner.builtin_constraints(
|
||||
symbol_table, [b"Thr\xe5", b"Thre"]
|
||||
)
|
||||
@@ -62,45 +69,45 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
_constraint, mem_object, _header = result
|
||||
yield mem_object
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
@classmethod
|
||||
def gather_thread_info(cls, ethread):
|
||||
try:
|
||||
thread_offset = ethread.vol.offset
|
||||
owner_proc_pid = ethread.Cid.UniqueProcess
|
||||
thread_tid = ethread.Cid.UniqueThread
|
||||
thread_start_addr = ethread.StartAddress
|
||||
thread_create_time = (
|
||||
ethread.get_create_time()
|
||||
) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
|
||||
thread_exit_time = (
|
||||
ethread.get_exit_time()
|
||||
) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.debug("Thread invalid address {:#x}".format(ethread.vol.offset))
|
||||
return None
|
||||
|
||||
for ethread in self.scan_threads(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
):
|
||||
try:
|
||||
thread_offset = ethread.vol.offset
|
||||
owner_proc_pid = ethread.Cid.UniqueProcess
|
||||
thread_tid = ethread.Cid.UniqueThread
|
||||
thread_start_addr = ethread.StartAddress
|
||||
thread_create_time = (
|
||||
ethread.get_create_time()
|
||||
) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
|
||||
thread_exit_time = (
|
||||
ethread.get_exit_time()
|
||||
) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
|
||||
except (ValueError, exceptions.InvalidAddressException):
|
||||
vollog.debug(
|
||||
"Thread :{}, invalid address {} in layer {}".format(
|
||||
thread_tid, thread_start_addr, kernel.layer_name
|
||||
)
|
||||
)
|
||||
continue
|
||||
return (
|
||||
format_hints.Hex(thread_offset),
|
||||
owner_proc_pid,
|
||||
thread_tid,
|
||||
format_hints.Hex(thread_start_addr),
|
||||
thread_create_time,
|
||||
thread_exit_time,
|
||||
)
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(thread_offset),
|
||||
owner_proc_pid,
|
||||
thread_tid,
|
||||
format_hints.Hex(thread_start_addr),
|
||||
thread_create_time,
|
||||
thread_exit_time,
|
||||
),
|
||||
)
|
||||
def _generator(self, filter_func: Callable):
|
||||
kernel_name = self.config["kernel"]
|
||||
|
||||
for ethread in self.implementation(self.context, kernel_name):
|
||||
info = self.gather_thread_info(ethread)
|
||||
|
||||
if info:
|
||||
yield (0, info)
|
||||
|
||||
def generate_timeline(self):
|
||||
for row in self._generator():
|
||||
filt_func = self.filter_func(self.config)
|
||||
|
||||
for row in self._generator(filt_func):
|
||||
_depth, row_data = row
|
||||
row_dict = {}
|
||||
(
|
||||
@@ -127,7 +134,14 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
row_dict["ExitTime"],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable:
|
||||
"""Returns a function that can filter this plugin's implementation method based on the config"""
|
||||
return lambda x: False
|
||||
|
||||
def run(self):
|
||||
filt_func = self.filter_func(self.config)
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
@@ -137,5 +151,5 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
("CreateTime", datetime.datetime),
|
||||
("ExitTime", datetime.datetime),
|
||||
],
|
||||
self._generator(),
|
||||
self._generator(filt_func),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# This file is Copyright 2019 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 typing import Callable, Iterable, List, Generator
|
||||
|
||||
from volatility3.framework import interfaces, constants
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.plugins.windows import pslist, thrdscan
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Threads(thrdscan.ThrdScan):
|
||||
"""Lists process threads"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.implementation = self.list_process_threads
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
description="Filter on specific process IDs",
|
||||
element_type=int,
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 1, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_threads(
|
||||
cls, kernel, proc: interfaces.objects.ObjectInterface
|
||||
) -> Generator[interfaces.objects.ObjectInterface, None, None]:
|
||||
"""Lists the Threads of a specific process.
|
||||
|
||||
Args:
|
||||
proc: _EPROCESS object from which to list the VADs
|
||||
filter_func: Function to take a virtual address descriptor value and return True if it should be filtered out
|
||||
|
||||
Returns:
|
||||
A list of threads based on the process and filtered based on the filter function
|
||||
"""
|
||||
seen = set()
|
||||
for thread in proc.ThreadListHead.to_list(
|
||||
f"{kernel.symbol_table_name}{constants.BANG}_ETHREAD", "ThreadListEntry"
|
||||
):
|
||||
if thread.vol.offset in seen:
|
||||
break
|
||||
seen.add(thread.vol.offset)
|
||||
yield thread
|
||||
|
||||
@classmethod
|
||||
def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable:
|
||||
return pslist.PsList.create_pid_filter(config.get("pid", None))
|
||||
|
||||
@classmethod
|
||||
def list_process_threads(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
module_name: str,
|
||||
filter_func: Callable,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Runs through all processes and lists threads for each process"""
|
||||
module = context.modules[module_name]
|
||||
layer_name = module.layer_name
|
||||
symbol_table_name = module.symbol_table_name
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
for thread in cls.list_threads(module, proc):
|
||||
yield thread
|
||||
@@ -0,0 +1,212 @@
|
||||
# This file is Copyright 2024 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 typing import Iterator, List, Tuple, Iterable
|
||||
|
||||
from volatility3.framework import (
|
||||
renderers,
|
||||
interfaces,
|
||||
constants,
|
||||
symbols,
|
||||
)
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols.windows import versions
|
||||
from volatility3.plugins.windows import ssdt, kpcrs
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Timers(interfaces.plugins.PluginInterface):
|
||||
"""Print kernel timers and associated module DPCs"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="kpcrs", plugin=kpcrs.KPCRs, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_timers(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
) -> Iterable[Tuple[str, int, str]]:
|
||||
"""Lists all kernel timers.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
kernel_module_name: The name of the kernel module on which to operate
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
|
||||
Yields:
|
||||
A _KTIMER entry
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
if versions.is_windows_7(
|
||||
context=context, symbol_table=symbol_table
|
||||
) or versions.is_windows_8_or_later(context=context, symbol_table=symbol_table):
|
||||
# Starting with Windows 7, there is no more KiTimerTableListHead. The list is
|
||||
# at _KPCR.PrcbData.TimerTable.TimerEntries
|
||||
# See http://pastebin.com/FiRsGW3f
|
||||
for kpcr in kpcrs.KPCRs.list_kpcrs(
|
||||
context, kernel_module_name, layer_name, symbol_table
|
||||
):
|
||||
if hasattr(kpcr.Prcb.TimerTable, "TableState"):
|
||||
for timer_entries in kpcr.Prcb.TimerTable.TimerEntries:
|
||||
for timer_entry in timer_entries:
|
||||
for timer in timer_entry.Entry.to_list(
|
||||
symbol_table + constants.BANG + "_KTIMER",
|
||||
"TimerListEntry",
|
||||
):
|
||||
yield timer
|
||||
|
||||
else:
|
||||
for timer_entries in kpcr.Prcb.TimerTable.TimerEntries:
|
||||
for timer in timer_entries.Entry.to_list(
|
||||
symbol_table + constants.BANG + "_KTIMER",
|
||||
"TimerListEntry",
|
||||
):
|
||||
yield timer
|
||||
|
||||
elif versions.is_xp_or_2003(
|
||||
context=context, symbol_table=symbol_table
|
||||
) or versions.is_vista_or_later(context=context, symbol_table=symbol_table):
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
if is_64bit or versions.is_vista_or_later(
|
||||
context=context, symbol_table=symbol_table
|
||||
):
|
||||
# On XP x64, Windows 2003 SP1-SP2, and Vista SP0-SP2, KiTimerTableListHead
|
||||
# is an array of 512 _KTIMER_TABLE_ENTRY structs.
|
||||
array_size = 512
|
||||
else:
|
||||
# On XP SP0-SP3 x86 and Windows 2003 SP0, KiTimerTableListHead
|
||||
# is an array of 256 _LIST_ENTRY for _KTIMERs.
|
||||
array_size = 256
|
||||
|
||||
timer_table_list_head = kernel.object(
|
||||
object_type="array",
|
||||
offset=kernel.get_symbol("KiTimerTableListHead").address,
|
||||
subtype=kernel.get_type("_LIST_ENTRY"),
|
||||
count=array_size,
|
||||
)
|
||||
for table in timer_table_list_head:
|
||||
for timer in table.to_list(
|
||||
symbol_table + constants.BANG + "_KTIMER",
|
||||
"TimerListEntry",
|
||||
):
|
||||
yield timer
|
||||
|
||||
else:
|
||||
raise NotImplementedError("This version of Windows is not supported!")
|
||||
|
||||
def _generator(self) -> Iterator[Tuple]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
layer_name = kernel.layer_name
|
||||
symbol_table = kernel.symbol_table_name
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
|
||||
for timer in self.list_timers(
|
||||
self.context, self.config["kernel"], layer_name, symbol_table
|
||||
):
|
||||
if not timer.valid_type():
|
||||
continue
|
||||
try:
|
||||
dpc = timer.get_dpc()
|
||||
if dpc == 0:
|
||||
continue
|
||||
if dpc.DeferredRoutine == 0:
|
||||
continue
|
||||
deferred_routine = dpc.DeferredRoutine
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
module_symbols = list(
|
||||
collection.get_module_symbols_by_absolute_location(deferred_routine)
|
||||
)
|
||||
|
||||
if module_symbols:
|
||||
for module_name, symbol_generator in module_symbols:
|
||||
symbols_found = False
|
||||
|
||||
# we might have multiple symbols pointing to the same location
|
||||
for symbol in symbol_generator:
|
||||
symbols_found = True
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(timer.vol.offset),
|
||||
timer.get_due_time(),
|
||||
timer.Period,
|
||||
timer.get_signaled(),
|
||||
format_hints.Hex(deferred_routine),
|
||||
module_name,
|
||||
symbol.split(constants.BANG)[1],
|
||||
),
|
||||
)
|
||||
|
||||
# no symbols, but we at least can report the module name
|
||||
if not symbols_found:
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(timer.vol.offset),
|
||||
timer.get_due_time(),
|
||||
timer.Period,
|
||||
timer.get_signaled(),
|
||||
format_hints.Hex(deferred_routine),
|
||||
module_name,
|
||||
renderers.NotAvailableValue(),
|
||||
),
|
||||
)
|
||||
else:
|
||||
# no module was found at the absolute location
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(timer.vol.offset),
|
||||
timer.get_due_time(),
|
||||
timer.Period,
|
||||
timer.get_signaled(),
|
||||
format_hints.Hex(deferred_routine),
|
||||
renderers.NotAvailableValue(),
|
||||
renderers.NotAvailableValue(),
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
("DueTime", str),
|
||||
("Period(ms)", int),
|
||||
("Signaled", str),
|
||||
("Routine", format_hints.Hex),
|
||||
("Module", str),
|
||||
("Symbol", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -33,7 +33,7 @@ class Passphrase(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(1, 1, 0)
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="min-length",
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# This file is Copyright 2024 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
|
||||
import datetime
|
||||
from typing import List, Iterable
|
||||
|
||||
from volatility3.framework import constants
|
||||
from volatility3.framework import interfaces, symbols
|
||||
from volatility3.framework import renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import configuration
|
||||
from volatility3.framework.renderers import format_hints, conversion
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.plugins import timeliner
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Lists the unloaded kernel modules."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def create_unloadedmodules_table(
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Creates a symbol table for the unloaded modules.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
config_path: The configuration path within the context of the symbol table to create
|
||||
|
||||
Returns:
|
||||
The name of the constructed unloaded modules table
|
||||
"""
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
|
||||
if is_64bit:
|
||||
symbol_filename = "unloadedmodules-x64"
|
||||
else:
|
||||
symbol_filename = "unloadedmodules-x86"
|
||||
|
||||
return intermed.IntermediateSymbolTable.create(
|
||||
context,
|
||||
configuration.path_join(config_path, "unloadedmodules"),
|
||||
"windows",
|
||||
symbol_filename,
|
||||
native_types=native_types,
|
||||
table_mapping=table_mapping,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def list_unloadedmodules(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
unloadedmodule_table_name: str,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the unloaded modules in the primary layer.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
|
||||
Returns:
|
||||
A list of Unloaded Modules as retrieved from MmUnloadedDrivers
|
||||
"""
|
||||
|
||||
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
|
||||
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
|
||||
unloadedmodules_offset = ntkrnlmp.get_symbol("MmUnloadedDrivers").address
|
||||
unloadedmodules = ntkrnlmp.object(
|
||||
object_type="pointer",
|
||||
offset=unloadedmodules_offset,
|
||||
subtype="array",
|
||||
)
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
|
||||
if is_64bit:
|
||||
unloaded_count_type = "unsigned long long"
|
||||
else:
|
||||
unloaded_count_type = "unsigned long"
|
||||
|
||||
last_unloadedmodule_offset = ntkrnlmp.get_symbol("MmLastUnloadedDriver").address
|
||||
unloaded_count = ntkrnlmp.object(
|
||||
object_type=unloaded_count_type, offset=last_unloadedmodule_offset
|
||||
)
|
||||
|
||||
unloadedmodules_array = context.object(
|
||||
object_type=unloadedmodule_table_name
|
||||
+ constants.BANG
|
||||
+ "_UNLOADED_DRIVERS",
|
||||
layer_name=layer_name,
|
||||
offset=unloadedmodules,
|
||||
)
|
||||
unloadedmodules_array.UnloadedDrivers.count = unloaded_count
|
||||
|
||||
for mod in unloadedmodules_array.UnloadedDrivers:
|
||||
yield mod
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
unloadedmodule_table_name = self.create_unloadedmodules_table(
|
||||
self.context, kernel.symbol_table_name, self.config_path
|
||||
)
|
||||
|
||||
for mod in self.list_unloadedmodules(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
unloadedmodule_table_name,
|
||||
):
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
mod.Name.String,
|
||||
format_hints.Hex(mod.StartAddress),
|
||||
format_hints.Hex(mod.EndAddress),
|
||||
conversion.wintime_to_datetime(mod.CurrentTime),
|
||||
),
|
||||
)
|
||||
|
||||
def generate_timeline(self):
|
||||
for row in self._generator():
|
||||
_depth, row_data = row
|
||||
description = f"Unloaded Module: {row_data[0]}"
|
||||
yield (description, timeliner.TimeLinerType.CHANGED, row_data[3])
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Name", str),
|
||||
("StartAddress", format_hints.Hex),
|
||||
("EndAddress", format_hints.Hex),
|
||||
("Time", datetime.datetime),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -18,7 +18,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 +32,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 +56,8 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
sanity_check = 1024 * 1024 * 1024 # 1 GB
|
||||
|
||||
for task in pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
@@ -67,18 +66,33 @@ 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, size in self.get_vad_maps(task):
|
||||
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, size, 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(
|
||||
@@ -91,7 +105,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
task: The EPROCESS object of which to traverse the vad tree
|
||||
|
||||
Returns:
|
||||
An iterable of tuples containing start and end addresses for each descriptor
|
||||
An iterable of tuples containing start and size for each descriptor
|
||||
"""
|
||||
vad_root = task.get_vad_root()
|
||||
for vad in vad_root.traverse():
|
||||
|
||||
@@ -78,7 +78,7 @@ class VirtMap(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
else:
|
||||
raise exceptions.SymbolError(
|
||||
None, module.name, "Required structures not found"
|
||||
"SystemVaRegions", module.name, "Required structures not found"
|
||||
)
|
||||
elif module.has_symbol("MiSystemVaType"):
|
||||
system_range_start = module.object(
|
||||
@@ -99,7 +99,7 @@ class VirtMap(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
else:
|
||||
raise exceptions.SymbolError(
|
||||
None, module.name, "Required structures not found"
|
||||
"MiVisibleState", module.name, "Required structures not found"
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
@@ -43,7 +43,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 +61,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 +119,14 @@ class YaraScan(plugins.PluginInterface):
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def yara_returns_instances(cls) -> 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
|
||||
|
||||
@@ -19,9 +19,11 @@ def wintime_to_datetime(
|
||||
return renderers.NotApplicableValue()
|
||||
unix_time = unix_time - 11644473600
|
||||
try:
|
||||
return datetime.datetime.utcfromtimestamp(unix_time)
|
||||
# Windows sometimes throws OSErrors rather than ValueErrors when it can't convert a value
|
||||
except (ValueError, OSError):
|
||||
return datetime.datetime.fromtimestamp(unix_time, datetime.timezone.utc)
|
||||
# Windows sometimes throws OSErrors rather than ValueError/OverflowError when it can't convert a value
|
||||
# Since Python 3.3, this should raise OverflowError instead of ValueError. However, it was observed
|
||||
# that even in Python 3.7.17, ValueError is still being raised.
|
||||
except (ValueError, OverflowError, OSError):
|
||||
return renderers.UnparsableValue()
|
||||
|
||||
|
||||
@@ -33,8 +35,10 @@ def unixtime_to_datetime(
|
||||
)
|
||||
|
||||
if unixtime > 0:
|
||||
with contextlib.suppress(ValueError):
|
||||
ret = datetime.datetime.utcfromtimestamp(unixtime)
|
||||
# Since Python 3.3, this should raise OverflowError instead of ValueError. However, it was observed
|
||||
# that even in Python 3.7.17, ValueError is still being raised. OSError is also raised on Linux
|
||||
with contextlib.suppress(ValueError, OverflowError, OSError):
|
||||
ret = datetime.datetime.fromtimestamp(unixtime, datetime.timezone.utc)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ Text renderers should attempt to honour all hints provided in this module where
|
||||
"""
|
||||
from typing import Type, Union
|
||||
|
||||
from volatility3.framework import interfaces
|
||||
|
||||
|
||||
class Bin(int):
|
||||
"""A class to indicate that the integer value should be represented as a
|
||||
@@ -66,3 +68,17 @@ class MultiTypeData(bytes):
|
||||
and self.split_nulls == other.split_nulls
|
||||
and self.show_hex == other.show_hex
|
||||
)
|
||||
|
||||
|
||||
BinOrAbsent = lambda x: (
|
||||
Bin(x) if not isinstance(x, interfaces.renderers.BaseAbsentValue) else x
|
||||
)
|
||||
HexOrAbsent = lambda x: (
|
||||
Hex(x) if not isinstance(x, interfaces.renderers.BaseAbsentValue) else x
|
||||
)
|
||||
HexBytesOrAbsent = lambda x: (
|
||||
HexBytes(x) if not isinstance(x, interfaces.renderers.BaseAbsentValue) else x
|
||||
)
|
||||
MultiTypeDataOrAbsent = lambda x: (
|
||||
MultiTypeData(x) if not isinstance(x, interfaces.renderers.BaseAbsentValue) else x
|
||||
)
|
||||
|
||||
@@ -282,7 +282,7 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface):
|
||||
urls = list(cls.file_symbol_url(sub_path, filename))
|
||||
if not urls:
|
||||
raise FileNotFoundError(
|
||||
"No symbol files found at provided filename: {}", filename
|
||||
f"No symbol files found at provided filename: {filename}",
|
||||
)
|
||||
table_name = context.symbol_space.free_table_name(filename)
|
||||
table = cls(
|
||||
|
||||
@@ -29,12 +29,18 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
self.set_type_class("files_struct", extensions.files_struct)
|
||||
self.set_type_class("kobject", extensions.kobject)
|
||||
self.set_type_class("cred", extensions.cred)
|
||||
self.set_type_class("inode", extensions.inode)
|
||||
# Might not exist in the current symbols
|
||||
self.optional_set_type_class("module", extensions.module)
|
||||
self.optional_set_type_class("bpf_prog", extensions.bpf_prog)
|
||||
self.optional_set_type_class("kernel_cap_struct", extensions.kernel_cap_struct)
|
||||
self.optional_set_type_class("kernel_cap_t", extensions.kernel_cap_t)
|
||||
|
||||
# kernels >= 4.18
|
||||
self.optional_set_type_class("timespec64", extensions.timespec64)
|
||||
# kernels < 4.18. Reuses timespec64 obj extension, since both has the same members
|
||||
self.optional_set_type_class("timespec", extensions.timespec64)
|
||||
|
||||
# Mount
|
||||
self.set_type_class("vfsmount", extensions.vfsmount)
|
||||
# Might not exist in older kernels or the current symbols
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
import collections.abc
|
||||
import logging
|
||||
import stat
|
||||
from datetime import datetime
|
||||
import socket as socket_module
|
||||
from typing import Generator, Iterable, Iterator, Optional, Tuple, List
|
||||
from typing import Generator, Iterable, Iterator, Optional, Tuple, List, Union
|
||||
|
||||
from volatility3.framework import constants, exceptions, objects, interfaces, symbols
|
||||
from volatility3.framework.renderers import conversion
|
||||
from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY
|
||||
from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS
|
||||
from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS
|
||||
@@ -1761,3 +1764,136 @@ class kernel_cap_t(kernel_cap_struct):
|
||||
)
|
||||
|
||||
return cap_value & self.get_kernel_cap_full()
|
||||
|
||||
|
||||
class timespec64(objects.StructType):
|
||||
def to_datetime(self) -> datetime:
|
||||
"""Returns the respective aware datetime"""
|
||||
|
||||
dt = conversion.unixtime_to_datetime(self.tv_sec + self.tv_nsec / 1e9)
|
||||
return dt
|
||||
|
||||
|
||||
class inode(objects.StructType):
|
||||
def is_valid(self) -> bool:
|
||||
# i_count is a 'signed' counter (atomic_t). Smear, or essentially a wrong inode
|
||||
# pointer, will easily cause an integer overflow here.
|
||||
return self.i_ino > 0 and self.i_count.counter >= 0
|
||||
|
||||
@property
|
||||
def is_dir(self) -> bool:
|
||||
"""Returns True if the inode is a directory"""
|
||||
return stat.S_ISDIR(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_reg(self) -> bool:
|
||||
"""Returns True if the inode is a regular file"""
|
||||
return stat.S_ISREG(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_link(self) -> bool:
|
||||
"""Returns True if the inode is a symlink"""
|
||||
return stat.S_ISLNK(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_fifo(self) -> bool:
|
||||
"""Returns True if the inode is a FIFO"""
|
||||
return stat.S_ISFIFO(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_sock(self) -> bool:
|
||||
"""Returns True if the inode is a socket"""
|
||||
return stat.S_ISSOCK(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_block(self) -> bool:
|
||||
"""Returns True if the inode is a block device"""
|
||||
return stat.S_ISBLK(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_char(self) -> bool:
|
||||
"""Returns True if the inode is a char device"""
|
||||
return stat.S_ISCHR(self.i_mode) != 0
|
||||
|
||||
@property
|
||||
def is_sticky(self) -> bool:
|
||||
"""Returns True if the sticky bit is set"""
|
||||
return (self.i_mode & stat.S_ISVTX) != 0
|
||||
|
||||
def get_inode_type(self) -> Union[str, None]:
|
||||
"""Returns inode type name
|
||||
|
||||
Returns:
|
||||
The inode type name
|
||||
"""
|
||||
if self.is_dir:
|
||||
return "DIR"
|
||||
elif self.is_reg:
|
||||
return "REG"
|
||||
elif self.is_link:
|
||||
return "LNK"
|
||||
elif self.is_fifo:
|
||||
return "FIFO"
|
||||
elif self.is_sock:
|
||||
return "SOCK"
|
||||
elif self.is_char:
|
||||
return "CHR"
|
||||
elif self.is_block:
|
||||
return "BLK"
|
||||
else:
|
||||
return None
|
||||
|
||||
def _time_member_to_datetime(self, member) -> datetime:
|
||||
if self.has_member(f"{member}_sec") and self.has_member(f"{member}_nsec"):
|
||||
# kernels >= 6.11 it's i_*_sec -> time64_t and i_*_nsec -> u32
|
||||
# Ref Linux commit 3aa63a569c64e708df547a8913c84e64a06e7853
|
||||
return conversion.unixtime_to_datetime(
|
||||
self.member(f"{member}_sec") + self.has_member(f"{member}_nsec") / 1e9
|
||||
)
|
||||
elif self.has_member(f"__{member}"):
|
||||
# 6.6 <= kernels < 6.11 it's a timespec64
|
||||
# Ref Linux commit 13bc24457850583a2e7203ded05b7209ab4bc5ef / 12cd44023651666bd44baa36a5c999698890debb
|
||||
return self.member(f"__{member}").to_datetime()
|
||||
elif self.has_member(member):
|
||||
# In kernels < 6.6 it's a timespec64 or timespec
|
||||
return self.member(member).to_datetime()
|
||||
else:
|
||||
raise exceptions.VolatilityException(
|
||||
"Unsupported kernel inode type implementation"
|
||||
)
|
||||
|
||||
def get_access_time(self) -> datetime:
|
||||
"""Returns the inode's last access time
|
||||
This is updated when inode contents are read
|
||||
|
||||
Returns:
|
||||
A datetime with the inode's last access time
|
||||
"""
|
||||
return self._time_member_to_datetime("i_atime")
|
||||
|
||||
def get_modification_time(self) -> datetime:
|
||||
"""Returns the inode's last modification time
|
||||
This is updated when the inode contents change
|
||||
|
||||
Returns:
|
||||
A datetime with the inode's last data modification time
|
||||
"""
|
||||
|
||||
return self._time_member_to_datetime("i_mtime")
|
||||
|
||||
def get_change_time(self) -> datetime:
|
||||
"""Returns the inode's last change time
|
||||
This is updated when the inode metadata changes
|
||||
|
||||
Returns:
|
||||
A datetime with the inode's last change time
|
||||
"""
|
||||
return self._time_member_to_datetime("i_ctime")
|
||||
|
||||
def get_file_mode(self) -> str:
|
||||
"""Returns the inode's file mode as string of the form '-rwxrwxrwx'.
|
||||
|
||||
Returns:
|
||||
The inode's file mode string
|
||||
"""
|
||||
return stat.filemode(self.i_mode)
|
||||
|
||||
@@ -17,6 +17,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
self.set_type_class("_KTHREAD", extensions.KTHREAD)
|
||||
self.set_type_class("_LIST_ENTRY", extensions.LIST_ENTRY)
|
||||
self.set_type_class("_EPROCESS", extensions.EPROCESS)
|
||||
self.set_type_class("_ERESOURCE", extensions.ERESOURCE)
|
||||
self.set_type_class("_UNICODE_STRING", extensions.UNICODE_STRING)
|
||||
self.set_type_class("_EX_FAST_REF", extensions.EX_FAST_REF)
|
||||
self.set_type_class("_TOKEN", extensions.TOKEN)
|
||||
@@ -39,6 +40,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
self.set_type_class("_VACB", extensions.VACB)
|
||||
self.set_type_class("_POOL_TRACKER_BIG_PAGES", pool.POOL_TRACKER_BIG_PAGES)
|
||||
self.set_type_class("_IMAGE_DOS_HEADER", pe.IMAGE_DOS_HEADER)
|
||||
self.set_type_class("_KTIMER", extensions.KTIMER)
|
||||
|
||||
# Might not necessarily defined in every version of windows
|
||||
self.optional_set_type_class("_IMAGE_NT_HEADERS", pe.IMAGE_NT_HEADERS)
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"enums": {
|
||||
"EventCategory": {
|
||||
"base": "long",
|
||||
"constants": {
|
||||
"EventCategoryReserved": 0,
|
||||
"EventCategoryHardwareProfileChange": 1,
|
||||
"EventCategoryDeviceInterfaceChange": 2,
|
||||
"EventCategoryTargetDeviceChange": 3,
|
||||
"EventCategoryKernelSoftRestart": 4
|
||||
},
|
||||
"size": 4
|
||||
}
|
||||
},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
@@ -48,19 +60,164 @@
|
||||
"user_types": {
|
||||
"_GENERIC_CALLBACK": {
|
||||
"fields": {
|
||||
"Callback": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
"Callback": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
"Associated": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
"size": 24
|
||||
},
|
||||
"_NOTIFICATION_PACKET": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"DriverObject": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_DRIVER_OBJECT"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"NotificationRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 24
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 48
|
||||
},
|
||||
"_SHUTDOWN_PACKET": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"DeviceObject": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_DEVICE_OBJECT"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 24
|
||||
},
|
||||
"_DBGPRINT_CALLBACK": {
|
||||
"fields": {
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 24
|
||||
},
|
||||
"_NOTIFY_ENTRY_HEADER": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"EventCategory": {
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"name": "EventCategory"
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"CallbackRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DriverObject": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_DRIVER_OBJECT"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
},
|
||||
"_REGISTRY_CALLBACK": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 48
|
||||
},
|
||||
"_KBUGCHECK_CALLBACK_RECORD": {
|
||||
"fields": {
|
||||
@@ -184,10 +341,10 @@
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "mhl by hand",
|
||||
"datetime": "2019-08-27T18:17:16.417006"
|
||||
"version": "0.0.2",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-02-02T19:32:00.000000"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"enums": {
|
||||
"EventCategory": {
|
||||
"base": "long",
|
||||
"constants": {
|
||||
"EventCategoryReserved": 0,
|
||||
"EventCategoryHardwareProfileChange": 1,
|
||||
"EventCategoryDeviceInterfaceChange": 2,
|
||||
"EventCategoryTargetDeviceChange": 3,
|
||||
"EventCategoryKernelSoftRestart": 4
|
||||
},
|
||||
"size": 4
|
||||
}
|
||||
},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
@@ -46,6 +58,151 @@
|
||||
}
|
||||
},
|
||||
"user_types": {
|
||||
"_NOTIFICATION_PACKET": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"DriverObject": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_DRIVER_OBJECT"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"NotificationRoutine": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_SHUTDOWN_PACKET": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"DeviceObject": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_DEVICE_OBJECT"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 12
|
||||
},
|
||||
"_REGISTRY_CALLBACK": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 28
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_REGISTRY_CALLBACK_LEGACY": {
|
||||
"fields": {
|
||||
"CreateTime": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 28
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_DBGPRINT_CALLBACK": {
|
||||
"fields": {
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 20
|
||||
},
|
||||
"_NOTIFY_ENTRY_HEADER": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"EventCategory": {
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"name": "EventCategory"
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"CallbackRoutine": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"DriverObject": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_DRIVER_OBJECT"
|
||||
}
|
||||
},
|
||||
"offset": 28
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_GENERIC_CALLBACK": {
|
||||
"fields": {
|
||||
"Callback": {
|
||||
@@ -57,10 +214,20 @@
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"Associated": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
"size": 12
|
||||
},
|
||||
"_KBUGCHECK_CALLBACK_RECORD": {
|
||||
"fields": {
|
||||
@@ -184,10 +351,10 @@
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "mhl by hand",
|
||||
"datetime": "2019-08-27T18:17:16.417006"
|
||||
"version": "0.0.2",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-02-02T19:32:00.000000"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ from volatility3.framework import (
|
||||
)
|
||||
from volatility3.framework.interfaces.objects import ObjectInterface
|
||||
from volatility3.framework.layers import intel
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import conversion
|
||||
from volatility3.framework.symbols import generic
|
||||
from volatility3.framework.symbols.windows.extensions import kdbg, pe, pool
|
||||
from volatility3.framework.symbols.windows.extensions import pool
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -306,16 +307,19 @@ class MMVAD_SHORT(objects.StructType):
|
||||
|
||||
raise AttributeError("Unable to find the private memory member")
|
||||
|
||||
@property
|
||||
def Protection(self):
|
||||
if self.has_member("u"):
|
||||
return self.u.VadFlags.Protection
|
||||
elif self.has_member("Core"):
|
||||
return self.Core.u.VadFlags.Protection
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_protection(self, protect_values, winnt_protections):
|
||||
"""Get the VAD's protection constants as a string."""
|
||||
|
||||
protect = None
|
||||
|
||||
if self.has_member("u"):
|
||||
protect = self.u.VadFlags.Protection
|
||||
|
||||
elif self.has_member("Core"):
|
||||
protect = self.Core.u.VadFlags.Protection
|
||||
protect = self.Protection
|
||||
|
||||
try:
|
||||
value = protect_values[protect]
|
||||
@@ -593,6 +597,38 @@ class UNICODE_STRING(objects.StructType):
|
||||
String = property(get_string)
|
||||
|
||||
|
||||
class ERESOURCE(objects.StructType):
|
||||
def is_valid(self) -> bool:
|
||||
vollog.debug(f"Checking ERESOURCE Validity: {hex(self.vol.offset)}")
|
||||
|
||||
if not self._context.layers[self.vol.layer_name].is_valid(self.vol.offset):
|
||||
return False
|
||||
|
||||
sym_table = self.get_symbol_table_name()
|
||||
|
||||
waiters_valid = self.SharedWaiters == 0 or self._context.layers[
|
||||
self.vol.layer_name
|
||||
].is_valid(
|
||||
self.SharedWaiters.vol.offset,
|
||||
self._context.symbol_space.get_type(
|
||||
sym_table + constants.BANG + "_KSEMAPHORE"
|
||||
).size,
|
||||
)
|
||||
|
||||
try:
|
||||
return (
|
||||
waiters_valid
|
||||
and self.SystemResourcesList.Flink is not None
|
||||
and self.SystemResourcesList.Blink is not None
|
||||
and self.SystemResourcesList.Flink != self.SystemResourcesList.Blink
|
||||
and self.SystemResourcesList.Flink.Blink == self.vol.offset
|
||||
and self.SystemResourcesList.Blink.Flink == self.vol.offset
|
||||
and self.NumberOfSharedWaiters == 0
|
||||
)
|
||||
except exceptions.InvalidAddressException:
|
||||
return False
|
||||
|
||||
|
||||
class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject):
|
||||
"""A class for executive kernel processes objects."""
|
||||
|
||||
@@ -611,11 +647,23 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject):
|
||||
|
||||
ctime = self.get_create_time()
|
||||
if not isinstance(ctime, datetime.datetime):
|
||||
# A process must have a creation time
|
||||
return False
|
||||
|
||||
if not (1998 < ctime.year < 2030):
|
||||
current_year = datetime.datetime.now().year
|
||||
if not (1998 < ctime.year < current_year + 10):
|
||||
return False
|
||||
|
||||
etime = self.get_exit_time()
|
||||
if isinstance(etime, datetime.datetime):
|
||||
if not (1998 < etime.year < current_year + 10):
|
||||
return False
|
||||
|
||||
# Exit time, if available, must be after the creation time
|
||||
# At this point, we are sure both are datetimes, so let's compare them
|
||||
if ctime > etime:
|
||||
return False
|
||||
|
||||
# NT pids are divisible by 4
|
||||
if self.UniqueProcessId % 4 != 0:
|
||||
return False
|
||||
@@ -633,7 +681,11 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject):
|
||||
if dtb & ~0xFFF == 0:
|
||||
return False
|
||||
|
||||
## TODO: we can also add the thread Flink and Blink tests if necessary
|
||||
# Quick smear test on thread Flink and Blink
|
||||
kernel = 0x80000000 # Yes, it's a quick test
|
||||
list_head = self.ThreadListHead
|
||||
if list_head.Flink < kernel or list_head.Blink < kernel:
|
||||
return False
|
||||
|
||||
except exceptions.InvalidAddressException:
|
||||
return False
|
||||
@@ -994,6 +1046,83 @@ class TOKEN(objects.StructType):
|
||||
vollog.log(constants.LOGLEVEL_VVVV, "Broken Token Privileges.")
|
||||
|
||||
|
||||
class KTIMER(objects.StructType):
|
||||
"""A class for Kernel Timers"""
|
||||
|
||||
VALID_TYPES = {
|
||||
8: "TimerNotificationObject",
|
||||
9: "TimerSynchronizationObject",
|
||||
}
|
||||
|
||||
def get_signaled(self):
|
||||
if self.Header.SignalState:
|
||||
return "Yes"
|
||||
return "-"
|
||||
|
||||
def get_raw_dpc(self):
|
||||
"""Returns the encoded DPC since it may not look like a pointer after encoding"""
|
||||
symbol_table_name = self.get_symbol_table_name()
|
||||
pointer_type = self._context.symbol_space.get_type(
|
||||
symbol_table_name + constants.BANG + "pointer"
|
||||
)
|
||||
|
||||
return self._context.object(
|
||||
object_type=pointer_type,
|
||||
layer_name=self.vol.layer_name,
|
||||
offset=self.Dpc.vol.offset,
|
||||
)
|
||||
|
||||
def valid_type(self):
|
||||
return self.Header.Type in self.VALID_TYPES
|
||||
|
||||
def get_due_time(self):
|
||||
return "{0:#010x}:{1:#010x}".format(self.DueTime.HighPart, self.DueTime.LowPart)
|
||||
|
||||
def get_dpc(self):
|
||||
"""Return Dpc, and if Windows 7 or later, decode it"""
|
||||
symbol_table_name = self.get_symbol_table_name()
|
||||
kvo = self._context.layers[self.vol.native_layer_name].config[
|
||||
"kernel_virtual_offset"
|
||||
]
|
||||
ntkrnlmp = self._context.module(
|
||||
symbol_table_name,
|
||||
layer_name=self.vol.native_layer_name,
|
||||
offset=kvo,
|
||||
native_layer_name=self.vol.native_layer_name,
|
||||
)
|
||||
|
||||
if ntkrnlmp.has_symbol("KiWaitNever") and ntkrnlmp.has_symbol("KiWaitAlways"):
|
||||
wait_never = ntkrnlmp.object(
|
||||
object_type="unsigned long long",
|
||||
offset=ntkrnlmp.get_symbol("KiWaitNever").address,
|
||||
)
|
||||
wait_always = ntkrnlmp.object(
|
||||
object_type="unsigned long long",
|
||||
offset=ntkrnlmp.get_symbol("KiWaitAlways").address,
|
||||
)
|
||||
|
||||
low_byte = (wait_never) & 0xFF
|
||||
entry = utility.rol(self.get_raw_dpc() ^ wait_never, low_byte)
|
||||
swap_xor = self._context.layers[self.vol.native_layer_name].canonicalize(
|
||||
self.vol.offset
|
||||
)
|
||||
entry = utility.bswap_64(entry ^ swap_xor)
|
||||
dpc = entry ^ wait_always
|
||||
|
||||
symbol_table_name = self.get_symbol_table_name()
|
||||
kdpc_type = self._context.symbol_space.get_type(
|
||||
symbol_table_name + constants.BANG + "_KDPC"
|
||||
)
|
||||
|
||||
return self._context.object(
|
||||
object_type=kdpc_type,
|
||||
layer_name=self.vol.layer_name,
|
||||
offset=dpc,
|
||||
)
|
||||
else:
|
||||
return self.Dpc
|
||||
|
||||
|
||||
class KTHREAD(objects.StructType):
|
||||
"""A class for thread control block objects."""
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import logging
|
||||
|
||||
from volatility3.framework import exceptions, objects
|
||||
from volatility3.framework.symbols.windows.extensions import pool
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _SHUTDOWN_PACKET(objects.StructType, pool.ExecutiveObject):
|
||||
"""Class for _SHUTDOWN_PACKET objects found in IoSh pools.
|
||||
|
||||
This class serves as a base class for all pooled shutdown callback packets.
|
||||
|
||||
It exposes a function which sanity-checks structure members.
|
||||
"""
|
||||
|
||||
def is_valid(self) -> bool:
|
||||
"""
|
||||
Perform some checks.
|
||||
"""
|
||||
try:
|
||||
if not (
|
||||
self.Entry.Flink.is_readable()
|
||||
and self.Entry.Blink.is_readable()
|
||||
and self.DeviceObject.is_readable()
|
||||
):
|
||||
return False
|
||||
|
||||
device = self.DeviceObject
|
||||
if not device or not (device.DriverObject.DriverStart % 0x1000 == 0):
|
||||
vollog.debug(
|
||||
f"callback obj 0x{self.vol.offset:x} invalid due to invalid device object"
|
||||
)
|
||||
return False
|
||||
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.debug(
|
||||
f"callback obj 0x{self.vol.offset:x} invalid due to invalid address access"
|
||||
)
|
||||
return False
|
||||
|
||||
try:
|
||||
header = device.get_object_header()
|
||||
valid = header.NameInfo.Name == "Device"
|
||||
return valid
|
||||
except ValueError:
|
||||
vollog.debug(f"Could not get NameInfo for object at 0x{self.vol.offset:x}")
|
||||
return False
|
||||
|
||||
|
||||
class_types_x86 = {"_SHUTDOWN_PACKET": _SHUTDOWN_PACKET}
|
||||
@@ -0,0 +1,278 @@
|
||||
# This file is Copyright 2019 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
|
||||
import struct
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional, Tuple, Union
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, objects, renderers
|
||||
from volatility3.framework.symbols.windows.extensions import conversion
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SHIM_CACHE_ENTRY(objects.StructType):
|
||||
"""Class for abstracting variations in the shimcache LRU list entry structure"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
size: int,
|
||||
members: Dict[str, Tuple[int, interfaces.objects.Template]],
|
||||
) -> None:
|
||||
super().__init__(context, type_name, object_info, size, members)
|
||||
self._exec_flag = None
|
||||
self._file_path = None
|
||||
self._file_size = None
|
||||
self._last_modified = None
|
||||
self._last_updated = None
|
||||
|
||||
@property
|
||||
def exec_flag(self) -> Union[bool, interfaces.renderers.BaseAbsentValue]:
|
||||
"""Checks if InsertFlags fields has been bitwise OR'd with a value of 2.
|
||||
This behavior was observed when processes are created by CSRSS."""
|
||||
if self._exec_flag is not None:
|
||||
return self._exec_flag
|
||||
|
||||
if hasattr(self, "ListEntryDetail") and hasattr(
|
||||
self.ListEntryDetail, "InsertFlags"
|
||||
):
|
||||
self._exec_flag = self.ListEntryDetail.InsertFlags & 0x2 == 2
|
||||
|
||||
elif hasattr(self, "InsertFlags"):
|
||||
self._exec_flag = self.InsertFlags & 0x2 == 2
|
||||
|
||||
elif hasattr(self, "ListEntryDetail") and hasattr(
|
||||
self.ListEntryDetail, "BlobBuffer"
|
||||
):
|
||||
blob_offset = self.ListEntryDetail.BlobBuffer
|
||||
blob_size = self.ListEntryDetail.BlobSize
|
||||
|
||||
if not self._context.layers[self.vol.native_layer_name].is_valid(
|
||||
blob_offset, blob_size
|
||||
):
|
||||
self._exec_flag = renderers.UnparsableValue()
|
||||
|
||||
raw_flag = self._context.layers[self.vol.native_layer_name].read(
|
||||
blob_offset, blob_size
|
||||
)
|
||||
if not raw_flag:
|
||||
self._exec_flag = renderers.UnparsableValue()
|
||||
|
||||
try:
|
||||
self._exec_flag = bool(struct.unpack("<I", raw_flag)[0])
|
||||
except struct.error:
|
||||
self._exec_flag = renderers.UnparsableValue()
|
||||
|
||||
else:
|
||||
# Always set to true for XP/2K3
|
||||
self._exec_flag = renderers.NotApplicableValue()
|
||||
return self._exec_flag
|
||||
|
||||
@property
|
||||
def file_size(self) -> Union[int, interfaces.renderers.BaseAbsentValue]:
|
||||
if self._file_size is not None:
|
||||
return self._file_size
|
||||
try:
|
||||
self._file_size = self.FileSize
|
||||
if self._file_size < 0:
|
||||
self._file_size = 0
|
||||
|
||||
except AttributeError:
|
||||
self._file_size = renderers.NotApplicableValue()
|
||||
except exceptions.InvalidAddressException:
|
||||
self._file_size = renderers.UnreadableValue()
|
||||
|
||||
return self._file_size
|
||||
|
||||
@property
|
||||
def last_modified(self) -> Union[datetime, interfaces.renderers.BaseAbsentValue]:
|
||||
if self._last_modified is not None:
|
||||
return self._last_modified
|
||||
try:
|
||||
self._last_modified = conversion.wintime_to_datetime(
|
||||
self.ListEntryDetail.LastModified.QuadPart
|
||||
)
|
||||
except AttributeError:
|
||||
self._last_modified = conversion.wintime_to_datetime(
|
||||
self.LastModified.QuadPart
|
||||
)
|
||||
except exceptions.InvalidAddressException:
|
||||
self._last_modified = renderers.UnreadableValue()
|
||||
|
||||
return self._last_modified
|
||||
|
||||
@property
|
||||
def last_update(self) -> Union[datetime, interfaces.renderers.BaseAbsentValue]:
|
||||
if self._last_updated is not None:
|
||||
return self._last_updated
|
||||
|
||||
try:
|
||||
self._last_updated = conversion.wintime_to_datetime(
|
||||
self.LastUpdate.QuadPart
|
||||
)
|
||||
except AttributeError:
|
||||
self._last_updated = renderers.NotApplicableValue()
|
||||
|
||||
return self._last_updated
|
||||
|
||||
@property
|
||||
def file_path(self) -> Union[str, interfaces.renderers.BaseAbsentValue]:
|
||||
if self._file_path is not None:
|
||||
return self._file_path
|
||||
|
||||
if not hasattr(self.Path, "Buffer"):
|
||||
return self.Path.cast(
|
||||
"string", max_length=self.Path.vol.count, encoding="utf-16le"
|
||||
)
|
||||
|
||||
try:
|
||||
file_path_raw = (
|
||||
self._context.layers[self.vol.native_layer_name].read(
|
||||
self.Path.Buffer, self.Path.Length
|
||||
)
|
||||
or b""
|
||||
)
|
||||
self._file_path = file_path_raw.decode("utf-16", errors="replace")
|
||||
except exceptions.InvalidAddressException:
|
||||
self._file_path = renderers.UnreadableValue()
|
||||
|
||||
return self._file_path
|
||||
|
||||
def is_valid(self) -> bool:
|
||||
"""Shim cache validation is limited to ensuring that a subset of the
|
||||
pointers in the LIST_ENTRY field are valid (similar to validation of
|
||||
ERESOURCE)"""
|
||||
|
||||
# shim entries on Windows XP do not have list entry attributes; in this case,
|
||||
# perform a different set of validations
|
||||
try:
|
||||
if not hasattr(self, "ListEntry"):
|
||||
return bool(self.last_modified and self.last_update and self.file_size)
|
||||
|
||||
# on some platforms ListEntry.Blink is null, so this cannot be validated
|
||||
if (
|
||||
self.ListEntry.Flink != 0
|
||||
and (
|
||||
self.ListEntry.Blink.dereference()
|
||||
!= self.ListEntry.Flink.dereference()
|
||||
)
|
||||
and (
|
||||
self.ListEntry.Flink.Blink
|
||||
== self.ListEntry.Flink.Blink.dereference().vol.offset
|
||||
)
|
||||
):
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except exceptions.InvalidAddressException:
|
||||
return False
|
||||
|
||||
|
||||
class SHIM_CACHE_HANDLE(objects.StructType):
|
||||
def __init__(
|
||||
self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
type_name: str,
|
||||
object_info: interfaces.objects.ObjectInformation,
|
||||
size: int,
|
||||
members: Dict[str, Tuple[int, interfaces.objects.Template]],
|
||||
) -> None:
|
||||
super().__init__(context, type_name, object_info, size, members)
|
||||
|
||||
@property
|
||||
def head(self) -> Optional[SHIM_CACHE_ENTRY]:
|
||||
try:
|
||||
if not self.eresource.is_valid():
|
||||
return None
|
||||
except exceptions.InvalidAddressException:
|
||||
return None
|
||||
|
||||
rtl_avl_table = self._context.object(
|
||||
self.get_symbol_table_name() + constants.BANG + "_RTL_AVL_TABLE",
|
||||
self.vol.layer_name,
|
||||
self.rtl_avl_table,
|
||||
self.vol.native_layer_name,
|
||||
)
|
||||
|
||||
if not self._context.layers[self.vol.layer_name].is_valid(
|
||||
self.rtl_avl_table.vol.offset
|
||||
):
|
||||
return None
|
||||
|
||||
offset_head = rtl_avl_table.vol.offset + rtl_avl_table.vol.size
|
||||
|
||||
head = self._context.object(
|
||||
self.get_symbol_table_name() + constants.BANG + "SHIM_CACHE_ENTRY",
|
||||
self.vol.layer_name,
|
||||
offset_head,
|
||||
)
|
||||
|
||||
if not head.is_valid():
|
||||
return None
|
||||
|
||||
return head
|
||||
|
||||
def is_valid(self, avl_section_start: int, avl_section_end: int) -> bool:
|
||||
if self.vol.offset == 0:
|
||||
return False
|
||||
|
||||
vollog.debug(f"Checking SHIM_CACHE_HANDLE validity @ {hex(self.vol.offset)}")
|
||||
|
||||
if not (
|
||||
self._context.layers[self.vol.layer_name].is_valid(self.vol.offset)
|
||||
and self.eresource.is_valid()
|
||||
and self.rtl_avl_table.is_valid(avl_section_start, avl_section_end)
|
||||
and self.head
|
||||
):
|
||||
return False
|
||||
|
||||
return self.head.is_valid()
|
||||
|
||||
|
||||
class RTL_AVL_TABLE(objects.StructType):
|
||||
def is_valid(self, page_start: int, page_end: int) -> bool:
|
||||
try:
|
||||
if self.BalancedRoot.Parent != self.BalancedRoot.vol.offset:
|
||||
vollog.debug(
|
||||
f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed BalancedRoot parent equality check"
|
||||
)
|
||||
return False
|
||||
|
||||
elif self.AllocateRoutine < page_start or self.AllocateRoutine > page_end:
|
||||
vollog.debug(
|
||||
f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed AllocateRoutine range check"
|
||||
)
|
||||
return False
|
||||
|
||||
elif self.CompareRoutine < page_start or self.CompareRoutine > page_end:
|
||||
vollog.debug(
|
||||
f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed CompareRoutine range check"
|
||||
)
|
||||
return False
|
||||
|
||||
elif (
|
||||
(self.AllocateRoutine.vol.offset == self.CompareRoutine.vol.offset)
|
||||
or (self.AllocateRoutine.vol.offset == self.FreeRoutine.vol.offset)
|
||||
or (self.CompareRoutine.vol.offset == self.FreeRoutine.vol.offset)
|
||||
):
|
||||
vollog.debug(
|
||||
f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed (Compare|Allocate|Free)Routine uniqueness check"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
except exceptions.InvalidAddressException:
|
||||
return False
|
||||
|
||||
|
||||
class_types = {
|
||||
"SHIM_CACHE_HANDLE": SHIM_CACHE_HANDLE,
|
||||
"SHIM_CACHE_ENTRY": SHIM_CACHE_ENTRY,
|
||||
"_RTL_AVL_TABLE": RTL_AVL_TABLE,
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"unsigned be short": {
|
||||
"kind": "int",
|
||||
"size": 2,
|
||||
"signed": false,
|
||||
"endian": "big"
|
||||
},
|
||||
"long long": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": true,
|
||||
"size": 8
|
||||
},
|
||||
"long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
}
|
||||
},
|
||||
"symbols": {},
|
||||
"user_types": {
|
||||
"_UDP_ENDPOINT": {
|
||||
"fields": {
|
||||
"Owner": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_EPROCESS"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"CreateTime": {
|
||||
"offset": 88,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"Next": {
|
||||
"offset": 112,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_UDP_ENDPOINT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LocalAddr": {
|
||||
"offset": 168,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_LOCAL_ADDRESS_WIN10_UDP"
|
||||
}
|
||||
}
|
||||
},
|
||||
"InetAF": {
|
||||
"offset": 32,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_INETAF"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"Port": {
|
||||
"offset": 160,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned be short"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 168
|
||||
},
|
||||
"_TCP_LISTENER": {
|
||||
"fields": {
|
||||
"Owner": {
|
||||
"offset": 48,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_EPROCESS"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"CreateTime": {
|
||||
"offset": 64,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"LocalAddr": {
|
||||
"offset": 96,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_LOCAL_ADDRESS"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"InetAF": {
|
||||
"offset": 40,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_INETAF"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"Next": {
|
||||
"offset": 120,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_TCP_LISTENER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Port": {
|
||||
"offset": 114,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned be short"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 128
|
||||
},
|
||||
"_TCP_ENDPOINT": {
|
||||
"fields": {
|
||||
"Owner": {
|
||||
"offset": 752,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_EPROCESS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateTime": {
|
||||
"offset": 776,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"AddrInfo": {
|
||||
"offset": 24,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_ADDRINFO"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ListEntry": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"InetAF": {
|
||||
"offset": 16,
|
||||
"type":{
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_INETAF"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LocalPort": {
|
||||
"offset": 112,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned be short"
|
||||
}
|
||||
},
|
||||
"RemotePort": {
|
||||
"offset": 114,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned be short"
|
||||
}
|
||||
},
|
||||
"State": {
|
||||
"offset": 108,
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"name": "TCPStateEnum"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 632
|
||||
},
|
||||
"_LOCAL_ADDRESS": {
|
||||
"fields": {
|
||||
"pData": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_IN_ADDR"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 20
|
||||
},
|
||||
"_LOCAL_ADDRESS_WIN10_UDP": {
|
||||
"fields": {
|
||||
"pData": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_IN_ADDR"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 4
|
||||
},
|
||||
"_ADDRINFO": {
|
||||
"fields": {
|
||||
"Local": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_LOCAL_ADDRESS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Remote": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_IN_ADDR"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 4
|
||||
},
|
||||
"_IN_ADDR": {
|
||||
"fields": {
|
||||
"addr4": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"count": 4,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"kind": "array"
|
||||
}
|
||||
},
|
||||
"addr6": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"count": 16,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"kind": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 6
|
||||
},
|
||||
"_INETAF": {
|
||||
"fields": {
|
||||
"AddressFamily": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned short"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 26
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"_INET_COMPARTMENT_SET": {
|
||||
"fields": {
|
||||
"InetCompartment": {
|
||||
"offset": 328,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_INET_COMPARTMENT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 384
|
||||
},
|
||||
"_INET_COMPARTMENT": {
|
||||
"fields": {
|
||||
"ProtocolCompartment": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_PROTOCOL_COMPARTMENT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 48
|
||||
},
|
||||
"_PROTOCOL_COMPARTMENT": {
|
||||
"fields": {
|
||||
"PortPool": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_INET_PORT_POOL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_PORT_ASSIGNMENT_ENTRY": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_PORT_ASSIGNMENT_LIST": {
|
||||
"fields": {
|
||||
"Assignments": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"count": 256,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_PORT_ASSIGNMENT_ENTRY"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 6144
|
||||
},
|
||||
"_PORT_ASSIGNMENT": {
|
||||
"fields": {
|
||||
"InPaBigPoolBase": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_PORT_ASSIGNMENT_LIST"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_INET_PORT_POOL": {
|
||||
"fields": {
|
||||
"PortAssignments": {
|
||||
"offset": 224,
|
||||
"type": {
|
||||
"count": 256,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_PORT_ASSIGNMENT"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PortBitMap": {
|
||||
"offset": 208,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_RTL_BITMAP"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 11200
|
||||
},
|
||||
"_PARTITION": {
|
||||
"fields": {
|
||||
"Endpoints" : {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_RTL_DYNAMIC_HASH_TABLE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UnknownHashTable" : {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_RTL_DYNAMIC_HASH_TABLE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 192
|
||||
},
|
||||
"_PARTITION_TABLE": {
|
||||
"fields": {
|
||||
"Partitions": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"count": 1,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_PARTITION"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 128
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"TCPStateEnum": {
|
||||
"base": "long",
|
||||
"constants": {
|
||||
"CLOSED": 0,
|
||||
"LISTENING": 1,
|
||||
"SYN_SENT": 2,
|
||||
"SYN_RCVD": 3,
|
||||
"ESTABLISHED": 4,
|
||||
"FIN_WAIT1": 5,
|
||||
"FIN_WAIT2": 6,
|
||||
"CLOSE_WAIT": 7,
|
||||
"CLOSING": 8,
|
||||
"LAST_ACK": 9,
|
||||
"TIME_WAIT": 12,
|
||||
"DELETE_TCB": 13
|
||||
},
|
||||
"size": 4
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona-by-hand",
|
||||
"datetime": "2024-07-30T13:00:00"
|
||||
},
|
||||
"format": "6.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 25
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 56
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 64
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 72
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 80
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 88
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 96
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 104
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"FileSize": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 48
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"FileSize": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"Padding": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 36
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 25
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 56
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 64
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 72
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 80
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 88
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 96
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 104
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"ShimFlags": {
|
||||
"offset": 44,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 48
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"ShimFlags": {
|
||||
"offset": 28,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 36
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 25
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 56
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 64
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 72
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 80
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 88
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 96
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 104
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"u1": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"ListEntryDetail": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "SHIM_CACHE_ENTRY_DETAIL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 48
|
||||
},
|
||||
"SHIM_CACHE_ENTRY_DETAIL": {
|
||||
"fields": {
|
||||
"u1": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"BlobSize": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"u2": {
|
||||
"offset": 20,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"BlobBuffer": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"u1": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 12,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"ListEntryDetail": {
|
||||
"offset": 20,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "SHIM_CACHE_ENTRY_DETAIL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 24
|
||||
},
|
||||
"SHIM_CACHE_ENTRY_DETAIL": {
|
||||
"fields": {
|
||||
"u1": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"BlobSize": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"BlobBuffer": {
|
||||
"offset": 20,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 24
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 25
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 56
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 64
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 72
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 80
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 88
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 96
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 104
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"ShimFlags": {
|
||||
"offset": 44,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"BlobSize": {
|
||||
"offset": 48,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"BlobBuffer": {
|
||||
"offset": 56,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"LastModified": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"ShimFlags": {
|
||||
"offset": 28,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"BlobSize": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"BlobBuffer": {
|
||||
"offset": 36,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 40
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 25
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 56
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 64
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 72
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 80
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 88
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 96
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 104
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"u1": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"u2": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"u3": {
|
||||
"offset": 48,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"ListEntryDetail": {
|
||||
"offset": 56,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "SHIM_CACHE_ENTRY_DETAIL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
},
|
||||
"SHIM_CACHE_ENTRY_DETAIL": {
|
||||
"fields": {
|
||||
"LastModified": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"ShimFlags": {
|
||||
"offset": 12,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"BlobSize": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"Padding": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"BlobBuffer": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 40
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"ListEntry": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
}
|
||||
},
|
||||
"u1": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"u2": {
|
||||
"offset": 12,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"Path": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
}
|
||||
},
|
||||
"u3": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"ListEntryDetail": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "SHIM_CACHE_ENTRY_DETAIL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 36
|
||||
},
|
||||
"SHIM_CACHE_ENTRY_DETAIL": {
|
||||
"fields": {
|
||||
"LastModified": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_LARGE_INTEGER"
|
||||
}
|
||||
},
|
||||
"InsertFlags": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
}
|
||||
},
|
||||
"ShimFlags": {
|
||||
"offset": 12,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"BlobSize": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"BlobBuffer": {
|
||||
"offset": 20,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 24
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,485 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"SHIM_CACHE_HEADER": {
|
||||
"fields": {
|
||||
"Magic": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"u1": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"NumEntries": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"u2": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 400
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"Path": {
|
||||
"type": {
|
||||
"count": 520,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LastModified": {
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "LARGE_INTEGER"
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"FileSize": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"LastUpdate": {
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "LARGE_INTEGER"
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 552
|
||||
},
|
||||
"_SEGMENT": {
|
||||
"fields": {
|
||||
"ControlArea": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_CONTROL_AREA"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TotalNumberOfPtes": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"NonExtendedPtes": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"WritableUserReferences": {
|
||||
"offset": 12,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"SizeOfSegment": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"SegmentPteTemplate": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_MMPTE"
|
||||
}
|
||||
},
|
||||
"NumberOfCommittedPages": {
|
||||
"offset": 28,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"ExtendInfo": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_MMEXTEND_INFO"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SystemImageBase": {
|
||||
"offset": 36,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BasedAddress": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"u1": {
|
||||
"offset": 44,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"u2": {
|
||||
"offset": 48,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"PrototypePte": {
|
||||
"offset": 52,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_MMPTE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ThePtes": {
|
||||
"offset": 60,
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 1,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "nt_symbols!_MMPTE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_LARGE_INTEGER": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"QuadPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "__unnamed_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "union",
|
||||
"size": 8
|
||||
},
|
||||
"__unnamed_2": {
|
||||
"fields": {
|
||||
"HighPart": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"LowPart": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_RTL_BALANCED_LINKS": {
|
||||
"fields": {
|
||||
"Parent": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LeftChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"RightChild": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Balance": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"Reserved": {
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 3,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_RTL_AVL_TABLE": {
|
||||
"fields": {
|
||||
"BalancedRoot": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"OrderedPointer": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"WhichOrderedElement": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 20
|
||||
},
|
||||
"NumberGenericTableElements": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"DepthOfTree": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"RestartKey": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_BALANCED_LINKS"
|
||||
}
|
||||
},
|
||||
"offset": 32
|
||||
},
|
||||
"DeleteCount": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 36
|
||||
},
|
||||
"CompareRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"AllocateRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 44
|
||||
},
|
||||
"FreeRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 48
|
||||
},
|
||||
"TableContext": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 52
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 56
|
||||
},
|
||||
"SHIM_CACHE_HEADER": {
|
||||
"fields": {
|
||||
"Magic": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"u1": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 4
|
||||
},
|
||||
"NumEntries": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"u2": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned int"
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 400
|
||||
},
|
||||
"SHIM_CACHE_ENTRY": {
|
||||
"fields": {
|
||||
"Path": {
|
||||
"type": {
|
||||
"count": 520,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"LastModified": {
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
},
|
||||
"offset": 528
|
||||
},
|
||||
"FileSize": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long long"
|
||||
},
|
||||
"offset": 536
|
||||
},
|
||||
"LastUpdate": {
|
||||
"type": {
|
||||
"kind": "union",
|
||||
"name": "_LARGE_INTEGER"
|
||||
},
|
||||
"offset": 544
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 552
|
||||
},
|
||||
"SHIM_CACHE_HANDLE": {
|
||||
"fields": {
|
||||
"eresource": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!ERESOURCE"
|
||||
}
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"rtl_avl_table": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_RTL_AVL_TABLE"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_SEGMENT": {
|
||||
"fields": {
|
||||
"ControlArea": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_CONTROL_AREA"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TotalNumberOfPtes": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"NonExtendedPtes": {
|
||||
"offset": 8,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"WritableUserReferences": {
|
||||
"offset": 12,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"SizeOfSegment": {
|
||||
"offset": 16,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"SegmentPteTemplate": {
|
||||
"offset": 24,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_MMPTE"
|
||||
}
|
||||
},
|
||||
"NumberOfCommittedPages": {
|
||||
"offset": 32,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
}
|
||||
},
|
||||
"ExtendInfo": {
|
||||
"offset": 36,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_MMEXTEND_INFO"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SystemImageBase": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BasedAddress": {
|
||||
"offset": 44,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"u1": {
|
||||
"offset": 48,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"u2": {
|
||||
"offset": 52,
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "long"
|
||||
}
|
||||
},
|
||||
"PrototypePte": {
|
||||
"offset": 56,
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_MMPTE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ThePtes": {
|
||||
"offset": 64,
|
||||
"type": {
|
||||
"kind": "array",
|
||||
"count": 1,
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "nt_symbols!_MMPTE"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 72
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "dgmcdona by hand",
|
||||
"datetime": "2024-07-05T18:28:00.000000+00:00"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_UNLOADED_DRIVER": {
|
||||
"fields": {
|
||||
"Name": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"StartAddress": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"EndAddress": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"CurrentTime": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
},
|
||||
"offset": 32
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 40
|
||||
},
|
||||
"_UNLOADED_DRIVERS": {
|
||||
"fields": {
|
||||
"UnloadedDrivers": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"count": 1,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_UNLOADED_DRIVER"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "Dave Lassalle by hand",
|
||||
"datetime": "2024-06-19T17:57:16.394003"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"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": {
|
||||
"_UNLOADED_DRIVER": {
|
||||
"fields": {
|
||||
"Name": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"StartAddress": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"EndAddress": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long"
|
||||
},
|
||||
"offset": 12
|
||||
},
|
||||
"CurrentTime": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
},
|
||||
"offset": 16
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 24
|
||||
},
|
||||
"_UNLOADED_DRIVERS": {
|
||||
"fields": {
|
||||
"UnloadedDrivers": {
|
||||
"offset": 0,
|
||||
"type": {
|
||||
"count": 1,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "_UNLOADED_DRIVER"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 4
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "Dave Lassalle by hand",
|
||||
"datetime": "2024-06-19T17:57:16.394003"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,24 @@ is_windows_xp = OsDistinguisher(
|
||||
],
|
||||
)
|
||||
|
||||
is_windows_xp_sp2 = OsDistinguisher(
|
||||
version_check=lambda x: (5, 1) <= x < (5, 2),
|
||||
fallback_checks=[
|
||||
("KdCopyDataBlock", None, False),
|
||||
("_MMFREE_POOL_ENTRY", None, False),
|
||||
("_HANDLE_TABLE", "HandleCount", True),
|
||||
],
|
||||
)
|
||||
|
||||
is_windows_xp_sp3 = OsDistinguisher(
|
||||
version_check=lambda x: (5, 1) <= x < (5, 2),
|
||||
fallback_checks=[
|
||||
("KdCopyDataBlock", None, False),
|
||||
("_MMFREE_POOL_ENTRY", None, True),
|
||||
("_HANDLE_TABLE", "HandleCount", True),
|
||||
],
|
||||
)
|
||||
|
||||
is_xp_or_2003 = OsDistinguisher(
|
||||
version_check=lambda x: (5, 1) <= x < (6, 0),
|
||||
fallback_checks=[
|
||||
@@ -122,6 +140,15 @@ is_xp_or_2003 = OsDistinguisher(
|
||||
],
|
||||
)
|
||||
|
||||
is_2003 = OsDistinguisher(
|
||||
version_check=lambda x: (5, 2) <= x < (5, 3),
|
||||
fallback_checks=[
|
||||
("KdCopyDataBlock", None, False),
|
||||
("_HANDLE_TABLE", "HandleCount", True),
|
||||
("_MM_AVL_TABLE", None, True),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_up_to_15063 = OsDistinguisher(
|
||||
version_check=lambda x: (10, 0) <= x < (10, 0, 15063),
|
||||
fallback_checks=[
|
||||
@@ -141,6 +168,15 @@ is_win10_15063 = OsDistinguisher(
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_15063_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 15063),
|
||||
fallback_checks=[
|
||||
("ObHeaderCookie", None, True),
|
||||
("_HANDLE_TABLE", "HandleCount", False),
|
||||
("_EPROCESS", "KeepAliveCounter", False),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_16299_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 16299),
|
||||
fallback_checks=[
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"base_types": {
|
||||
"pointer": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 8
|
||||
},
|
||||
"unsigned char": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 1
|
||||
},
|
||||
"unsigned long": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 4
|
||||
},
|
||||
"unsigned long long": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 8
|
||||
},
|
||||
"unsigned short": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 2
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"metadata": {
|
||||
"format": "6.1.0",
|
||||
"producer": {
|
||||
"datetime": "2021-07-31T17:37:28.313255",
|
||||
"name": "vmextract-by-hand",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
},
|
||||
"symbols": {
|
||||
"revision_id": {
|
||||
"address": 0,
|
||||
"constant_data": "MTg="
|
||||
}
|
||||
},
|
||||
"user_types": {
|
||||
"_VMCS": {
|
||||
"fields": {
|
||||
"ept": {
|
||||
"offset": 320,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"executive_vmcs_ptr": {
|
||||
"offset": 208,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"guest_cr3": {
|
||||
"offset": 528,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"guest_cr4": {
|
||||
"offset": 536,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"guest_pdpte": {
|
||||
"offset": 544,
|
||||
"type": {
|
||||
"count": 4,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"guest_physical_addr": {
|
||||
"offset": 328,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"host_cr3": {
|
||||
"offset": 816,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"host_cr4": {
|
||||
"offset": 824,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"vmcs_link_ptr": {
|
||||
"offset": 248,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"vpid": {
|
||||
"offset": 206,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned short"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 4096
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"base_types": {
|
||||
"pointer": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 8
|
||||
},
|
||||
"unsigned char": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 1
|
||||
},
|
||||
"unsigned long": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 4
|
||||
},
|
||||
"unsigned long long": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 8
|
||||
},
|
||||
"unsigned short": {
|
||||
"endian": "little",
|
||||
"kind": "int",
|
||||
"signed": false,
|
||||
"size": 2
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"metadata": {
|
||||
"format": "6.1.0",
|
||||
"producer": {
|
||||
"datetime": "2021-07-16T16:21:01.062423",
|
||||
"name": "vmextract-by-hand",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
},
|
||||
"symbols": {
|
||||
"revision_id": {
|
||||
"address": 0,
|
||||
"constant_data": "NA=="
|
||||
}
|
||||
},
|
||||
"user_types": {
|
||||
"_VMCS": {
|
||||
"fields": {
|
||||
"ept": {
|
||||
"offset": 320,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"executive_vmcs_ptr": {
|
||||
"offset": 208,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"guest_cr3": {
|
||||
"offset": 528,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"guest_cr4": {
|
||||
"offset": 536,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"guest_pdpte": {
|
||||
"offset": 544,
|
||||
"type": {
|
||||
"count": 4,
|
||||
"kind": "array",
|
||||
"subtype": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"guest_physical_addr": {
|
||||
"offset": 328,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"host_cr3": {
|
||||
"offset": 816,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"host_cr4": {
|
||||
"offset": 824,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"vmcs_link_ptr": {
|
||||
"offset": 248,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned long long"
|
||||
}
|
||||
},
|
||||
"vpid": {
|
||||
"offset": 206,
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "unsigned short"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 4096
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
|
||||
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
|
||||
Reference in New Issue
Block a user