@@ -112,6 +112,9 @@ def get_basic_information(self):
112
112
'process_id' : self .process_id , 'filename' : self .filename ,
113
113
'number_of_retrieved_pages' : len (self .pages )}
114
114
115
+ def get_information_for_metadata_file (self ):
116
+ return {'path' : self .path , 'base_address' : self .base_address , 'size' : self .size , 'process_id' : self .process_id }
117
+
115
118
116
119
def delete_dmp_files (modules : List [Module ]) -> None :
117
120
for module in modules :
@@ -288,6 +291,46 @@ def dump_page(page: Page, page_offset: int, file_path: str) -> None:
288
291
dumped_page .write (page_contents )
289
292
290
293
294
+ def detect_dll_proxying (modules : List [Module ], output_directory : str , detection_info_filename : str , logger ) -> List [
295
+ str ]:
296
+ mapped_modules_info : List [Dict [str , Any ]] = []
297
+ for module in modules :
298
+ mapped_modules_info .append (module .get_information_for_metadata_file ())
299
+ detection_info : Dict [str , Any ] = {'mapped_modules' : mapped_modules_info }
300
+ most_common_path : str = get_most_common_element ([module .path .casefold () for module in modules ])
301
+ most_common_size : int = get_most_common_element ([module .size for module in modules ])
302
+
303
+ # Look at the cases where the path or the size do not match with the most common ones and mark them as suspicious
304
+ suspicious_modules : List [Module ] = []
305
+ for module in modules :
306
+ if module .path .casefold () != most_common_path or module .size != most_common_size :
307
+ suspicious_modules .append (module )
308
+
309
+ detection_result_key : str = 'dll_proxying_detection_result'
310
+ if suspicious_modules :
311
+ detection_info [detection_result_key ] = True
312
+ else :
313
+ detection_info [detection_result_key ] = False
314
+
315
+ detection_details : Dict [str , Any ] = {}
316
+ suspicious_processes : List [int ] = []
317
+ for suspicious_module in suspicious_modules :
318
+ suspicious_processes .append (suspicious_module .process_id )
319
+
320
+ detection_details ['suspicious_processes' ] = suspicious_processes
321
+ detection_info ['detection_details' ] = detection_details
322
+
323
+ files_generated : List [str ] = []
324
+ detection_info_path : str = os .path .join (output_directory , detection_info_filename )
325
+ with open (detection_info_path , 'w' ) as detection_details_file :
326
+ json .dump (detection_info , detection_details_file , ensure_ascii = False , indent = 4 )
327
+
328
+ files_generated .append (detection_info_path )
329
+ logger .info (
330
+ f'\n The --detect option was supplied to detect the presence of the DLL proxying technique. For more details check the { detection_info_filename } file.' )
331
+ return files_generated
332
+
333
+
291
334
def mix_modules (modules : List [Module ], output_directory : str , mixed_module_filename : str ,
292
335
mixed_module_metadata_filename : str , dump_anomalies : bool , logger , is_modex_calling : bool ,
293
336
start_time ) -> List [str ]:
@@ -428,14 +471,18 @@ def mix_modules(modules: List[Module], output_directory: str, mixed_module_filen
428
471
logger .info (
429
472
f'\t \t { private_bytes_retrieved / bytes_retrieved :.2%} were private ({ private_bytes_retrieved } private bytes in total)' )
430
473
474
+ mixed_modules_info : List [Dict [str , Any ]] = []
475
+ for module in modules :
476
+ mixed_modules_info .append (module .get_information_for_metadata_file ())
477
+
431
478
# Join all the metadata about the mixed module
432
479
mixed_module_metadata : Dict [str , Any ] = {'module_path' : module_path .casefold (),
433
480
'module_base_address' : hex (module_base_address ),
434
481
'module_size' : module_size ,
435
482
'general_statistics' : {'bytes_retrieved' : bytes_retrieved ,
436
483
'shared_bytes_retrieved' : shared_bytes_retrieved ,
437
484
'private_bytes_retrieved' : private_bytes_retrieved },
438
- 'pages' : mixed_module_pages_metadata }
485
+ 'pages' : mixed_module_pages_metadata , 'mixed_modules' : mixed_modules_info }
439
486
440
487
# Statistics regarding a Modex extraction
441
488
if is_modex_calling :
@@ -504,6 +551,10 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
504
551
requirements .BooleanRequirement (name = 'dump_anomalies' ,
505
552
description = "When there are different shared pages at the same offset, dump those pages" ,
506
553
default = False ,
554
+ optional = True ),
555
+ requirements .BooleanRequirement (name = 'detect' ,
556
+ description = "Detect the presence of the DLL proxying technique" ,
557
+ default = False ,
507
558
optional = True )
508
559
]
509
560
@@ -517,6 +568,7 @@ def run(self):
517
568
518
569
module_supplied : str = self .config ['module' ].casefold ()
519
570
dump_anomalies : bool = self .config ['dump_anomalies' ]
571
+ is_detect_option_supplied : bool = self .config ['detect' ]
520
572
modules_to_mix : List [Module ] = []
521
573
files_finally_generated : List [str ] = [log_file_path ]
522
574
@@ -550,26 +602,31 @@ def run(self):
550
602
module_size = None
551
603
552
604
if module_base_address is not None and module_size is not None :
553
- file_handle = dlllist .DllList .dump_pe (self .context ,
554
- pe_table_name ,
555
- entry ,
556
- self .open ,
557
- process_layer_name ,
558
- prefix = f'pid.{ process_id } .' )
559
- if file_handle :
560
- file_handle .close ()
561
- dumped_module_filename = file_handle .preferred_filename
605
+ if is_detect_option_supplied : # In this case, there is no need to dump the modules
562
606
modules_to_mix .append (
563
- Module (module_name , module_path , module_base_address , module_size ,
564
- process_id , dumped_module_filename , []))
607
+ Module (module_name , module_path , module_base_address , module_size , process_id , '' ,
608
+ []))
609
+ else :
610
+ file_handle = dlllist .DllList .dump_pe (self .context ,
611
+ pe_table_name ,
612
+ entry ,
613
+ self .open ,
614
+ process_layer_name ,
615
+ prefix = f'pid.{ process_id } .' )
616
+ if file_handle :
617
+ file_handle .close ()
618
+ dumped_module_filename = file_handle .preferred_filename
619
+ modules_to_mix .append (
620
+ Module (module_name , module_path , module_base_address , module_size ,
621
+ process_id , dumped_module_filename , []))
565
622
except exceptions .InvalidAddressException :
566
623
pass
567
624
568
625
if not modules_to_mix :
569
626
logger .info ('The module supplied is not mapped in any process' )
570
627
return renderers .TreeGrid ([("Filename" , str )], self ._generator (files_finally_generated ))
571
628
572
- logger .info (f'Modules to mix (before validation) ({ len (modules_to_mix )} ):' )
629
+ logger .info (f'Mapped modules (before validation) ({ len (modules_to_mix )} ):' )
573
630
for module_to_mix in modules_to_mix :
574
631
logger .info (f'\t { module_to_mix .get_basic_information ()} ' )
575
632
@@ -578,7 +635,13 @@ def run(self):
578
635
579
636
if not modules_to_mix :
580
637
logger .info (
581
- '\n All the identified modules are under the C:\\ Windows\\ SysWOW64 directory, as a result, they cannot be mixed' )
638
+ '\n All the identified modules are under the C:\\ Windows\\ SysWOW64 directory, as a result, the execution cannot proceed' )
639
+ return renderers .TreeGrid ([("Filename" , str )], self ._generator (files_finally_generated ))
640
+
641
+ if is_detect_option_supplied :
642
+ detection_info_filename : str = 'detection.json'
643
+ files_finally_generated += detect_dll_proxying (modules_to_mix , output_directory , detection_info_filename ,
644
+ logger )
582
645
return renderers .TreeGrid ([("Filename" , str )], self ._generator (files_finally_generated ))
583
646
584
647
# Make sure that the modules can be mixed
0 commit comments