-
Notifications
You must be signed in to change notification settings - Fork 473
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
New linux plugin: modxview #1330
base: develop
Are you sure you want to change the base?
Changes from 1 commit
d5a0b93
e1b343a
c88ebe8
9440f53
9d08c46
b209ea3
485ef89
dd3542b
a388895
f3d7647
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
from volatility3.framework.objects import utility | ||
from volatility3.framework.symbols import intermed | ||
from volatility3.framework.symbols.linux import extensions | ||
from volatility3.framework.constants import linux as linux_constants | ||
|
||
|
||
class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): | ||
|
@@ -830,3 +831,123 @@ def get_cached_pages(self) -> Iterator[interfaces.objects.ObjectInterface]: | |
page = self.vmlinux.object("page", offset=page_addr, absolute=True) | ||
if page: | ||
yield page | ||
|
||
|
||
class Tainting: | ||
"""Tainted kernel and modules parsing capabilities. | ||
|
||
Relevant kernel functions: | ||
- modules: module_flags_taint | ||
- kernel: print_tainted | ||
""" | ||
|
||
def __init__( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd also expected these just to be function containers, rather than carrying state themselves. We can have them carry state I guess, I'm just not sure the extra complexity/separation of parameters is worth it? |
||
self, | ||
context: interfaces.context.ContextInterface, | ||
kernel_module_name: str, | ||
): | ||
self.kernel = context.modules[kernel_module_name] | ||
|
||
@property | ||
def kernel_taint_flags_list( | ||
self, | ||
) -> Optional[List[interfaces.objects.ObjectInterface]]: | ||
if self.kernel.has_symbol("taint_flags"): | ||
return list(self.kernel.object_from_symbol("taint_flags")) | ||
return None | ||
|
||
def _module_flags_taint_pre_4_10_rc1( | ||
self, taints: int, is_module: bool = False | ||
) -> str: | ||
"""Convert the module's taints value to a 1-1 character mapping. | ||
Relies on statically defined taints mappings in the framework. | ||
|
||
Args: | ||
taints: The taints value, represented by an integer | ||
is_module: Indicates if the taints value is associated with a built-in/LKM module | ||
|
||
Returns: | ||
The raw taints string. | ||
""" | ||
taints_string = "" | ||
for char, taint_flag in linux_constants.TAINT_FLAGS.items(): | ||
if is_module and is_module != taint_flag.module: | ||
continue | ||
|
||
if taints & taint_flag.shift: | ||
taints_string += char | ||
|
||
return taints_string | ||
|
||
def _module_flags_taint_post_4_10_rc1( | ||
self, taints: int, is_module: bool = False | ||
) -> str: | ||
"""Convert the module's taints value to a 1-1 character mapping. | ||
Relies on kernel symbol embedded taints definitions. | ||
|
||
struct taint_flag { | ||
char c_true; /* character printed when tainted */ | ||
char c_false; /* character printed when not tainted */ | ||
bool module; /* also show as a per-module taint flag */ | ||
}; | ||
|
||
Args: | ||
taints: The taints value, represented by an integer | ||
is_module: Indicates if the taints value is associated with a built-in/LKM module | ||
|
||
Returns: | ||
The raw taints string. | ||
""" | ||
taints_string = "" | ||
for i, taint_flag in enumerate(self.kernel_taint_flags_list): | ||
if is_module and is_module != taint_flag.module: | ||
continue | ||
c_true = chr(taint_flag.c_true) | ||
c_false = chr(taint_flag.c_false) | ||
if taints & (1 << i): | ||
taints_string += c_true | ||
elif c_false != " ": | ||
taints_string += c_false | ||
|
||
return taints_string | ||
|
||
def get_taints_as_plain_string(self, taints: int, is_module: bool = False) -> str: | ||
"""Convert the taints value to a 1-1 character mapping. | ||
|
||
Args: | ||
taints: The taints value, represented by an integer | ||
is_module: Indicates if the taints value is associated with a built-in/LKM module | ||
s | ||
Returns: | ||
The raw taints string. | ||
|
||
Documentation: | ||
- module_flags_taint kernel function | ||
""" | ||
|
||
if self.kernel_taint_flags_list: | ||
return self._module_flags_taint_post_4_10_rc1(taints, is_module) | ||
return self._module_flags_taint_pre_4_10_rc1(taints, is_module) | ||
|
||
def get_taints_parsed(self, taints: int, is_module: bool = False) -> List[str]: | ||
"""Convert the taints string to a 1-1 descriptor mapping. | ||
|
||
Args: | ||
taints: The taints value, represented by an integer | ||
is_module: Indicates if the taints value is associated with a built-in/LKM module | ||
|
||
Returns: | ||
A comprehensive (user-friendly) taint descriptor list. | ||
|
||
Documentation: | ||
- module_flags_taint kernel function | ||
""" | ||
comprehensive_taints = [] | ||
for character in self.get_taints_as_plain_string(taints, is_module): | ||
taint_flag = linux_constants.TAINT_FLAGS.get(character) | ||
if not taint_flag: | ||
comprehensive_taints.append(f"<UNKNOWN_TAINT_CHAR_{character}>") | ||
elif taint_flag.when_present: | ||
comprehensive_taints.append(taint_flag.desc) | ||
|
||
return comprehensive_taints |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -279,76 +279,29 @@ def get_symbol_by_address(self, wanted_sym_address): | |||
|
||||
return None | ||||
|
||||
def _module_flags_taints_pre_4_10_rc1(self) -> str: | ||||
"""Convert the module's taints value to a 1-1 character mapping. | ||||
Relies on statically defined taints mappings in the framework. | ||||
|
||||
Returns: | ||||
The raw taints string. | ||||
""" | ||||
taints_string = "" | ||||
for char, taint_flag in linux_constants.TAINT_FLAGS.items(): | ||||
if taint_flag.module and self.taints & taint_flag.shift: | ||||
taints_string += char | ||||
|
||||
return taints_string | ||||
|
||||
def _module_flags_taints_post_4_10_rc1(self) -> str: | ||||
"""Convert the module's taints value to a 1-1 character mapping. | ||||
Relies on kernel symbol embedded taints definitions. | ||||
|
||||
struct taint_flag { | ||||
char c_true; /* character printed when tainted */ | ||||
char c_false; /* character printed when not tainted */ | ||||
bool module; /* also show as a per-module taint flag */ | ||||
}; | ||||
|
||||
Returns: | ||||
The raw taints string. | ||||
""" | ||||
taints_string = "" | ||||
for i, taint_flag in enumerate(self.taint_flags_list): | ||||
c_true = chr(taint_flag.c_true) | ||||
c_false = chr(taint_flag.c_false) | ||||
if taint_flag.module and (self.taints & (1 << i)): | ||||
taints_string += c_true | ||||
elif taint_flag.module and c_false != " ": | ||||
taints_string += c_false | ||||
|
||||
return taints_string | ||||
|
||||
def get_taints_as_plain_string(self) -> str: | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are externally visible additions to the API, which means a MINOR version number somewhere, needs to go up. I suspect it may be the framework itself because I don't think anything further down is versioned? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed, we want to include this API in a more generic place, but keep these convenient self-contained functions. So right now I'm not sure of how many bumps are needed. |
||||
"""Convert the module's taints value to a 1-1 character mapping. | ||||
Convenient wrapper around framework's Tainting capabilities. | ||||
|
||||
Returns: | ||||
The raw taints string. | ||||
|
||||
Documentation: | ||||
- module_flags_taint kernel function | ||||
""" | ||||
|
||||
if self.taint_flags_list: | ||||
return self._module_flags_taints_post_4_10_rc1() | ||||
return self._module_flags_taints_pre_4_10_rc1() | ||||
return linux.Tainting( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't quite what I had in mind, because it requires There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so we will have to tweak the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Errr, there isn't really a way of versioning |
||||
self._context, | ||||
linux.LinuxUtilities.get_module_from_volobj_type(self._context, self).name, | ||||
).get_taints_as_plain_string(self.taints, True) | ||||
|
||||
def get_taints_parsed(self) -> List[str]: | ||||
"""Convert the module's taints string to a 1-1 descriptor mapping. | ||||
Convenient wrapper around framework's Tainting capabilities. | ||||
|
||||
Returns: | ||||
A comprehensive (user-friendly) taint descriptor list. | ||||
|
||||
Documentation: | ||||
- module_flags_taint kernel function | ||||
""" | ||||
comprehensive_taints = [] | ||||
for character in self.get_taints_as_plain_string(): | ||||
taint_flag = linux_constants.TAINT_FLAGS.get(character) | ||||
if not taint_flag: | ||||
comprehensive_taints.append(f"<UNKNOWN_TAINT_CHAR_{character}>") | ||||
elif taint_flag.when_present: | ||||
comprehensive_taints.append(taint_flag.desc) | ||||
|
||||
return comprehensive_taints | ||||
return linux.Tainting( | ||||
self._context, | ||||
linux.LinuxUtilities.get_module_from_volobj_type(self._context, self).name, | ||||
).get_taints_parsed(self.taints, True) | ||||
|
||||
@property | ||||
def section_symtab(self): | ||||
|
@@ -376,13 +329,6 @@ def section_strtab(self): | |||
return self.strtab | ||||
raise AttributeError("Unable to get strtab") | ||||
|
||||
@property | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This property needs to be exposed to avoid a bump too.... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This property was removed from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing it is a non-additive API change. That's why I said it needs to still be exposed there for anything that had been using it, otherwise a caller is going to call it expecting it to work and they'll get an ugly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ugh, I see, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It still needs to be exposed then, otherwise any code calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This property was added in this PR :), it was not present before. Changes might be confusing as we are moving things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it was only to be used internally, it needed to be called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a good point, however there is no need for bumping as it hasn't made it into the framework yet. I'll make sure to make it "private" though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, sorry, I know it's tricky to get right (and I'm getting lost looking at commit diffs inside the PR, so I thought it had already gone in). Yeah, anything that doesn't need to be accessed from the outside would should be marked with a leading |
||||
def taint_flags_list(self) -> Optional[List[interfaces.objects.ObjectInterface]]: | ||||
kernel = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self) | ||||
if kernel.has_symbol("taint_flags"): | ||||
return list(kernel.object_from_symbol("taint_flags")) | ||||
return None | ||||
|
||||
|
||||
class task_struct(generic.GenericIntelProcess): | ||||
def add_process_layer( | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might want to go in a
tainting.py
module, so it would becomelinux.tainting.Tainting
(and so that the init doesn't get too full).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also would need its own version number, and a required_framework_version for anything it made use of (and the
__init__
would need to check those...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, see my branch for an example.