@@ -556,6 +556,84 @@ function Score-And-Add {
556556 }
557557}
558558
559+ # ============================================================================
560+ # Get-PrunedFiles — fast recursive file walker that prunes dependency-cache
561+ # directories BEFORE descending. PowerShell's Get-ChildItem -Recurse can't
562+ # do this (any -Where applied to the pipeline runs after enumeration, so
563+ # node_modules / site-packages / .venv / etc. get fully walked before the
564+ # filter rejects results). This helper uses .NET DirectoryInfo enumeration
565+ # with explicit pruning, which is dramatically faster on ML-heavy / dev
566+ # machines.
567+ #
568+ # Returns System.IO.FileInfo objects — same shape as Get-ChildItem -File,
569+ # so callers can use .FullName, .Name, .Length, .CreationTime, .LastWriteTime,
570+ # .DirectoryName, .Extension, .BaseName interchangeably.
571+ # ============================================================================
572+
573+ $Script :DefaultPrunedDirNames = @ (
574+ ' node_modules' , ' .git' , ' .hg' , ' .svn' , ' site-packages' ,
575+ ' venv' , ' .venv' , ' env' , ' envs' ,
576+ ' __pycache__' , ' .pytest_cache' , ' .mypy_cache' , ' .ruff_cache' , ' .tox' ,
577+ ' anaconda3' , ' miniconda3' , ' conda' ,
578+ ' .cache' , ' .npm' , ' .yarn' ,
579+ ' .next' , ' .nuxt' , ' .cargo' , ' .rustup'
580+ )
581+
582+ function Get-PrunedFiles {
583+ [CmdletBinding ()]
584+ param (
585+ [Parameter (Mandatory )][string ]$Root ,
586+ [string []]$Extensions = $null , # e.g. @('.onnx') or @('.exe','.py')
587+ [int ]$MaxDepth = 8 ,
588+ [int ]$ResultCap = 0 , # 0 = unlimited
589+ [string []]$ExcludeDirNames = $null # $null = default dep-cache list
590+ )
591+ if (-not (Test-Path - LiteralPath $Root )) { return }
592+
593+ $excludes = if ($null -ne $ExcludeDirNames ) { $ExcludeDirNames } else { $Script :DefaultPrunedDirNames }
594+ $excludeSet = @ {}
595+ foreach ($n in $excludes ) { $excludeSet [$n.ToLower ()] = $true }
596+
597+ $extSet = $null
598+ if ($Extensions ) {
599+ $extSet = @ {}
600+ foreach ($e in $Extensions ) {
601+ $k = if ($e.StartsWith (' .' )) { $e.ToLower () } else { ' .' + $e.ToLower () }
602+ $extSet [$k ] = $true
603+ }
604+ }
605+
606+ $results = [System.Collections.Generic.List [System.IO.FileInfo ]]::new()
607+ $stack = [System.Collections.Generic.Stack [object ]]::new()
608+ try {
609+ $stack.Push (@ { Dir = [System.IO.DirectoryInfo ]::new($Root ); Depth = 0 })
610+ } catch { return $results }
611+
612+ while ($stack.Count -gt 0 ) {
613+ $cur = $stack.Pop ()
614+ $dir = $cur.Dir
615+ $depth = $cur.Depth
616+
617+ try {
618+ foreach ($f in $dir.EnumerateFiles ()) {
619+ if ($extSet -and -not $extSet.ContainsKey ($f.Extension.ToLower ())) { continue }
620+ [void ]$results.Add ($f )
621+ if ($ResultCap -gt 0 -and $results.Count -ge $ResultCap ) { return $results }
622+ }
623+ } catch {}
624+
625+ if ($depth -ge $MaxDepth ) { continue }
626+
627+ try {
628+ foreach ($d in $dir.EnumerateDirectories ()) {
629+ if ($excludeSet.ContainsKey ($d.Name.ToLower ())) { continue }
630+ $stack.Push (@ { Dir = $d ; Depth = $depth + 1 })
631+ }
632+ } catch {}
633+ }
634+ return $results
635+ }
636+
559637# ============================================================================
560638# SCAN FUNCTIONS
561639# ============================================================================
@@ -1109,17 +1187,15 @@ function Scan-UserScriptContents {
11091187 $scanRoots = $scanRoots | Where-Object { Test-Path $_ }
11101188
11111189 $scriptFiles = [System.Collections.Generic.List [object ]]::new()
1190+ # v4.1.4: Get-PrunedFiles handles dep-cache pruning + multi-extension walk
1191+ # in one pass per root (faster than the prior per-extension loop).
1192+ $scriptExts = @ (' .bat' , ' .cmd' , ' .ps1' , ' .vbs' , ' .wsf' , ' .psm1' , ' .lua' , ' .ahk' )
11121193 foreach ($root in $scanRoots ) {
1113- foreach ($ext in @ (' *.bat' , ' *.cmd' , ' *.ps1' , ' *.vbs' , ' *.wsf' , ' *.psm1' , ' *.lua' , ' *.ahk' )) {
1114- try {
1115- Get-ChildItem $root - Recurse - File - Depth 8 - Filter $ext - ErrorAction SilentlyContinue |
1116- Where-Object {
1117- $_.Length -lt 10 MB -and
1118- ($_.Name.ToLower () -notin $excludeNames ) -and
1119- (-not ($selfDir -and $_.DirectoryName.ToLower ().StartsWith($selfDir )))
1120- } |
1121- ForEach-Object { [void ]$scriptFiles.Add ($_ ) }
1122- } catch {}
1194+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions $scriptExts - ResultCap 3000 )) {
1195+ if ($f.Length -ge 10 MB ) { continue }
1196+ if ($f.Name.ToLower () -in $excludeNames ) { continue }
1197+ if ($selfDir -and $f.DirectoryName.ToLower ().StartsWith($selfDir )) { continue }
1198+ [void ]$scriptFiles.Add ($f )
11231199 }
11241200 }
11251201
@@ -1207,25 +1283,24 @@ function Scan-ObscuredFileNames {
12071283
12081284 $extWatchlist = @ (' .exe' , ' .dll' , ' .bat' , ' .cmd' , ' .ps1' , ' .vbs' , ' .lua' , ' .ahk' , ' .sys' , ' .bin' )
12091285
1286+ # v4.1.4: dep-cache prune. node_modules etc. would never be the place where
1287+ # someone hides a cheat under a hex-name; skipping these is detection-neutral.
12101288 foreach ($root in $roots ) {
1211- try {
1212- Get-ChildItem $root - Recurse - File - Depth 8 - ErrorAction SilentlyContinue |
1213- Where-Object { $extWatchlist -contains $_.Extension.ToLower () -and $_.Length -lt 100 MB } |
1214- ForEach-Object {
1215- $name = $_.BaseName
1216- $reason = ' '
1217- if ($name -match ' ^0x[0-9a-fA-F]+$' ) { $reason = " 0x-prefix hex name ($name $ ( $_.Extension ) )" }
1218- elseif ($name -match ' ^[0-9a-fA-F]{8,}$' -and $name -match ' [a-fA-F]' ) { $reason = " raw hex name ($name $ ( $_.Extension ) )" }
1219- elseif ($name -match ' ^\d{4,}$' ) { $reason = " pure-numeric name ($name $ ( $_.Extension ) )" }
1220- elseif ($name -match ' ^[a-zA-Z0-9]{1,2}$' -and $name -notin @ (' go' , ' vc' , ' 7z' , ' C' , ' x' )) { $reason = " ultra-short obscured name ($name $ ( $_.Extension ) )" }
1221- if ($reason ) {
1222- Add-Finding ' ObscuredNames' $_.FullName " Obscured filename: $reason " ' MEDIUM' ' dual-use' @ {
1223- FileName = $_.Name ; FullPath = $_.FullName ; Pattern = $reason
1224- SizeBytes = $_.Length ; LastWrite = $_.LastWriteTime.ToString (' s' )
1225- }
1226- }
1289+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions $extWatchlist - ResultCap 4000 )) {
1290+ if ($f.Length -ge 100 MB ) { continue }
1291+ $name = $f.BaseName
1292+ $reason = ' '
1293+ if ($name -match ' ^0x[0-9a-fA-F]+$' ) { $reason = " 0x-prefix hex name ($name $ ( $f.Extension ) )" }
1294+ elseif ($name -match ' ^[0-9a-fA-F]{8,}$' -and $name -match ' [a-fA-F]' ) { $reason = " raw hex name ($name $ ( $f.Extension ) )" }
1295+ elseif ($name -match ' ^\d{4,}$' ) { $reason = " pure-numeric name ($name $ ( $f.Extension ) )" }
1296+ elseif ($name -match ' ^[a-zA-Z0-9]{1,2}$' -and $name -notin @ (' go' , ' vc' , ' 7z' , ' C' , ' x' )) { $reason = " ultra-short obscured name ($name $ ( $f.Extension ) )" }
1297+ if ($reason ) {
1298+ Add-Finding ' ObscuredNames' $f.FullName " Obscured filename: $reason " ' MEDIUM' ' dual-use' @ {
1299+ FileName = $f.Name ; FullPath = $f.FullName ; Pattern = $reason
1300+ SizeBytes = $f.Length ; LastWrite = $f.LastWriteTime.ToString (' s' )
12271301 }
1228- } catch {}
1302+ }
1303+ }
12291304 }
12301305}
12311306
@@ -1301,14 +1376,13 @@ function Scan-KnownHashes {
13011376 }
13021377 $roots = $roots | Where-Object { Test-Path $_ }
13031378
1379+ # v4.1.4: dep-cache prune. Skips node_modules / site-packages / .venv etc.
1380+ # A real cheat-sample wouldn't be stashed inside an npm dependency tree.
13041381 $candidates = [System.Collections.Generic.List [object ]]::new()
13051382 foreach ($root in $roots ) {
1306- foreach ($ext in @ (' *.exe' , ' *.dll' )) {
1307- try {
1308- Get-ChildItem $root - Recurse - File - Depth 8 - Filter $ext - ErrorAction SilentlyContinue |
1309- Where-Object { $_.Length -lt 100 MB -and $_.Length -gt 0 } |
1310- ForEach-Object { [void ]$candidates.Add ($_ ) }
1311- } catch {}
1383+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions @ (' .exe' , ' .dll' ) - ResultCap 2000 )) {
1384+ if ($f.Length -le 0 -or $f.Length -ge 100 MB ) { continue }
1385+ [void ]$candidates.Add ($f )
13121386 }
13131387 }
13141388
@@ -1346,9 +1420,11 @@ function Scan-LuaScripts {
13461420 " $env: USERPROFILE \Projects" , " $env: USERPROFILE \Games"
13471421 ) | Where-Object { $_ -and (Test-Path $_ ) }
13481422
1423+ # v4.1.4: prune dep-cache dirs (node_modules / site-packages / .venv / etc.)
1424+ # before recursing — those routinely contain bundled .lua files (Neovim
1425+ # plugins, build-tool .lua scripts) that aren't real cheat scripts.
13491426 foreach ($root in $roots ) {
1350- Get-ChildItem $root - Recurse - File - Depth 8 - Filter ' *.lua' - ErrorAction SilentlyContinue | ForEach-Object {
1351- $file = $_
1427+ foreach ($file in (Get-PrunedFiles - Root $root - Extensions @ (' .lua' ) - ResultCap 1500 )) {
13521428 $zone = Get-DownloadSourceUrl $file.FullName
13531429 $meta = @ {
13541430 FileName = $file.Name
@@ -1368,20 +1444,20 @@ function Scan-LuaScripts {
13681444 if ($hit ) {
13691445 $meta [' Pattern' ] = $hit
13701446 Add-Finding ' LuaScript' $file.FullName " [$hit ] $ ( $file.Name ) " ' HIGH' ' cheat' $meta
1371- return
1447+ continue
13721448 }
13731449
13741450 $hitC = Match- Keyword $lc $Keywords_High_Cheats
13751451 if ($hitC ) {
13761452 $meta [' Pattern' ] = $hitC
13771453 Add-Finding ' LuaScript' $file.FullName " [$hitC ] $ ( $file.Name ) " ' HIGH' ' cheat' $meta
1378- return
1454+ continue
13791455 }
13801456 $hitI = Match- Keyword $lc $Keywords_High_Input
13811457 if ($hitI ) {
13821458 $meta [' Pattern' ] = $hitI
13831459 Add-Finding ' LuaScript' $file.FullName " [$hitI ] $ ( $file.Name ) " ' HIGH' ' input' $meta
1384- return
1460+ continue
13851461 }
13861462
13871463 # No cheat or input-device keyword. Emit at INFO so the file is listed
@@ -1722,10 +1798,10 @@ function Scan-AIVisionArtifacts {
17221798 # — walking them was the single biggest contributor to AIVision wall time.
17231799 Write-Host ' [*] AI-vision aimbot artifacts (ONNX / YOLO / external HID)...' - ForegroundColor DarkGray
17241800
1725- # Skip these directory NAMES (matched as path segments, case-insensitive).
1726- # A real AI-aimbot constellation lives in a hand-organized user folder,
1727- # not in a dependency cache. Skipping these is detection-neutral.
1728- $excludePattern = ' (?i)\\(node_modules|\.git|\.hg|\.svn|site-packages|\.venv|venv|env|envs|__pycache__|\.pytest_cache|\.mypy_cache|\.ruff_cache|\.tox|anaconda3|miniconda3|conda|\.cache|\.npm|\.yarn|\.next|\.nuxt|\.cargo|\.rustup|dist-info)\\ '
1801+ # v4.1.4 perf: use Get-PrunedFiles which prunes node_modules / site-packages
1802+ # / .venv / conda envs / etc. BEFORE recursing into them. The previous
1803+ # v4.1.1 fix only filtered AFTER Get-ChildItem enumerated, which on
1804+ # ML-heavy machines still hung walking caches with 100k+ files inside.
17291805
17301806 $roots = @ (
17311807 " $env: USERPROFILE \Documents" , " $env: USERPROFILE \Desktop" ,
@@ -1739,81 +1815,61 @@ function Scan-AIVisionArtifacts {
17391815 $arduinoHits = [System.Collections.Generic.List [object ]]::new()
17401816 $pyDepHits = [System.Collections.Generic.List [object ]]::new()
17411817
1742- # Hard caps on the .exe/.py walk in particular — ML-heavy users can have
1743- # tens of thousands of Python scripts across virtualenvs. We don't need
1744- # to scan them all to find a named-brand aimbot binary.
17451818 $exePyCapPerRoot = 800
17461819 $pyDepCapPerRoot = 150
17471820
17481821 foreach ($root in $roots ) {
1749- # ONNX models (YOLO weights). Cap recursion depth implicitly via cap on results.
1750- try {
1751- Get-ChildItem $root - Recurse - File - Depth 8 - Filter ' *.onnx' - ErrorAction SilentlyContinue |
1752- Where-Object { $_.FullName -notmatch $excludePattern } |
1753- Select-Object - First 200 |
1754- ForEach-Object { [void ]$onnxFiles.Add ($_ ) }
1755- } catch {}
1822+ # ONNX models (YOLO weights).
1823+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions @ (' .onnx' ) - ResultCap 200 )) {
1824+ [void ]$onnxFiles.Add ($f )
1825+ }
17561826
17571827 # Named-brand executables (HIGH on their own).
1758- try {
1759- Get-ChildItem $root - Recurse - File - Depth 8 - Include ' *.exe' , ' *.py' - ErrorAction SilentlyContinue |
1760- Where-Object { $_.FullName -notmatch $excludePattern -and $_.Length -lt 200 MB } |
1761- Select-Object - First $exePyCapPerRoot |
1762- ForEach-Object {
1763- $hit = Match- Keyword " $ ( $_.Name ) $ ( $_.DirectoryName ) " $VisionAimbot_AI_PC
1764- if ($hit ) {
1765- $meta = @ {
1766- Pattern = $hit
1767- FileName = $_.Name
1768- FullPath = $_.FullName
1769- SizeBytes = $_.Length
1770- Created = $_.CreationTime.ToString (' s' )
1771- LastWrite = $_.LastWriteTime.ToString (' s' )
1772- }
1773- Add-Finding ' AIVision' $_.FullName " [$hit ] AI-vision aimbot executable: $ ( $_.Name ) " ' HIGH' ' cheat' $meta
1774- [void ]$brandHits.Add ($_ )
1775- }
1828+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions @ (' .exe' , ' .py' ) - ResultCap $exePyCapPerRoot )) {
1829+ if ($f.Length -ge 200 MB ) { continue }
1830+ $hit = Match- Keyword " $ ( $f.Name ) $ ( $f.DirectoryName ) " $VisionAimbot_AI_PC
1831+ if ($hit ) {
1832+ $meta = @ {
1833+ Pattern = $hit
1834+ FileName = $f.Name
1835+ FullPath = $f.FullName
1836+ SizeBytes = $f.Length
1837+ Created = $f.CreationTime.ToString (' s' )
1838+ LastWrite = $f.LastWriteTime.ToString (' s' )
17761839 }
1777- } catch {}
1840+ Add-Finding ' AIVision' $f.FullName " [$hit ] AI-vision aimbot executable: $ ( $f.Name ) " ' HIGH' ' cheat' $meta
1841+ [void ]$brandHits.Add ($f )
1842+ }
1843+ }
17781844
1779- # Arduino sketches with HID-descriptor patterns - dead giveaway when
1780- # paired with the ONNX/Python side of the constellation.
1781- try {
1782- Get-ChildItem $root - Recurse - File - Depth 8 - Filter ' *.ino' - ErrorAction SilentlyContinue |
1783- Where-Object { $_.FullName -notmatch $excludePattern } |
1784- Select-Object - First 100 |
1785- ForEach-Object {
1786- try {
1787- $content = Get-Content $_.FullName - Raw - ErrorAction Stop
1788- if ($content -match ' (?i)(Mouse\.move|HID-Project|MouseAbsolute|Keyboard\.press.*Mouse)' ) {
1789- [void ]$arduinoHits.Add ($_ )
1790- }
1791- } catch {}
1845+ # Arduino sketches with HID-descriptor patterns.
1846+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions @ (' .ino' ) - ResultCap 100 )) {
1847+ try {
1848+ $content = Get-Content $f.FullName - Raw - ErrorAction Stop
1849+ if ($content -match ' (?i)(Mouse\.move|HID-Project|MouseAbsolute|Keyboard\.press.*Mouse)' ) {
1850+ [void ]$arduinoHits.Add ($f )
17921851 }
1793- } catch {}
1852+ } catch {}
1853+ }
17941854
1795- # Python ML dependency markers - requirements.txt / pyproject.toml /
1796- # site-packages dirs naming aimbot-typical libraries.
1797- try {
1798- Get-ChildItem $root - Recurse - File - Depth 8 - Include ' requirements.txt' , ' pyproject.toml' , ' *.cfg' - ErrorAction SilentlyContinue |
1799- Where-Object { $_.FullName -notmatch $excludePattern } |
1800- Select-Object - First $pyDepCapPerRoot |
1801- ForEach-Object {
1802- try {
1803- $content = Get-Content $_.FullName - Raw - ErrorAction Stop
1804- $combo = 0
1805- if ($content -match ' (?i)ultralytics' ) { $combo ++ }
1806- if ($content -match ' (?i)\btorch\b' ) { $combo ++ }
1807- if ($content -match ' (?i)\bmss\b' ) { $combo ++ }
1808- if ($content -match ' (?i)pyautogui|pydirectinput|pynput' ) { $combo ++ }
1809- if ($content -match ' (?i)opencv-python|cv2' ) { $combo ++ }
1810- if ($content -match ' (?i)onnxruntime' ) { $combo ++ }
1811- if ($combo -ge 3 ) {
1812- [void ]$pyDepHits.Add (@ { File = $_ ; Score = $combo })
1813- }
1814- } catch {}
1855+ # Python ML dependency markers - requirements.txt / pyproject.toml / .cfg
1856+ foreach ($f in (Get-PrunedFiles - Root $root - Extensions @ (' .txt' , ' .toml' , ' .cfg' ) - ResultCap ($pyDepCapPerRoot * 3 ))) {
1857+ $n = $f.Name.ToLower ()
1858+ if ($n -ne ' requirements.txt' -and $n -ne ' pyproject.toml' -and -not $n.EndsWith (' .cfg' )) { continue }
1859+ try {
1860+ $content = Get-Content $f.FullName - Raw - ErrorAction Stop
1861+ $combo = 0
1862+ if ($content -match ' (?i)ultralytics' ) { $combo ++ }
1863+ if ($content -match ' (?i)\btorch\b' ) { $combo ++ }
1864+ if ($content -match ' (?i)\bmss\b' ) { $combo ++ }
1865+ if ($content -match ' (?i)pyautogui|pydirectinput|pynput' ) { $combo ++ }
1866+ if ($content -match ' (?i)opencv-python|cv2' ) { $combo ++ }
1867+ if ($content -match ' (?i)onnxruntime' ) { $combo ++ }
1868+ if ($combo -ge 3 ) {
1869+ [void ]$pyDepHits.Add (@ { File = $f ; Score = $combo })
18151870 }
1816- } catch {}
1871+ } catch {}
1872+ }
18171873 }
18181874
18191875 # Emit ONNX findings according to constellation rules.
0 commit comments