Skip to content

Commit 966d23e

Browse files
authored
Merge pull request #1233 from gcmoreira/linux_page_cache
Linux Page Cache and IDR
2 parents 9bba628 + 3bf9f8c commit 966d23e

File tree

9 files changed

+1622
-25
lines changed

9 files changed

+1622
-25
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
4747
- name: Clean up post-test
4848
run: |
49-
rm -rf *.lime
49+
rm -rf *.bin
5050
rm -rf *.img
5151
cd volatility3/symbols
5252
rm -rf linux
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
2+
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
3+
#
4+
import logging
5+
from typing import List
6+
7+
from volatility3.framework import renderers, interfaces, exceptions
8+
from volatility3.framework.renderers import format_hints
9+
from volatility3.framework.interfaces import plugins
10+
from volatility3.framework.configuration import requirements
11+
12+
vollog = logging.getLogger(__name__)
13+
14+
15+
class EBPF(plugins.PluginInterface):
16+
"""Enumerate eBPF programs"""
17+
18+
_required_framework_version = (2, 0, 0)
19+
20+
_version = (1, 0, 0)
21+
22+
@classmethod
23+
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
24+
return [
25+
requirements.ModuleRequirement(
26+
name="kernel",
27+
description="Linux kernel",
28+
architectures=["Intel32", "Intel64"],
29+
),
30+
]
31+
32+
def get_ebpf_programs(
33+
self,
34+
context: interfaces.context.ContextInterface,
35+
vmlinux_module_name: str,
36+
) -> interfaces.objects.ObjectInterface:
37+
"""Enumerate eBPF programs walking its IDR.
38+
39+
Args:
40+
context: The context to retrieve required elements (layers, symbol tables) from
41+
vmlinux_module_name: The name of the kernel module on which to operate
42+
Yields:
43+
eBPF program objects
44+
"""
45+
vmlinux = context.modules[vmlinux_module_name]
46+
47+
if not vmlinux.has_symbol("prog_idr"):
48+
raise exceptions.VolatilityException(
49+
"Cannot find the eBPF prog idr. Unsupported kernel"
50+
)
51+
52+
prog_idr = vmlinux.object_from_symbol("prog_idr")
53+
for page_addr in prog_idr.get_entries():
54+
bpf_prog = vmlinux.object("bpf_prog", offset=page_addr, absolute=True)
55+
yield bpf_prog
56+
57+
def _generator(self):
58+
for prog in self.get_ebpf_programs(self.context, self.config["kernel"]):
59+
prog_addr = prog.vol.offset
60+
prog_type = prog.get_type() or renderers.NotAvailableValue()
61+
prog_tag = prog.get_tag() or renderers.NotAvailableValue()
62+
prog_name = prog.get_name() or renderers.NotAvailableValue()
63+
fields = (format_hints.Hex(prog_addr), prog_name, prog_tag, prog_type)
64+
yield (0, fields)
65+
66+
def run(self):
67+
headers = [
68+
("Address", format_hints.Hex),
69+
("Name", str),
70+
("Tag", str),
71+
("Type", str),
72+
]
73+
return renderers.TreeGrid(headers, self._generator())

volatility3/framework/plugins/linux/mountinfo.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class MountInfo(plugins.PluginInterface):
3737

3838
_required_framework_version = (2, 2, 0)
3939

40-
_version = (1, 0, 0)
40+
_version = (1, 2, 0)
4141

4242
@classmethod
4343
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -143,10 +143,10 @@ def get_mountinfo(
143143
sb_opts,
144144
)
145145

146+
@staticmethod
146147
def _get_tasks_mountpoints(
147-
self,
148148
tasks: Iterable[interfaces.objects.ObjectInterface],
149-
filtered_by_pids: bool,
149+
filtered_by_pids: bool = False,
150150
):
151151
seen_mountpoints = set()
152152
for task in tasks:
@@ -184,8 +184,8 @@ def _generator(
184184
self,
185185
tasks: Iterable[interfaces.objects.ObjectInterface],
186186
mnt_ns_ids: List[int],
187-
mount_format: bool,
188-
filtered_by_pids: bool,
187+
mount_format: bool = False,
188+
filtered_by_pids: bool = False,
189189
) -> Iterable[Tuple[int, Tuple]]:
190190
show_filter_warning = False
191191
for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(
@@ -247,6 +247,37 @@ def _generator(
247247
"Could not filter by mount namespace id. This field is not available in this kernel."
248248
)
249249

250+
@classmethod
251+
def get_superblocks(
252+
cls,
253+
context: interfaces.context.ContextInterface,
254+
vmlinux_module_name: str,
255+
) -> Iterable[interfaces.objects.ObjectInterface]:
256+
"""Yield file system superblocks based on the task's mounted filesystems.
257+
258+
Args:
259+
context: The context to retrieve required elements (layers, symbol tables) from
260+
vmlinux_module_name: The name of the kernel module on which to operate
261+
262+
Yields:
263+
super_block: Kernel's struct super_block object
264+
"""
265+
# No filter so that we get all the mount namespaces from all tasks
266+
tasks = pslist.PsList.list_tasks(context, vmlinux_module_name)
267+
268+
seen_sb_ptr = set()
269+
for task, mnt, _mnt_ns_id in cls._get_tasks_mountpoints(tasks):
270+
path_root = linux.LinuxUtilities.get_path_mnt(task, mnt)
271+
if not path_root:
272+
continue
273+
274+
sb_ptr = mnt.get_mnt_sb()
275+
if not sb_ptr or sb_ptr in seen_sb_ptr:
276+
continue
277+
seen_sb_ptr.add(sb_ptr)
278+
279+
yield sb_ptr.dereference(), path_root
280+
250281
def run(self):
251282
pids = self.config.get("pids")
252283
mount_ns_ids = self.config.get("mntns")

0 commit comments

Comments
 (0)