# 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
#
"""A module containing a collection of plugins that produce data typically
found in Mac's lsmod command."""
from volatility3.framework import renderers, interfaces, contexts
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.framework.objects import utility
from volatility3.framework.renderers import format_hints


class Lsmod(plugins.PluginInterface):
    """Lists loaded kernel modules."""

    _required_framework_version = (1, 0, 0)

    _version = (1, 0, 0)

    @classmethod
    def get_requirements(cls):
        return [
            requirements.TranslationLayerRequirement(name = 'primary',
                                                     description = 'Memory layer for the kernel',
                                                     architectures = ["Intel32", "Intel64"]),
            requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel")
        ]

    @classmethod
    def list_modules(cls, context: interfaces.context.ContextInterface, layer_name: str, darwin_symbols: str):
        """Lists all the 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
            darwin_symbols: The name of the table containing the kernel symbols

        Returns:
            A list of modules from the `layer_name` layer
        """
        kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
        kernel_layer = context.layers[layer_name]

        kmod_ptr = kernel.object_from_symbol(symbol_name = "kmod")

        try:
            kmod = kmod_ptr.dereference().cast("kmod_info")
        except exceptions.InvalidAddressException:
            return []

        yield kmod

        try:
            kmod = kmod.next
        except exceptions.InvalidAddressException:
            return []

        seen = set()

        while kmod != 0 and \
              kmod not in seen and \
              len(seen) < 1024:

            kmod_obj = kmod.dereference()

            if not kernel_layer.is_valid(kmod_obj.vol.offset, kmod_obj.vol.size):
                break

            seen.add(kmod)

            yield kmod

            try:
                kmod = kmod.next
            except exceptions.InvalidAddressException:
                return

    def _generator(self):
        for module in self.list_modules(self.context, self.config['primary'], self.config['darwin']):

            mod_name = utility.array_to_string(module.name)
            mod_size = module.size

            yield 0, (format_hints.Hex(module.vol.offset), mod_name, mod_size)

    def run(self):
        return renderers.TreeGrid([("Offset", format_hints.Hex), ("Name", str), ("Size", int)], self._generator())

[((41, 17, 41, 72), 'volatility3.framework.contexts.Module', 'contexts.Module', ({(41, 33, 41, 40): 'context', (41, 42, 41, 56): 'darwin_symbols', (41, 58, 41, 68): 'layer_name', (41, 70, 41, 71): '0'}, {}), '(context, darwin_symbols, layer_name, 0)', False, 'from volatility3.framework import renderers, interfaces, contexts\n'), ((23, 12, 25, 92), 'volatility3.framework.configuration.requirements.TranslationLayerRequirement', 'requirements.TranslationLayerRequirement', (), '', False, 'from volatility3.framework.configuration import requirements\n'), ((26, 12, 26, 92), 'volatility3.framework.configuration.requirements.SymbolTableRequirement', 'requirements.SymbolTableRequirement', (), '', False, 'from volatility3.framework.configuration import requirements\n'), ((81, 23, 81, 59), 'volatility3.framework.objects.utility.array_to_string', 'utility.array_to_string', ({(81, 47, 81, 58): 'module.name'}, {}), '(module.name)', False, 'from volatility3.framework.objects import utility\n'), ((84, 22, 84, 57), 'volatility3.framework.renderers.format_hints.Hex', 'format_hints.Hex', ({(84, 39, 84, 56): 'module.vol.offset'}, {}), '(module.vol.offset)', False, 'from volatility3.framework.renderers import format_hints\n')]