44
55import logging
66import datetime
7- from typing import List , Iterable
7+ from typing import List , Generator , Tuple
88
99from volatility3 .framework import constants
1010from volatility3 .framework import interfaces , symbols , exceptions
1414from volatility3 .framework .renderers import format_hints , conversion
1515from volatility3 .framework .symbols import intermed
1616from volatility3 .plugins import timeliner
17+ from volatility3 .plugins .windows import modules
1718
1819vollog = logging .getLogger (__name__ )
1920
@@ -22,7 +23,7 @@ class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInt
2223 """Lists the unloaded kernel modules."""
2324
2425 _required_framework_version = (2 , 0 , 0 )
25- _version = (1 , 0 , 2 )
26+ _version = (2 , 0 , 0 )
2627
2728 @classmethod
2829 def get_requirements (cls ) -> List [interfaces .configuration .RequirementInterface ]:
@@ -75,10 +76,9 @@ def create_unloadedmodules_table(
7576 def list_unloadedmodules (
7677 cls ,
7778 context : interfaces .context .ContextInterface ,
78- layer_name : str ,
79- symbol_table : str ,
79+ kernel_module_name : str ,
8080 unloadedmodule_table_name : str ,
81- ) -> Iterable [ interfaces . objects . ObjectInterface ]:
81+ ) -> Generator [ Tuple [ str , int , int , datetime . datetime ], None , None ]:
8282 """Lists all the unloaded modules in the primary layer.
8383
8484 Args:
@@ -90,20 +90,16 @@ def list_unloadedmodules(
9090 A list of Unloaded Modules as retrieved from MmUnloadedDrivers
9191 """
9292
93- kvo = context .layers [layer_name ].config .get ("kernel_virtual_offset" , None )
94- if not kvo :
95- raise ValueError (
96- "Intel layer does not have an associated kernel virtual offset, failing"
97- )
98- ntkrnlmp = context .module (symbol_table , layer_name = layer_name , offset = kvo )
93+ ntkrnlmp = context .modules [kernel_module_name ]
94+
9995 unloadedmodules_offset = ntkrnlmp .get_symbol ("MmUnloadedDrivers" ).address
10096 unloadedmodules = ntkrnlmp .object (
10197 object_type = "pointer" ,
10298 offset = unloadedmodules_offset ,
10399 subtype = "array" ,
104100 )
105101 is_64bit = symbols .symbol_table_is_64bit (
106- context = context , symbol_table_name = symbol_table
102+ context = context , symbol_table_name = ntkrnlmp . symbol_table_name
107103 )
108104
109105 if is_64bit :
@@ -116,53 +112,86 @@ def list_unloadedmodules(
116112 object_type = unloaded_count_type , offset = last_unloadedmodule_offset
117113 )
118114
115+ # Bring down to default when smear present. Some samples had this completely broken
116+ if unloaded_count > 1024 :
117+ vollog .warning (
118+ f"Smeared array count found { unloaded_count } . Defaulting to 1024 elements."
119+ )
120+ unloaded_count = 1024
121+
119122 unloadedmodules_array = context .object (
120123 object_type = unloadedmodule_table_name
121124 + constants .BANG
122125 + "_UNLOADED_DRIVERS" ,
123- layer_name = layer_name ,
126+ layer_name = ntkrnlmp . layer_name ,
124127 offset = unloadedmodules ,
125128 )
126129 unloadedmodules_array .UnloadedDrivers .count = unloaded_count
127130
131+ kernel_space_start = modules .Modules .get_kernel_space_start (
132+ context , kernel_module_name
133+ )
134+
135+ address_mask = context .layers [ntkrnlmp .layer_name ].address_mask
136+
128137 for driver in unloadedmodules_array .UnloadedDrivers :
129138 # Mass testing led to dozens of samples backtracing on this plugin when
130139 # accessing members of modules coming out this list
131140 # Given how often temporary drivers load and unload on Win10+, I
132141 # assume the chance for smear is very high
133142 try :
134- driver .StartAddress
135- driver .EndAddress
136- driver .CurrentTime
137- yield driver
143+ start_address = driver .StartAddress & address_mask
144+ end_address = driver .EndAddress & address_mask
145+ current_time = driver .CurrentTime
146+ driver_name = driver . Name . String
138147 except exceptions .InvalidAddressException :
139148 continue
140149
150+ if (
151+ current_time > 1024
152+ and start_address > kernel_space_start
153+ and start_address & 0xFFF == 0x0
154+ and end_address & 0xFFF == 0x0
155+ and end_address > kernel_space_start
156+ ):
157+ yield driver_name , start_address , end_address , current_time
158+
141159 def _generator (self ):
142160 kernel = self .context .modules [self .config ["kernel" ]]
143161
162+ if not kernel .has_symbol ("MmUnloadedDrivers" ):
163+ vollog .error (
164+ "The symbol table for this sample is missing the `MmUnloadedDrivers` symbol. Cannot proceed."
165+ )
166+ return
167+
168+ if not kernel .has_symbol ("MmLastUnloadedDriver" ):
169+ vollog .error (
170+ "The symbol table for this sample is missing the `MmLastUnloadededDriver` symbol. Cannot proceed."
171+ )
172+ return
173+
144174 unloadedmodule_table_name = self .create_unloadedmodules_table (
145175 self .context , kernel .symbol_table_name , self .config_path
146176 )
147177
148- for mod in self .list_unloadedmodules (
178+ for (
179+ driver_name ,
180+ start_address ,
181+ end_address ,
182+ current_time ,
183+ ) in self .list_unloadedmodules (
149184 self .context ,
150- kernel .layer_name ,
151- kernel .symbol_table_name ,
185+ self .config ["kernel" ],
152186 unloadedmodule_table_name ,
153187 ):
154- try :
155- name = mod .Name .String
156- except exceptions .InvalidAddressException :
157- name = renderers .UnreadableValue ()
158-
159188 yield (
160189 0 ,
161190 (
162- name ,
163- format_hints .Hex (mod . StartAddress ),
164- format_hints .Hex (mod . EndAddress ),
165- conversion .wintime_to_datetime (mod . CurrentTime ),
191+ driver_name ,
192+ format_hints .Hex (start_address ),
193+ format_hints .Hex (end_address ),
194+ conversion .wintime_to_datetime (current_time ),
166195 ),
167196 )
168197
0 commit comments