Merge branch 'develop' into 816-port-cmdscan-and-console-plugins-from-vol2-to-vol3-please

This commit is contained in:
Dave Lassalle
2024-08-07 11:17:05 -07:00
57 changed files with 8911 additions and 220 deletions
+1
View File
@@ -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
+11
View File
@@ -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")
+17 -2
View File
@@ -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
+12
View File
@@ -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(
+2 -4
View File
@@ -1,11 +1,9 @@
# 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 = 2 # Number of changes that do not change the interface
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 = ""
# 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
+21
View File
@@ -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:
@@ -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())
+3 -1
View File
@@ -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
@@ -1,10 +1,9 @@
# 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
@@ -14,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__)
@@ -23,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, 1)
_version = (3, 0, 0)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -76,67 +75,11 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
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
@@ -204,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,
@@ -212,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:
@@ -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, 1)
_version = (1, 0, 2)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -142,6 +142,7 @@ 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:
@@ -175,10 +176,11 @@ class Handles(interfaces.plugins.PluginInterface):
virtual_layer_name, func_addr_to_read, num_bytes_to_read
)
except exceptions.InvalidAddressException:
vollog.debug(
f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}"
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)}"
)
return None
self._sar_value = DEFAULT_SAR_VALUE
return self._sar_value
md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
@@ -198,9 +200,10 @@ class Handles(interfaces.plugins.PluginInterface):
break
if self._sar_value is None:
vollog.debug(
f"Failed to to locate SAR value having parsed {instruction_count} instructions"
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
@@ -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(),
)
@@ -47,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()
@@ -69,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(),
@@ -6,7 +6,7 @@ from typing import Iterable
from volatility3.framework import interfaces
from volatility3.framework.configuration import requirements
from volatility3.plugins.windows import poolscanner, dlllist, pslist, modules
from volatility3.plugins.windows import poolscanner, modules, pedump
vollog = logging.getLogger(__name__)
@@ -35,12 +35,6 @@ class ModScan(modules.Modules):
requirements.VersionRequirement(
name="modules", component=modules.Modules, version=(2, 0, 0)
),
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",
@@ -58,6 +52,9 @@ class ModScan(modules.Modules):
optional=True,
default=None,
),
requirements.VersionRequirement(
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
),
]
@classmethod
@@ -9,7 +9,7 @@ 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__)
@@ -35,9 +35,6 @@ 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",
@@ -55,6 +52,9 @@ class Modules(interfaces.plugins.PluginInterface):
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):
@@ -63,16 +63,15 @@ class Modules(interfaces.plugins.PluginInterface):
)
file_output = f"Cannot find a viable session layer for {mod.DllBase:#x}"
if session_layer_name:
file_handle = dlllist.DllList.dump_pe(
file_output = pedump.PEDump.dump_ldr_entry(
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
if not file_output:
file_output = "Error outputting file"
return file_output
@@ -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
@@ -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(),
)
@@ -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,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
+113 -58
View File
@@ -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"]
)
@@ -76,7 +83,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
ethread.get_exit_time()
) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
except exceptions.InvalidAddressException:
vollog.debug("Thread invalid address {:#x}".format(thread.vol.offset))
vollog.debug("Thread invalid address {:#x}".format(ethread.vol.offset))
return None
return (
@@ -88,18 +95,19 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
thread_exit_time,
)
def _generator(self):
kernel = self.context.modules[self.config["kernel"]]
def _generator(self, filter_func: Callable):
kernel_name = self.config["kernel"]
for ethread in self.scan_threads(
self.context, kernel.layer_name, kernel.symbol_table_name
):
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 = {}
(
@@ -126,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),
@@ -136,5 +151,5 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
("CreateTime", datetime.datetime),
("ExitTime", datetime.datetime),
],
self._generator(),
self._generator(filt_func),
)
@@ -3,7 +3,7 @@
#
import logging
from typing import List, Generator
from typing import Callable, Iterable, List, Generator
from volatility3.framework import interfaces, constants
from volatility3.framework.configuration import requirements
@@ -18,6 +18,10 @@ class Threads(thrdscan.ThrdScan):
_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
@@ -34,7 +38,7 @@ class Threads(thrdscan.ThrdScan):
optional=True,
),
requirements.PluginRequirement(
name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 0, 0)
name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 1, 0)
),
]
@@ -60,18 +64,27 @@ class Threads(thrdscan.ThrdScan):
seen.add(thread.vol.offset)
yield thread
def _generator(self):
kernel = self.context.modules[self.config["kernel"]]
@classmethod
def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable:
return pslist.PsList.create_pid_filter(config.get("pid", None))
filter_func = pslist.PsList.create_pid_filter(self.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=self.context,
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
context=context,
layer_name=layer_name,
symbol_table=symbol_table_name,
filter_func=filter_func,
):
for thread in self.list_threads(kernel, proc):
info = self.gather_thread_info(thread)
if info:
yield (0, info)
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(),
)
@@ -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(),
)
@@ -56,7 +56,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
sanity_check = 0x1000 * 0x1000 * 0x1000
sanity_check = 1024 * 1024 * 1024 # 1 GB
for task in pslist.PsList.list_processes(
context=self.context,
@@ -66,15 +66,14 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
):
layer_name = task.add_process_layer()
layer = self.context.layers[layer_name]
for start, end in self.get_vad_maps(task):
size = end - start
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, end - start, True)):
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:
@@ -106,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():
@@ -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
)
@@ -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)
@@ -105,8 +105,11 @@
},
"NotificationRoutine": {
"type": {
"kind": "base",
"name": "unsigned int"
"kind": "pointer",
"subtype": {
"kind": "base",
"name": "void"
}
},
"offset": 24
}
@@ -20,6 +20,7 @@ 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 pool
@@ -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."""
@@ -1010,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,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=[
+1
View File
@@ -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