Files

160 lines
7.0 KiB
Python

import os
import csv
import argparse
import pefile
def auto_int(x):
"""Helper to accept both decimal integers and hex strings (0x...) from CLI."""
return int(x, 0)
def parse_flat_file(file_path, list_type_label):
"""
Parses a flat text file containing only clean DLL filenames (one per line).
Normalizes them to lowercase for robust, case-insensitive comparison.
"""
names_set = set()
if not file_path:
return names_set
print(f"[*] Loading {list_type_label} from: '{file_path}'")
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
for line in f:
dll_name = line.strip().lower()
# Ignore blank lines or markdown-style separator lines
if not dll_name or dll_name.startswith('---') or dll_name.startswith('[+]'):
continue
names_set.add(dll_name)
print(f"[+] Loaded {len(names_set)} modules into {list_type_label} filter.")
except Exception as e:
print(f"[-] Warning: Failed to read {list_type_label} file: {e}")
return names_set
def find_phantom_dll_candidates(target_dir, min_file_size_mb, min_text_section_size, excluded_dlls, included_dlls, log_file=None, console_output=True):
"""
Scans a directory for DLLs matching structural criteria.
Supports exclusion filters, targeted scope filters, and conditional pipeline logging.
"""
min_file_size_bytes = min_file_size_mb * 1024 * 1024
candidates = []
if console_output:
print(f"[*] Scanning Target Directory: '{target_dir}'")
print(f"[*] Filtering for Files : > {min_file_size_mb}MB")
print(f"[*] Required .text Space : {hex(min_text_section_size)} bytes")
if included_dlls:
print(f"[*] Targeted Include Filter : Active ({len(included_dlls)} specific targets allowed)")
if excluded_dlls:
print(f"[*] Exclusion Filter : Active ({len(excluded_dlls)} modules blacklisted)")
print("-" * 140)
print(f"{'Full File Path':<85} | {'File Size (MB)':<15} | {'Size of .text':<15} | {'Virtual Address':<15}")
print("-" * 140)
for root, _, files in os.walk(target_dir):
for file in files:
file_lower = file.lower()
if not file_lower.endswith('.dll'):
continue
# Gate 1: Include lookup list filter bounds
if included_dlls and (file_lower not in included_dlls):
continue
# Gate 2: Exclude lookup blacklist filter bounds
if excluded_dlls and (file_lower in excluded_dlls):
continue
file_path = os.path.join(root, file)
try:
file_size = os.path.getsize(file_path)
if file_size < min_file_size_bytes:
continue
pe = pefile.PE(file_path, fast_load=True)
for section in pe.sections:
section_name = section.Name.decode('utf-8', errors='ignore').strip('\x00')
if section_name == '.text':
v_size = section.Misc_VirtualSize
if v_size >= min_text_section_size:
file_size_mb = file_size / (1024 * 1024)
if console_output:
print(f"{file_path:<85} | {file_size_mb:<15.2f} | {hex(v_size):<15} | {hex(section.VirtualAddress):<15}")
candidates.append({
'Name': file, # Base filename matches list-process-dlls paradigm expect layouts
'TextSectionSize': v_size
})
break
except (pefile.PEFormatError, PermissionError, FileNotFoundError):
continue
if console_output:
print("-" * 140)
print(f"[*] Found {len(candidates)} potential candidates matching the criteria.")
# --- PIPELINE SEAMLESS AUTOMATION EXPORT ---
if log_file and candidates:
# Field structure exactly matching list-process-dlls layout configuration needs
fieldnames = ['Name', 'TextSectionSize']
try:
with open(log_file, mode='w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for cand in candidates:
writer.writerow({
'Name': cand['Name'],
'TextSectionSize': cand['TextSectionSize']
})
print(f"[+] Operational structural blueprint saved to: '{log_file}'")
except Exception as file_err:
print(f"[-] Error: Failed to write pipeline target spreadsheet: {file_err}")
return candidates
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Find DLL candidates with targeted tracking and exclusion parameters.")
# Positionals & core targets
parser.add_argument("size", type=auto_int, help="The total required size of the .text section (e.g., 0x80000)")
parser.add_argument("-d", "--dir", type=str, default=r"C:\Windows\System32", help="Target directory to scan (default: C:\\Windows\\System32)")
parser.add_argument("-m", "--min-size", type=float, default=1.0, help="Minimum file size on disk in MB (default: 1.0)")
# Flat configurations files switches
parser.add_argument("-x", "--exclude", type=str, default=None, help="Path to a text file containing specific DLLs to EXCLUDE")
parser.add_argument("-i", "--include", type=str, default=None, help="Path to a text file containing specific DLLs to INCLUDE")
# Pipeline output and terminal display switches
parser.add_argument("-l", "--log", type=str, default=None, help="Destination pipeline tracking file path to export CSV output parameters.")
parser.add_argument("-c", "--console", dest="console", action="store_true", default=True, help="Print structured layout results table directly to screen terminal (Default).")
parser.add_argument("--no-console", dest="console", action="store_false", help="Suppress terminal visual representation during backend background tasks loops.")
args = parser.parse_args()
if not os.path.isdir(args.dir):
print(f"[-] Error: '{args.dir}' is not a valid directory.")
exit(1)
# Parse flat filters definitions
excluded_modules = parse_flat_file(args.exclude, "EXCLUDE_MODULES")
included_modules = parse_flat_file(args.include, "INCLUDE_MODULES")
find_phantom_dll_candidates(
target_dir=args.dir,
min_file_size_mb=args.min_size,
min_text_section_size=args.size,
excluded_dlls=excluded_modules,
included_dlls=included_modules,
log_file=args.log,
console_output=args.console
)