55import time
66from nxc .paths import NXC_PATH
77
8+
89class NXCModule :
910 # Extracts content from Windows Notepad binary tab state files
1011 # Module by @termanix
@@ -25,15 +26,15 @@ def options(self, context, module_options):
2526 def extract_strings (self , data , min_length = 4 ):
2627 """Extract printable strings from binary data, similar to the strings command."""
2728 results = []
28-
29+
2930 # ASCII strings extraction
3031 ascii_strings = re .findall (b"[ -~]{%d,}" % min_length , data )
3132 for s in ascii_strings :
3233 try :
3334 results .append (("ASCII" , s .decode ("ascii" )))
3435 except Exception as e :
3536 self .context .log .fail (f"Failed extracting ASCII strings: { e } " )
36-
37+
3738 # UTF-16LE strings extraction (common in Windows)
3839 utf16_pattern = re .compile (b"(?:[\x20 -\x7E ]\x00 ){%d,}" % min_length )
3940 utf16_strings = utf16_pattern .findall (data )
@@ -43,33 +44,30 @@ def extract_strings(self, data, min_length=4):
4344 results .append (("UTF-16LE" , decoded ))
4445 except Exception as e :
4546 self .context .log .fail (f"Failed extracting UTF-16LE strings: { e } " )
46-
47+
4748 return results
4849
4950 def is_meaningful_content (self , string ):
5051 """Check if a string has meaningful content."""
5152 # Filter out strings that are just repetitions of the same character
5253 if len (set (string )) <= 2 and len (string ) > 4 :
5354 return False
54-
55+
5556 # Filter out strings that don't have any letters or numbers
5657 if not any (c .isalnum () for c in string ):
5758 return False
58-
59+
5960 # Filter out strings that look like memory addresses or hex dumps
6061 if re .match (r"^[0-9A-F]+$" , string ) and len (string ) >= 8 :
6162 return False
62-
63+
6364 # Filter out strings that are just whitespace or control characters
6465 if string .isspace ():
6566 return False
66-
67+
6768 # Filter out common binary file markers that aren't actual content
6869 common_garbage = ["NULL" , "true" , "false" , "xmlns" , "http://" , "https://" , "COM1" , "COM2" , "COM3" ]
69- if string in common_garbage :
70- return False
71-
72- return True
70+ return string not in common_garbage
7371
7472 def read_and_decode_file (self , connection , context , file_path , user ):
7573 buf = BytesIO ()
@@ -90,15 +88,14 @@ def read_and_decode_file(self, connection, context, file_path, user):
9088 else :
9189 # If it's a different error, just skip this file
9290 context .log .debug (f"Error accessing { file_path } : { e } " )
93-
91+
9492 buf .seek (0 )
9593 binary_data = buf .read ()
96-
94+
9795 # Extract meaningful strings
98- return [(encoding , string ) for encoding , string in self .extract_strings (binary_data )
96+ return [(encoding , string ) for encoding , string in self .extract_strings (binary_data )
9997 if self .is_meaningful_content (string )]
10098
101-
10299 def on_admin_login (self , context , connection ):
103100 found = 0
104101 context .log .display ("Searching for Notepad cache..." )
@@ -114,7 +111,7 @@ def on_admin_login(self, context, connection):
114111 for file in connection .conn .listPath ("C$" , f"{ notepad_dir } \\ *" ):
115112 if file .get_longname () not in self .false_positive and file .get_longname ().endswith (".bin" ):
116113 file_path = f"{ notepad_dir } { file .get_longname ()} "
117-
114+
118115 # Read the binary file
119116 meaningful_strings = self .read_and_decode_file (connection , context , file_path , directory .get_longname ())
120117
@@ -124,7 +121,7 @@ def on_admin_login(self, context, connection):
124121
125122 # Output content
126123 content_lines = []
127-
124+
128125 # First loop to handle meaningful strings
129126 for _ , string in meaningful_strings :
130127 if bool (re .match (self .FILE_PATH_REGEX , string )): # Only needed if checking locally
@@ -138,7 +135,7 @@ def on_admin_login(self, context, connection):
138135 else :
139136 context .log .highlight (f"\t { string } " )
140137 content_lines .append (string ) # Store the string value only
141-
138+
142139 # Save to file
143140 filename = f"{ connection .host } _{ directory .get_longname ()} _notepad_tabstate_{ found } .txt"
144141 export_path = join (NXC_PATH , "modules" , "notepad" )
0 commit comments