-
Notifications
You must be signed in to change notification settings - Fork 484
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
Windows: Allow windows.dlllist to report back DLLs from wow64 processes #1572
base: develop
Are you sure you want to change the base?
Changes from all commits
bd83369
c317cad
d172e12
9ce1fd7
02d0790
af9bd53
495d646
830c28a
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 |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
from volatility3.framework.renderers import conversion | ||
from volatility3.framework.symbols import generic | ||
from volatility3.framework.symbols.windows.extensions import pool | ||
from volatility3.framework.symbols import windows | ||
|
||
vollog = logging.getLogger(__name__) | ||
|
||
|
@@ -775,39 +776,131 @@ def get_peb(self) -> interfaces.objects.ObjectInterface: | |
) | ||
return peb | ||
|
||
def get_peb32(self) -> interfaces.objects.ObjectInterface: | ||
"""Constructs a PEB32 object""" | ||
if constants.BANG not in self.vol.type_name: | ||
raise ValueError( | ||
f"Invalid symbol table name syntax (no {constants.BANG} found)" | ||
) | ||
|
||
# add_process_layer can raise InvalidAddressException. | ||
# if that happens, we let the exception propagate upwards | ||
proc_layer_name = self.add_process_layer() | ||
proc_layer = self._context.layers[proc_layer_name] | ||
|
||
# Determine if process is running under WOW64. | ||
if self.get_is_wow64(): | ||
proc = self.get_wow_64_process() | ||
else: | ||
return None | ||
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 means the function signature needs to be |
||
# Confirm WoW64Process points to a valid process address | ||
if not proc_layer.is_valid(proc): | ||
raise exceptions.InvalidAddressException( | ||
proc_layer_name, proc, f"Invalid Wow64Process address at {self.Peb:0x}" | ||
) | ||
|
||
# Leverage the context of existing symbol table to help configure | ||
# a new symbol table for 32-bit types | ||
sym_table = self.get_symbol_table_name() | ||
config_path = self._context.symbol_space[sym_table].config_path | ||
|
||
# Load the 32-bit types into a new symbol space | ||
# We use the WindowsKernelIntermedSymbols class to make | ||
# sure we get all the object helpers. For example, traversing | ||
# linked-lists. | ||
self._32bit_table_name = windows.WindowsKernelIntermedSymbols.create( | ||
self._context, config_path, "windows", "wow64" | ||
) | ||
|
||
# windows 10 | ||
if self._context.symbol_space.has_type( | ||
sym_table + constants.BANG + "_EWOW64PROCESS" | ||
): | ||
offset = proc.Peb | ||
|
||
# vista sp0-sp1 and 2003 sp1-sp2 | ||
elif self._context.symbol_space.has_type( | ||
sym_table + constants.BANG + "_WOW64_PROCESS" | ||
): | ||
offset = proc.Wow64 | ||
|
||
else: | ||
offset = proc | ||
|
||
peb32 = self._context.object( | ||
f"{self._32bit_table_name}{constants.BANG}_PEB32", | ||
layer_name=proc_layer_name, | ||
offset=offset, | ||
) | ||
return peb32 | ||
|
||
def load_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: | ||
"""Generator for DLLs in the order that they were loaded.""" | ||
|
||
try: | ||
peb = self.get_peb() | ||
yield from peb.Ldr.InLoadOrderModuleList.to_list( | ||
f"{self.get_symbol_table_name()}{constants.BANG}_LDR_DATA_TABLE_ENTRY", | ||
"InLoadOrderLinks", | ||
) | ||
pebs = [ | ||
self.get_peb(), | ||
self.get_peb32(), | ||
] | ||
for peb in pebs: | ||
if peb: | ||
sym_table = self.get_symbol_table_name() | ||
if peb.Ldr.vol.type_name.endswith("unsigned long"): | ||
ldr_data = self._context.symbol_space.get_type( | ||
self._32bit_table_name + constants.BANG + "_PEB_LDR_DATA" | ||
) | ||
peb.Ldr = peb.Ldr.cast("pointer", subtype=ldr_data) | ||
sym_table = self._32bit_table_name | ||
yield from peb.Ldr.InLoadOrderModuleList.to_list( | ||
f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", | ||
"InLoadOrderLinks", | ||
) | ||
except exceptions.InvalidAddressException: | ||
return None | ||
|
||
def init_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: | ||
"""Generator for DLLs in the order that they were initialized""" | ||
|
||
try: | ||
peb = self.get_peb() | ||
yield from peb.Ldr.InInitializationOrderModuleList.to_list( | ||
f"{self.get_symbol_table_name()}{constants.BANG}_LDR_DATA_TABLE_ENTRY", | ||
"InInitializationOrderLinks", | ||
) | ||
pebs = [ | ||
self.get_peb(), | ||
self.get_peb32(), | ||
] | ||
for peb in pebs: | ||
if peb: | ||
sym_table = self.get_symbol_table_name() | ||
if peb.Ldr.vol.type_name.endswith("unsigned long"): | ||
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. Hard coding specific types in may be troublesome in the future. There was an instance in the linux world where the pointers when from 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. Would there be a big problem in just always casting the value, regardless of whether it's already the right type, or just verify it's a point to the right subtype and otherwise cast it? |
||
ldr_data = self._context.symbol_space.get_type( | ||
self._32bit_table_name + constants.BANG + "_PEB_LDR_DATA" | ||
) | ||
peb.Ldr = peb.Ldr.cast("pointer", subtype=ldr_data) | ||
sym_table = self._32bit_table_name | ||
yield from peb.Ldr.InInitializationOrderModuleList.to_list( | ||
f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", | ||
"InInitializationOrderLinks", | ||
) | ||
except exceptions.InvalidAddressException: | ||
return None | ||
|
||
def mem_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: | ||
"""Generator for DLLs in the order that they appear in memory""" | ||
|
||
try: | ||
peb = self.get_peb() | ||
yield from peb.Ldr.InMemoryOrderModuleList.to_list( | ||
f"{self.get_symbol_table_name()}{constants.BANG}_LDR_DATA_TABLE_ENTRY", | ||
"InMemoryOrderLinks", | ||
) | ||
pebs = [ | ||
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 feels like duplicating quite a big more code than the previous version. It might be worth pulling out the pointer check on these into a separate little private subfunction? |
||
self.get_peb(), | ||
self.get_peb32(), | ||
] | ||
for peb in pebs: | ||
if peb: | ||
sym_table = self.get_symbol_table_name() | ||
if peb.Ldr.vol.type_name.endswith("unsigned long"): | ||
ldr_data = self._context.symbol_space.get_type( | ||
self._32bit_table_name + constants.BANG + "_PEB_LDR_DATA" | ||
) | ||
peb.Ldr = peb.Ldr.cast("pointer", subtype=ldr_data) | ||
sym_table = self._32bit_table_name | ||
yield from peb.Ldr.InMemoryOrderModuleList.to_list( | ||
f"{sym_table}{constants.BANG}" + "_LDR_DATA_TABLE_ENTRY", | ||
"InMemoryOrderLinks", | ||
) | ||
except exceptions.InvalidAddressException: | ||
return None | ||
|
||
|
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.
I can't recall if this is strictly true. I suspect it's true but I think it's possible you could ask the symbol table directly for this type, and it wouldn't necessarily include the table name. Were you running into errors or is this just defensive coding?