diff --git a/volatility3/framework/plugins/linux/check_idt.py b/volatility3/framework/plugins/linux/check_idt.py index ffb707af5..653ecc081 100644 --- a/volatility3/framework/plugins/linux/check_idt.py +++ b/volatility3/framework/plugins/linux/check_idt.py @@ -3,7 +3,7 @@ # import logging -from typing import List +from typing import List, Optional import volatility3.framework.symbols.linux.utilities.modules as linux_utilities_modules from volatility3.framework import interfaces, renderers, symbols @@ -20,6 +20,9 @@ class Check_idt(interfaces.plugins.PluginInterface): _required_framework_version = (2, 0, 0) + # 2.0.0 - Add versioning at all, add `get_idt_type` + _version = (2, 0, 0) + @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: return [ @@ -41,7 +44,42 @@ class Check_idt(interfaces.plugins.PluginInterface): ), ] + @staticmethod + def get_idt_type(context, vmlinux_name) -> Optional[str]: + """ + Determines the IDT type for this symbol table or returns None + + The original version ended clauses with an `else` leading to bad fall through + of returning a type that did not exist in the symbol table. + + Future updates should not leave fall through cases to avoid this repeating. + """ + + vmlinux = context.modules[vmlinux_name] + + is_32bit = not symbols.symbol_table_is_64bit(context, vmlinux.symbol_table_name) + + # These are in a specific order. Only append to the lists going forward + # or ask Andrew to run tests before merging. + if is_32bit: + idt_types = ["gate_struct", "desc_struct", "gate_struct32"] + else: + idt_types = ["gate_struct64", "gate_struct", "idt_desc"] + + for idt_type in idt_types: + if vmlinux.has_type(idt_type): + return idt_type + + return None + def _generator(self): + idt_type = self.get_idt_type(self.context, self.config["kernel"]) + if not idt_type: + vollog.error( + "Unable to determine the data structure type for IDT entries. Please file a bug on the GitHub tracker with your kernel version." + ) + return + vmlinux = self.context.modules[self.config["kernel"]] modules = lsmod.Lsmod.list_modules(self.context, vmlinux.name) @@ -50,30 +88,15 @@ class Check_idt(interfaces.plugins.PluginInterface): self.context, vmlinux.name, modules ) - is_32bit = not symbols.symbol_table_is_64bit( - self.context, vmlinux.symbol_table_name - ) - idt_table_size = 256 - address_mask = self.context.layers[vmlinux.layer_name].address_mask + kernel_layer = self.context.layers[vmlinux.layer_name] + + address_mask = kernel_layer.address_mask # hw handlers + system call check_idxs = list(range(20)) + [128] - if is_32bit: - if vmlinux.has_type("gate_struct"): - idt_type = "gate_struct" - else: - idt_type = "desc_struct" - else: - if vmlinux.has_type("gate_struct64"): - idt_type = "gate_struct64" - elif vmlinux.has_type("gate_struct"): - idt_type = "gate_struct" - else: - idt_type = "idt_desc" - addrs = vmlinux.object_from_symbol("idt_table") table = vmlinux.object( @@ -87,15 +110,16 @@ class Check_idt(interfaces.plugins.PluginInterface): for i in check_idxs: ent = table[i] - if not ent: + if not ent or not kernel_layer.is_valid(ent.vol.offset): continue - if hasattr(ent, "Address"): - idt_addr = ent.Address + if hasattr(ent, "a"): + idt_addr = (ent.b & 0xFFFF0000) | (ent.a & 0x0000FFFF) else: low = ent.offset_low middle = ent.offset_middle + # offset_high is for 64bit systems if hasattr(ent, "offset_high"): high = ent.offset_high else: @@ -105,11 +129,16 @@ class Check_idt(interfaces.plugins.PluginInterface): idt_addr = idt_addr & address_mask - module_name, symbol_name = ( - linux_utilities_modules.Modules.lookup_module_address( - self.context, vmlinux.name, handlers, idt_addr + # 0 means unintialized/unused, not a rootkit + if idt_addr == 0: + module_name = renderers.NotAvailableValue() + symbol_name = renderers.NotAvailableValue() + else: + module_name, symbol_name = ( + linux_utilities_modules.Modules.lookup_module_address( + self.context, vmlinux.name, handlers, idt_addr + ) ) - ) yield ( 0,