22# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33#
44import re
5- import functools
65import logging
76import contextlib
8- from typing import List , Iterable
7+ from typing import List , Set , Tuple , Iterable
98from volatility3 .framework import renderers , interfaces , exceptions , objects
109from volatility3 .framework .constants .architectures import LINUX_ARCHS
1110from volatility3 .framework .renderers import format_hints
@@ -41,7 +40,21 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
4140 ),
4241 ]
4342
44- def _get_modules_memory_boundaries (self , vmlinux ):
43+ @staticmethod
44+ def get_modules_memory_boundaries (
45+ context : interfaces .context .ContextInterface ,
46+ vmlinux_module_name : str ,
47+ ) -> Tuple [int ]:
48+ """Determine the boundaries of the module allocation area
49+
50+ Args:
51+ context: The context to retrieve required elements (layers, symbol tables) from
52+ vmlinux_module_name: The name of the kernel module on which to operate
53+
54+ Returns:
55+ A tuple containing the minimum and maximum addresses for the module allocation area.
56+ """
57+ vmlinux = context .modules [vmlinux_module_name ]
4558 if vmlinux .has_symbol ("mod_tree" ):
4659 mod_tree = vmlinux .object_from_symbol ("mod_tree" )
4760 modules_addr_min = mod_tree .addr_min
@@ -73,8 +86,8 @@ def _get_modules_memory_boundaries(self, vmlinux):
7386
7487 return modules_addr_min , modules_addr_max
7588
89+ @staticmethod
7690 def _get_module_state_values_bytes (
77- self ,
7891 context : interfaces .context .ContextInterface ,
7992 vmlinux_module_name : str ,
8093 ) -> List [bytes ]:
@@ -97,12 +110,13 @@ def _get_module_state_values_bytes(
97110 ]
98111 return values_bytes
99112
100- def get_hidden_modules_vol2 (
101- self ,
113+ @classmethod
114+ def _get_hidden_modules_vol2 (
115+ cls ,
102116 context : interfaces .context .ContextInterface ,
103117 vmlinux_module_name : str ,
104- known_module_addresses ,
105- modules_memory_boundaries : tuple ,
118+ known_module_addresses : Set [ int ] ,
119+ modules_memory_boundaries : Tuple ,
106120 ) -> Iterable [interfaces .objects .ObjectInterface ]:
107121 """Enumerate hidden modules using the traditional implementation.
108122
@@ -111,6 +125,9 @@ def get_hidden_modules_vol2(
111125 Args:
112126 context: The context to retrieve required elements (layers, symbol tables) from
113127 vmlinux_module_name: The name of the kernel module on which to operate
128+ known_module_addresses: Set with known module addresses
129+ modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
130+
114131 Yields:
115132 module objects
116133 """
@@ -175,7 +192,7 @@ def get_hidden_modules_vol2(
175192 scan_buf = b"" .join (scan_list )
176193 del scan_list
177194
178- module_state_values_bytes = self ._get_module_state_values_bytes (
195+ module_state_values_bytes = cls ._get_module_state_values_bytes (
179196 context , vmlinux_module_name
180197 )
181198 values_bytes_pattern = b"|" .join (module_state_values_bytes )
@@ -190,27 +207,37 @@ def get_hidden_modules_vol2(
190207 if module and module .is_valid ():
191208 yield module
192209
193- @functools .cached_property
194- def module_address_alignment (self ) -> int :
210+ @classmethod
211+ def _get_module_address_alignment (
212+ cls ,
213+ context : interfaces .context .ContextInterface ,
214+ vmlinux_module_name : str ,
215+ ) -> int :
195216 """Obtain the module memory address alignment. This is only used with the fast scan method.
196217
197218 struct module is aligned to the L1 cache line, which is typically 64 bytes for most
198219 common i386/AMD64/ARM64 configurations. In some cases, it can be 128 bytes, but this
199220 will still work.
200221
222+ Args:
223+ context: The context to retrieve required elements (layers, symbol tables) from
224+ vmlinux_module_name: The name of the kernel module on which to operate
225+
201226 Returns:
202227 The struct module alignment
203228 """
204229 # FIXME: When dwarf2json/ISF supports type alignments. Read it directly from the type metadata
205- # The cached_property won't provide any benefits until then
230+ # Also, 'context' and 'vmlinux_module_name' are not used yet, but they will be needed to obtain
231+ # the type metadata
206232 return 64
207233
208- def get_hidden_modules_fast (
209- self ,
234+ @classmethod
235+ def _get_hidden_modules_fast (
236+ cls ,
210237 context : interfaces .context .ContextInterface ,
211238 vmlinux_module_name : str ,
212- known_module_addresses ,
213- modules_memory_boundaries : tuple ,
239+ known_module_addresses : Set [ int ] ,
240+ modules_memory_boundaries : Tuple ,
214241 ) -> Iterable [interfaces .objects .ObjectInterface ]:
215242 """Enumerate hidden modules by taking advantage of memory address alignment patterns
216243
@@ -229,6 +256,9 @@ def get_hidden_modules_fast(
229256 Args:
230257 context: The context to retrieve required elements (layers, symbol tables) from
231258 vmlinux_module_name: The name of the kernel module on which to operate
259+ known_module_addresses: Set with known module addresses
260+ modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
261+
232262 Yields:
233263 module objects
234264 """
@@ -237,12 +267,16 @@ def get_hidden_modules_fast(
237267
238268 module_addr_min , module_addr_max = modules_memory_boundaries
239269
240- module_state_values_bytes = self ._get_module_state_values_bytes (
270+ module_state_values_bytes = cls ._get_module_state_values_bytes (
271+ context , vmlinux_module_name
272+ )
273+
274+ module_address_alignment = cls ._get_module_address_alignment (
241275 context , vmlinux_module_name
242276 )
243277
244278 for module_addr in range (
245- module_addr_min , module_addr_max , self . module_address_alignment
279+ module_addr_min , module_addr_max , module_address_alignment
246280 ):
247281 if module_addr in known_module_addresses :
248282 continue
@@ -264,51 +298,59 @@ def get_hidden_modules_fast(
264298 if module and module .is_valid ():
265299 yield module
266300
267- def _validate_alignment_patterns (self , addresses : Iterable [int ]) -> bool :
301+ @staticmethod
302+ def _validate_alignment_patterns (
303+ addresses : Iterable [int ],
304+ address_alignment : int ,
305+ ) -> bool :
268306 """Check if the memory addresses meet our alignments patterns
269307
270308 Args:
271309 addresses: Iterable with the address values
310+ address_alignment: Number of bytes for alignment validation
272311
273312 Returns:
274313 True if all the addresses meet the alignment
275314 """
276- return all (addr % self . module_address_alignment == 0 for addr in addresses )
315+ return all (addr % address_alignment == 0 for addr in addresses )
277316
317+ @classmethod
278318 def get_hidden_modules (
279- self ,
319+ cls ,
280320 context : interfaces .context .ContextInterface ,
281321 vmlinux_module_name : str ,
322+ known_module_addresses : Set [int ],
323+ modules_memory_boundaries : Tuple ,
324+ fast_method : bool = False ,
282325 ) -> Iterable [interfaces .objects .ObjectInterface ]:
283326 """Enumerate hidden modules
284327
285328 Args:
286329 context: The context to retrieve required elements (layers, symbol tables) from
287330 vmlinux_module_name: The name of the kernel module on which to operate
331+ known_module_addresses: Set with known module addresses
332+ modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
333+ fast_method: If True, it uses the fast method. Otherwise, it uses the traditional one.
334+
288335 Yields:
289336 module objects
290337 """
291- vmlinux = context .modules [vmlinux_module_name ]
292- vmlinux_layer = context .layers [vmlinux .layer_name ]
293-
294- known_module_addresses = {
295- vmlinux_layer .canonicalize (module .vol .offset )
296- for module in lsmod .Lsmod .list_modules (context , vmlinux_module_name )
297- }
298-
299- modules_memory_boundaries = self ._get_modules_memory_boundaries (vmlinux )
300-
301- if self .config .get ("fast" ):
302- if self ._validate_alignment_patterns (known_module_addresses ):
303- scan_method = self .get_hidden_modules_fast
338+ if fast_method :
339+ module_address_alignment = cls ._get_module_address_alignment (
340+ context , vmlinux_module_name
341+ )
342+ if cls ._validate_alignment_patterns (
343+ known_module_addresses , module_address_alignment
344+ ):
345+ scan_method = cls ._get_hidden_modules_fast
304346 else :
305347 vollog .warning (
306- f"Module addresses aren't aligned to { self . module_address_alignment } bytes. "
348+ f"Module addresses aren't aligned to { module_address_alignment } bytes. "
307349 "Switching to the traditional scan method."
308350 )
309- scan_method = self . get_hidden_modules_vol2
351+ scan_method = cls . _get_hidden_modules_vol2
310352 else :
311- scan_method = self . get_hidden_modules_vol2
353+ scan_method = cls . _get_hidden_modules_vol2
312354
313355 yield from scan_method (
314356 context ,
@@ -317,9 +359,45 @@ def get_hidden_modules(
317359 modules_memory_boundaries ,
318360 )
319361
362+ @classmethod
363+ def get_lsmod_module_addresses (
364+ cls ,
365+ context : interfaces .context .ContextInterface ,
366+ vmlinux_module_name : str ,
367+ ) -> Set [int ]:
368+ """Obtain a set the known module addresses from linux.lsmod plugin
369+
370+ Args:
371+ context: The context to retrieve required elements (layers, symbol tables) from
372+ vmlinux_module_name: The name of the kernel module on which to operate
373+
374+ Returns:
375+ A set containing known kernel module addresses
376+ """
377+ vmlinux = context .modules [vmlinux_module_name ]
378+ vmlinux_layer = context .layers [vmlinux .layer_name ]
379+
380+ known_module_addresses = {
381+ vmlinux_layer .canonicalize (module .vol .offset )
382+ for module in lsmod .Lsmod .list_modules (context , vmlinux_module_name )
383+ }
384+ return known_module_addresses
385+
320386 def _generator (self ):
321387 vmlinux_module_name = self .config ["kernel" ]
322- for module in self .get_hidden_modules (self .context , vmlinux_module_name ):
388+ known_module_addresses = self .get_lsmod_module_addresses (
389+ self .context , vmlinux_module_name
390+ )
391+ modules_memory_boundaries = self .get_modules_memory_boundaries (
392+ self .context , vmlinux_module_name
393+ )
394+ for module in self .get_hidden_modules (
395+ self .context ,
396+ vmlinux_module_name ,
397+ known_module_addresses ,
398+ modules_memory_boundaries ,
399+ fast_method = self .config .get ("fast" ),
400+ ):
323401 module_addr = module .vol .offset
324402 module_name = module .get_name () or renderers .NotAvailableValue ()
325403 fields = (format_hints .Hex (module_addr ), module_name )
0 commit comments