Skip to content

Volatility3 upgrade#11

Draft
Swashbuckler1 wants to merge 5 commits intomasterfrom
volatility3
Draft

Volatility3 upgrade#11
Swashbuckler1 wants to merge 5 commits intomasterfrom
volatility3

Conversation

@Swashbuckler1
Copy link
Collaborator

@Swashbuckler1 Swashbuckler1 commented Jan 16, 2026

Motivation for change

Upgrading to volatility3 after PANDA upgrade

Additional information

For reference, this is the old code for svcscan:

def get_svcscan(config):
    """List all of the system services"""
    scanner = svcscan.SvcScan(config)
    aggregate_data = []
    for service in scanner.calculate():
        sdata = {
            "ServiceName": str(service.ServiceName.dereference()),
            "DisplayName": str(service.DisplayName.dereference()),
            "DriverName": str(service.DriverName.dereference()),
            "State": str(service.State),
            "Pid": int(service.ServiceProcess.dereference().ProcessId),
        }

        aggregate_data.append(sdata)
    return aggregate_data

volatility3 has decoupled the DriverName field from svcscan nodes.

To parse the plugin output, you need to walk the treegrid using a visitor function as so:

def get_svcscan():
    """List all of the system services"""
    config_path = "plugins.SvcScan"
    automagics = automagic.choose_automagic(available_automagics, svcscan.SvcScan)
    constructed = plugins.construct_plugin(ctx, automagics, svcscan.SvcScan, config_path, progress_callback=None, open_method=None)
    treegrid = constructed.run()
    svcscan_data = []
    treegrid.visit(node=None, function=svcscan_visitor, initial_accumulator=svcscan_data)
    driverscan_data = get_driverscan()

    for i in range(len(svcscan_data)):
        for drv in driverscan_data:
            if svcscan_data[i]["offset"] == drv["offset"]:
                svcscan_data[i]["DriverName"] = drv["name"]
                continue
    return svcscan_data


def get_driverscan():
    from volatility3.plugins.windows import driverscan # Imported here because circular import error when at top of file
    config_path = "plugins.DriverScan"
    automagics = automagic.choose_automagic(available_automagics, driverscan.DriverScan)
    constructed = plugins.construct_plugin(ctx, automagics, driverscan.DriverScan, config_path, progress_callback=None, open_method=None)
    treegrid = constructed.run()
    driverscan_data = []
    treegrid.visit(node=None, function=driverscan_visitor, initial_accumulator=driverscan_data)
    return driverscan_data

def svcscan_visitor(node, accumulator):
    if node.values:
        # print(node)
        offset = node.values[0]
        pid = node.values[2]
        state = node.values[4]
        name, display_name = node.values[6:8]

        pid = str(pid) if type(pid) == volatility3.framework.renderers.NotApplicableValue else int(pid)

        svc_data = {
            "ServiceName": name,
            "DisplayName": display_name,
            "State": state,
            "Pid": pid,
            "offset": int(offset),
        }
        accumulator.append(svc_data)
    return accumulator

def driverscan_visitor(node, accumulator):
    if node.values:
        offset, start = node.values[:2]
        name = node.values[-1]
        drv_data = {
            "offset": int(offset),
            "start": int(start),
            "name": str(name),
        }
        accumulator.append(drv_data)
    return accumulator

The reason for these 4 functions is that when you run svcscan, the nodes in the treegrid contain a binary and binary_registry field:

(Pdb) node
<TreeNode [1] - RowStructure(offset=14049152, order=81, pid=<volatility3.framework.renderers.NotApplicableValue object at 0x7f3030d684f0>, start='SERVICE_DEMAND_START', state='SERVICE_STOPPED', type='SERVICE_KERNEL_DRIVER', name='ErrDev', display='Microsoft Hardware Error Device Driver', binary=<volatility3.framework.renderers.NotApplicableValue object at 0x7f3030f8f430>, binary_registry_='\\SystemRoot\\system32\\drivers\\errdev.sys', dll=<volatility3.framework.renderers.UnreadableValue object at 0x7f303124d100>)>

