Skip to content

Commit 696c937

Browse files
authored
Remove comments and fix grammar
1 parent 0010719 commit 696c937

2 files changed

Lines changed: 344 additions & 0 deletions

File tree

requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
rich
2+
psutil
3+
pynvml
4+
numba
5+
numpy
6+
cupy

search.py

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
import os
2+
import sys
3+
import threading
4+
import queue
5+
import base64
6+
import time
7+
import argparse
8+
import psutil
9+
import pynvml
10+
from rich.live import Live
11+
from rich.table import Table
12+
from rich.console import Console
13+
14+
from numba import cuda
15+
import numpy as np
16+
17+
def check_system_limits():
18+
max_ram_gb = 32
19+
max_vram_gb = 8
20+
21+
total_ram = psutil.virtual_memory().total / (1024 ** 3)
22+
if total_ram > max_ram_gb:
23+
print(f"[WARN] {total_ram:.1f} GB RAM - Not supported")
24+
25+
try:
26+
pynvml.nvmlInit()
27+
handle = pynvml.nvmlDeviceGetHandleByIndex(0)
28+
mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
29+
total_vram = mem_info.total / (1024 ** 3)
30+
if total_vram > max_vram_gb:
31+
print(f"[WARN] {total_vram:.1f} GB VRAM - Not supported")
32+
pynvml.nvmlShutdown()
33+
except:
34+
print("[INFO] Couldn’t check VRAM (no supported GPU or nvml issue)")
35+
36+
37+
def main():
38+
check_system_limits()
39+
args = parse_args()
40+
41+
def chunk_reader(fobj, chunk_size=2*1024*1024):
42+
while True:
43+
data = fobj.read(chunk_size)
44+
if not data:
45+
break
46+
yield data
47+
48+
def is_system_folder(path):
49+
sys_folders = ['AppData', 'Local', 'Microsoft', 'Windows', 'ProgramData', 'Program Files', 'Program Files (x86)']
50+
return any(part in path.split(os.sep) for part in sys_folders)
51+
52+
@cuda.jit
53+
def cuda_search_kernel(data, pattern, result):
54+
i = cuda.grid(1)
55+
n = data.size
56+
m = pattern.size
57+
if i + m <= n:
58+
match = True
59+
for j in range(m):
60+
if data[i + j] != pattern[j]:
61+
match = False
62+
break
63+
if match:
64+
result[0] = 1
65+
66+
def cuda_search(data_bytes, pattern_bytes):
67+
data_np = np.frombuffer(data_bytes, dtype=np.uint8)
68+
pattern_np = np.frombuffer(pattern_bytes, dtype=np.uint8)
69+
result = np.zeros(1, dtype=np.uint8)
70+
threads_per_block = 256
71+
blocks = (len(data_np) + threads_per_block - 1) // threads_per_block
72+
d_data = cuda.to_device(data_np)
73+
d_pattern = cuda.to_device(pattern_np)
74+
d_result = cuda.to_device(result)
75+
cuda_search_kernel[blocks, threads_per_block](d_data, d_pattern, d_result)
76+
d_result.copy_to_host(result)
77+
return result[0] == 1
78+
79+
class SearchThread(threading.Thread):
80+
def __init__(self, tid, task_queue, results, args, status_dict, counter_lock, files_scanned):
81+
super().__init__()
82+
self.tid = tid
83+
self.task_queue = task_queue
84+
self.results = results
85+
self.args = args
86+
self.status_dict = status_dict
87+
self.daemon = True
88+
self.counter_lock = counter_lock
89+
self.files_scanned = files_scanned
90+
self.cuda_available = False
91+
try:
92+
cuda.select_device(0)
93+
self.cuda_available = True
94+
except cuda.cudadrv.error.CudaSupportError:
95+
self.cuda_available = False
96+
97+
def run(self):
98+
while True:
99+
try:
100+
path = self.task_queue.get(timeout=1)
101+
except queue.Empty:
102+
self.status_dict[self.tid] = "> Idle"
103+
break
104+
105+
self.status_dict[self.tid] = f"[INFO] Scanning {os.path.basename(path)}"
106+
107+
if self.args.skip_sys and is_system_folder(path):
108+
self.status_dict[self.tid] = "> Idle"
109+
self.task_queue.task_done()
110+
continue
111+
112+
if os.path.isdir(path):
113+
if not self.args.nosub:
114+
try:
115+
for entry in os.listdir(path):
116+
full_path = os.path.join(path, entry)
117+
self.task_queue.put(full_path)
118+
except Exception as e:
119+
if self.args.errors:
120+
print(f"[ERROR] Cannot list {path}: {e}")
121+
else:
122+
filename = os.path.basename(path)
123+
124+
if self.args.fc and filename != self.args.fc:
125+
self.status_dict[self.tid] = "> Idle"
126+
self.task_queue.task_done()
127+
continue
128+
129+
if self.args.ext and not filename.lower().endswith(self.args.ext.lower()):
130+
self.status_dict[self.tid] = "> Idle"
131+
self.task_queue.task_done()
132+
continue
133+
134+
try:
135+
fsize = os.path.getsize(path)
136+
if self.args.minsize and fsize < self.args.minsize:
137+
self.status_dict[self.tid] = "> Idle"
138+
self.task_queue.task_done()
139+
continue
140+
if self.args.maxsize and fsize > self.args.maxsize:
141+
self.status_dict[self.tid] = "> Idle"
142+
self.task_queue.task_done()
143+
continue
144+
145+
if self.args.amongus and fsize == 69420:
146+
print(f"[?] Sus file found: {path}")
147+
148+
except Exception as e:
149+
if self.args.errors:
150+
print(f"[ERROR] cannot get size {path}: {e}")
151+
self.status_dict[self.tid] = "> Idle"
152+
self.task_queue.task_done()
153+
continue
154+
155+
found_content = False
156+
try:
157+
with open(path, 'rb') as f:
158+
for chunk in chunk_reader(f):
159+
if self.args.fake:
160+
found_content = True
161+
break
162+
163+
if self.args.content:
164+
search_bytes = self.args.content.encode(errors='ignore')
165+
166+
if self.cuda_available:
167+
if cuda_search(chunk, search_bytes):
168+
found_content = True
169+
break
170+
else:
171+
if search_bytes.lower() in chunk.lower():
172+
found_content = True
173+
break
174+
175+
elif self.args.hex:
176+
try:
177+
target_bytes = bytes.fromhex(self.args.hex)
178+
if self.cuda_available:
179+
if cuda_search(chunk, target_bytes):
180+
found_content = True
181+
break
182+
else:
183+
if target_bytes in chunk:
184+
found_content = True
185+
break
186+
except Exception:
187+
pass
188+
elif self.args.base64:
189+
try:
190+
target_bytes = base64.b64decode(self.args.base64)
191+
if self.cuda_available:
192+
if cuda_search(chunk, target_bytes):
193+
found_content = True
194+
break
195+
else:
196+
if target_bytes in chunk:
197+
found_content = True
198+
break
199+
except Exception:
200+
pass
201+
202+
if self.args.fc:
203+
if found_content or not self.args.content:
204+
self.results.append(path)
205+
if not self.args.quiet:
206+
print(f"[MATCH] {path} (filename match)")
207+
else:
208+
if self.args.content and found_content:
209+
self.results.append(path)
210+
if not self.args.quiet:
211+
print(f"[MATCH] {path} (contains '{self.args.content}')")
212+
elif not self.args.content and not self.args.fc:
213+
self.results.append(path)
214+
if not self.args.quiet and self.args.noisy:
215+
print(f"[FILE] {path}")
216+
except Exception as e:
217+
if self.args.errors:
218+
print(f"[ERROR] Cannot open/read {path}: {e}")
219+
220+
with self.counter_lock:
221+
self.files_scanned[0] += 1
222+
223+
self.status_dict[self.tid] = "> Idle"
224+
self.task_queue.task_done()
225+
if self.args.delay:
226+
time.sleep(self.args.delay)
227+
228+
def parse_args():
229+
parser = argparse.ArgumentParser(description="Epic Multithreaded Python Search Tool (arguments are stackable)")
230+
231+
parser.add_argument("path", nargs="?", default="C:\\", help="Path or disk(s) to search")
232+
233+
parser.add_argument("-adisks", action="store_true", help="Search all available fixed disks")
234+
parser.add_argument("-spdisk", type=str, help="Specify one disk (like C:\\)")
235+
parser.add_argument("-wdisk", action="store_true", help="Search root of disks instead of folders")
236+
237+
parser.add_argument("-nosub", action="store_true", help="No recursion into subdirectories")
238+
parser.add_argument("-fc", type=str, help="Focus on specific filename only")
239+
parser.add_argument("-content", type=str, help="Search inside files for text")
240+
parser.add_argument("-hex", type=str, help="Search for hex-encoded bytes inside files")
241+
parser.add_argument("-base64", type=str, help="Search for base64-encoded string inside files")
242+
parser.add_argument("-ext", type=str, help="Only scan files with this extension")
243+
244+
parser.add_argument("-minsize", type=int, help="Only scan files bigger than this size (bytes)")
245+
parser.add_argument("-maxsize", type=int, help="Only scan files smaller than this size (bytes)")
246+
247+
parser.add_argument("-threads", type=int, help="Number of threads to use")
248+
parser.add_argument("-noisy", action="store_true", help="Print every file scanned")
249+
parser.add_argument("-quiet", action="store_true", help="Suppress status except matches")
250+
parser.add_argument("-nocache", action="store_true", help="Disable caching chunks to temp files (ignored)")
251+
parser.add_argument("-keeptemp", action="store_true", help="Do NOT delete temp cache files (ignored)")
252+
parser.add_argument("-stats", action="store_true", help="Show final stats")
253+
parser.add_argument("-fake", action="store_true", help="Simulate scanning, no real file open")
254+
parser.add_argument("-delay", type=float, help="Sleep seconds between scans")
255+
parser.add_argument("-errors", action="store_true", help="Show detailed errors")
256+
parser.add_argument("-skip_sys", action="store_true", help="Skip common system folders (C:\\Users only)")
257+
258+
parser.add_argument("-skibidibounce", action="store_true", help="Random delays + glitchy output")
259+
parser.add_argument("-speedrun", action="store_true", help="Max threads + no safety checks")
260+
parser.add_argument("-amongus", action="store_true", help="Say 'sus file' on file size 69420")
261+
parser.add_argument("-nuke", action="store_true", help="Scary name, does nothing lol")
262+
parser.add_argument("-😎", action="store_true", help="Cool output mode (animated?)")
263+
264+
return parser.parse_args()
265+
266+
def build_status_table(status_dict, files_scanned, elapsed, args):
267+
table = Table(title="Thread Statuses (live)")
268+
269+
table.add_column("Thread ID", justify="center", style="white", no_wrap=True)
270+
table.add_column("Status", style="black")
271+
272+
for tid, status in sorted(status_dict.items()):
273+
table.add_row(str(tid), status)
274+
275+
progress_str = f"Files scanned: {files_scanned[0]}"
276+
if elapsed > 0 and files_scanned[0] > 0:
277+
rate = files_scanned[0] / elapsed
278+
progress_str += f" | Rate: {rate:.1f} files/s"
279+
else:
280+
rate = 0
281+
282+
if args.speedrun:
283+
progress_str += "Speedrun mode ON"
284+
285+
table.caption = progress_str
286+
return table
287+
288+
def main():
289+
args = parse_args()
290+
291+
if args.speedrun:
292+
args.threads = 32
293+
args.nocache = True
294+
args.quiet = True
295+
args.errors = False
296+
297+
start_time = time.time()
298+
task_queue = queue.Queue()
299+
results = []
300+
status_dict = {}
301+
counter_lock = threading.Lock()
302+
files_scanned = [0]
303+
304+
base_path = args.spdisk or args.path
305+
if args.adisks:
306+
for letter in 'CDEFGHIJKLMNOPQRSTUVWXYZ':
307+
drive = f"{letter}:\\"
308+
if os.path.exists(drive):
309+
task_queue.put(drive)
310+
else:
311+
task_queue.put(base_path)
312+
313+
thread_count = args.threads or os.cpu_count()
314+
threads = []
315+
316+
for i in range(thread_count):
317+
status_dict[i] = "init"
318+
t = SearchThread(i, task_queue, results, args, status_dict, counter_lock, files_scanned)
319+
threads.append(t)
320+
t.start()
321+
322+
console = Console()
323+
try:
324+
with Live(build_status_table(status_dict, files_scanned, 0, args), console=console, refresh_per_second=5) as live:
325+
while any(t.is_alive() for t in threads):
326+
elapsed = time.time() - start_time
327+
live.update(build_status_table(status_dict, files_scanned, elapsed, args))
328+
time.sleep(0.2)
329+
except KeyboardInterrupt:
330+
print("\n[!] CTRL+c detected. Exiting gracefully...")
331+
sys.exit(1)
332+
333+
if args.stats:
334+
elapsed = time.time() - start_time
335+
print(f"\n\n[STATS] Done in {elapsed:.2f}s. Files matched: {len(results)} | Scanned: {files_scanned[0]}")
336+
337+
if __name__ == "__main__":
338+
main()

0 commit comments

Comments
 (0)