using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Management; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using CheatScanner.Models; using Microsoft.Win32; namespace CheatScanner.Services; public class SystemScanner { private readonly List _rules; public SystemScanner(List rules) { _rules = rules; } public async Task ScanAsync(IProgress<(string Status, int Percent)> progress) { return await Task.Run(() => { var report = new ScanReport { ScanVersion = "1.0.0", Timestamp = DateTime.UtcNow.ToString("o"), Detections = new List(), ProcessList = new List(), }; progress.Report(("Collecting hardware fingerprint...", 5)); report.Hwid = GetHwid(); progress.Report(("Enumerating running processes...", 12)); var processes = Process.GetProcesses(); report.ProcessList = processes .Select(p => p.ProcessName) .OrderBy(n => n, StringComparer.OrdinalIgnoreCase) .ToList(); var gameNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "hl", "hl2", "cs", "cstrike", "csgo", "cs2", "steam" }; report.GameRunning = processes .FirstOrDefault(p => gameNames.Contains(p.ProcessName)) ?.ProcessName ?? ""; var enabledRules = _rules.Where(r => r.Enabled).ToList(); int total = Math.Max(1, enabledRules.Count); int done = 0; foreach (var rule in enabledRules) { done++; int pct = 20 + (int)((done / (double)total) * 75); progress.Report(($"Checking: {rule.Name}", pct)); Detection? det = null; if (rule.Match.ProcessModule is not null) det ??= CheckProcessModule(rule, processes); if (rule.Match.ProcessName is not null) det ??= CheckProcessName(rule, processes); if (rule.Match.FilePath is not null) det ??= CheckFilePath(rule); if (rule.Match.RegistryKey is not null) det ??= CheckRegistry(rule); if (det is not null) report.Detections.Add(det); } progress.Report(("Scan complete.", 100)); return report; }); } // ── Matchers ──────────────────────────────────────────────────────────── private static Detection? CheckProcessModule(CheatRule rule, Process[] processes) { var needle = rule.Match.ProcessModule!.ToLower(); foreach (var proc in processes) { try { foreach (ProcessModule mod in proc.Modules) { if (mod.ModuleName.ToLower().Contains(needle)) return Make(rule, $"Module '{mod.ModuleName}' loaded in '{proc.ProcessName}' — {mod.FileName}", "process_module"); } } catch { /* inaccessible process — skip */ } } return null; } private static Detection? CheckProcessName(CheatRule rule, Process[] processes) { var needle = rule.Match.ProcessName!.ToLower(); var found = processes.FirstOrDefault(p => p.ProcessName.ToLower().Contains(needle)); if (found is null) return null; return Make(rule, $"Process '{found.ProcessName}' (PID {found.Id}) is running", "process_name"); } private static Detection? CheckFilePath(CheatRule rule) { var path = Environment.ExpandEnvironmentVariables(rule.Match.FilePath!); if (!File.Exists(path) && !Directory.Exists(path)) return null; return Make(rule, $"Found at: {path}", "file_path"); } private static Detection? CheckRegistry(CheatRule rule) { try { var keyPath = rule.Match.RegistryKey!; RegistryKey? key = null; if (keyPath.StartsWith("HKCU\\", StringComparison.OrdinalIgnoreCase)) key = Registry.CurrentUser.OpenSubKey(keyPath[5..]); else if (keyPath.StartsWith("HKLM\\", StringComparison.OrdinalIgnoreCase)) key = Registry.LocalMachine.OpenSubKey(keyPath[5..]); else if (keyPath.StartsWith("HKCR\\", StringComparison.OrdinalIgnoreCase)) key = Registry.ClassesRoot.OpenSubKey(keyPath[5..]); if (key is null) return null; using (key) { if (rule.Match.RegistryValue is not null) { var val = key.GetValue(rule.Match.RegistryValue); if (val is null) return null; return Make(rule, $"Registry: {keyPath}\\{rule.Match.RegistryValue} = {val}", "registry"); } return Make(rule, $"Registry key present: {keyPath}", "registry"); } } catch { return null; } } private static Detection Make(CheatRule rule, string evidence, string matchedBy) => new() { RuleId = rule.Id, Severity = rule.Severity, Name = rule.Name, Description = rule.Description, Evidence = evidence, MatchedBy = matchedBy, }; // ── HWID ──────────────────────────────────────────────────────────────── private static string GetHwid() { try { var sb = new StringBuilder(); using (var q = new ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor")) foreach (var obj in q.Get()) sb.Append(obj["ProcessorId"]); using (var q = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BIOS")) foreach (var obj in q.Get()) sb.Append(obj["SerialNumber"]); sb.Append(Environment.MachineName); using var sha = SHA256.Create(); var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString())); return Convert.ToHexString(hash)[..16]; } catch { return "UNKNOWN"; } } }