However, those are not what we want. We need the actual name of the driver which we can get from the driverscan plugin node's 'name' field.

(Pdb) node
<TreeNode [1] - RowStructure(offset=2125042096, start=273228685197312, size=57344, servicekey='monitor', drivername='monitor', name='\\Driver\\monitor')>

This forces us to have to run driverscan first and then iterate through the services and match them.

The problem with this code is that the driver offsets do not match with those of the service, so you cannot match the driver names properly.

The other approach is as follows:

Find one of the drivers by running get_driverscan() function and its offset

(Pdb) node
<TreeNode [0] - RowStructure(offset=2124476512, start=273228685139968, size=57344, servicekey='vga', drivername='vga', name='\\Driver\\vga')>
(Pdb) offset = node.values[0]

Find the symbol tables in the symbol space

(Pdb) dict(ctx.symbol_space.items())
{'pdbscan': <volatility3.framework.symbols.native.NativeTable object at 0x7f17e3268dc0>, 'symbol_table_name1': <volatility3.framework.symbols.windows.WindowsKernelIntermedSymbols object at 0x7f17e32419d0>}

symbol_table_name1 has the kernel symbols

Next list the symbols

(Pdb) ctx.symbol_space["symbol_table_name1"].types

I ommited the output because it's very large but the one we're looking for is '_DRIVER_OBJECT'

Get the driver object

(Pdb) drv = ctx.object("symbol_table_name1!_DRIVER_OBJECT", "layer_name", offset)
(Pdb) drv
<DRIVER_OBJECT symbol_table_name1!_DRIVER_OBJECT: layer_name @ 0x7ea0f060 #336>

Attempt to get the driver name

(Pdb) drv.DriverName
<UNICODE_STRING symbol_table_name1!_UNICODE_STRING (.DriverName): layer_name @ 0x7ea0f098 #16>
(Pdb) dir(drv.DriverName)
['Buffer', 'Length', 'MaximumLength', 'String', 'VolTemplateProxy', '__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_check_members', '_concrete_members', '_context', '_vol', 'cast', 'get_string', 'get_symbol_table_name', 'has_member', 'has_valid_member', 'has_valid_members', 'member', 'vol', 'write']
(Pdb) drv.DriverName.String
*** volatility3.framework.exceptions.PagedInvalidAddressException: Page Fault at entry 0x0 in table page table
(Pdb) drv.DriverName.get_string()
*** volatility3.framework.exceptions.PagedInvalidAddressException: Page Fault at entry 0x0 in table page table

DriverName is a UNICODE_STRING as shown here: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_driver_object

typedef struct _DRIVER_OBJECT {
  CSHORT             Type;
  CSHORT             Size;
  PDEVICE_OBJECT     DeviceObject;
  ULONG              Flags;
  PVOID              DriverStart;
  ULONG              DriverSize;
  PVOID              DriverSection;
  PDRIVER_EXTENSION  DriverExtension;
  UNICODE_STRING     DriverName;
  PUNICODE_STRING    HardwareDatabase;
  PFAST_IO_DISPATCH  FastIoDispatch;
  PDRIVER_INITIALIZE DriverInit;
  PDRIVER_STARTIO    DriverStartIo;
  PDRIVER_UNLOAD     DriverUnload;
  PDRIVER_DISPATCH   MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT, *PDRIVER_OBJECT;

I'm not even able to read the length of DriverName so that I can read the raw bytes

(Pdb) drv.DriverName.Length
*** volatility3.framework.exceptions.PagedInvalidAddressException: Page Fault at entry 0x0 in table page table

@Swashbuckler1 Swashbuckler1 self-assigned this Jan 16, 2026
@Swashbuckler1 Swashbuckler1 marked this pull request as draft January 16, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant