Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Modules/Lsmod plugin #364

Merged
merged 15 commits into from
Sep 26, 2023
71 changes: 71 additions & 0 deletions dissect/target/plugins/os/unix/linux/modules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from dataclasses import dataclass
from typing import Iterator

from dissect.target.exceptions import UnsupportedPluginError
from dissect.target.helpers.record import TargetRecordDescriptor
from dissect.target.plugin import Plugin, export
from dissect.target.target import Target

ModuleRecord = TargetRecordDescriptor(
"linux/module",
[
("string", "name"),
("varint", "size"),
("varint", "refcount"),
("string[]", "used_by"),
("path", "source"),
],
)


@dataclass
class Module:
path: str
name: str
size: int
refcnt: int
used_by: list[str]


class ModulePlugin(Plugin):
def __init__(self, target: Target):
super().__init__(target)
self._module_base_path = self.target.fs.path("/sys/module")

def check_compatible(self) -> bool:
if not self._module_base_path.is_dir() or not next(self._module_base_path.iterdir(), None):
raise UnsupportedPluginError("No module paths found.")

Check warning on line 37 in dissect/target/plugins/os/unix/linux/modules.py

View check run for this annotation

Codecov / codecov/patch

dissect/target/plugins/os/unix/linux/modules.py#L37

Added line #L37 was not covered by tests

def _iterate_modules(self) -> Iterator[Module]:
for module_path in self._module_base_path.iterdir():
if module_path.joinpath("initstate").exists():
holders = []
if (holders_path := module_path.joinpath("holders")).exists():
holders = [item.name for item in holders_path.iterdir()]
yield Module(
module_path,
module_path.name,
int(module_path.joinpath("coresize").read_text()),
int(module_path.joinpath("refcnt").read_text()),
holders,
)

@export(record=ModuleRecord)
def sysmodules(self) -> Iterator[ModuleRecord]:
"""Return information about active kernel modules."""
for module in self._iterate_modules():
yield ModuleRecord(
name=module.name,
size=module.size,
refcount=module.refcnt,
used_by=module.used_by,
source=module.path,
DevJoost marked this conversation as resolved.
Show resolved Hide resolved
_target=self.target,
)

@export(output="yield")
def lsmod(self) -> Iterator[str]:
"""Return information about active kernel modules in lsmod format"""
yield f"{'Module ':<28} {'Size':<7} Used by"
for module in self._iterate_modules():
yield f"{module.name:<28} {module.size:<7} {module.refcnt} {','.join(module.used_by)}"

Check warning on line 71 in dissect/target/plugins/os/unix/linux/modules.py

View check run for this annotation

Codecov / codecov/patch

dissect/target/plugins/os/unix/linux/modules.py#L69-L71

Added lines #L69 - L71 were not covered by tests
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
live
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
live
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4
22 changes: 22 additions & 0 deletions tests/test_plugins_os_unix_linux_modules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from dissect.target.filesystem import VirtualFilesystem
from dissect.target.plugins.os.unix.linux.modules import ModulePlugin
from dissect.target.target import Target

from ._utils import absolute_path


def test_modules_plugin(target_unix: Target, fs_unix: VirtualFilesystem) -> None:
test_folder = absolute_path("data/plugins/os/unix/linux/modules/module")
fs_unix.map_dir("/sys/module", test_folder)

target_unix.add_plugin(ModulePlugin)
results = sorted(list(target_unix.sysmodules()), key=lambda x: x.name)
assert len(results) == 2
assert results[0].name == "modulea"
assert results[0].size == 1
assert results[0].refcount == 3
assert results[0].used_by == ["holdera"]
assert results[1].name == "moduleb"
assert results[1].size == 2
assert results[1].refcount == 4
assert results[1].used_by == ["holdera", "holderb"]