using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using Microsoft.Win32; using MemScanEDR.Models; using MemScanEDR.Services; namespace MemScanEDR; // ─── Value Converter: List → comma-separated string ─── public class ListToStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => value is List list && list.Count > 0 ? string.Join(", ", list) : "—"; public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); } // ─── Value Converter: RiskLevel → Brush color ─── public class RiskLevelToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is RiskLevel level) { return level switch { RiskLevel.High => new System.Windows.Media.SolidColorBrush( System.Windows.Media.Color.FromRgb(0xef, 0x44, 0x44)), // Red RiskLevel.Suspicious => new System.Windows.Media.SolidColorBrush( System.Windows.Media.Color.FromRgb(0xf5, 0x9e, 0x0b)), // Yellow/Amber _ => new System.Windows.Media.SolidColorBrush( System.Windows.Media.Color.FromRgb(0x22, 0xc5, 0x5e)) // Green }; } return new System.Windows.Media.SolidColorBrush( System.Windows.Media.Color.FromRgb(0x22, 0xc5, 0x5e)); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); } // ─── Value Converter: RiskLevel → display text ─── public class RiskLevelToTextConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is RiskLevel level) { return level switch { RiskLevel.High => Lang.T("高危", "HIGH"), RiskLevel.Suspicious => Lang.T("可疑", "SUSP"), _ => Lang.T("安全", "SAFE") }; } return "—"; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); } // ─── Simple language helper ────────────────────────────────── public static class Lang { private static bool _isChinese = true; public static bool IsChinese => _isChinese; public static void SetChinese(bool zh) => _isChinese = zh; public static string T(string zh, string en) => _isChinese ? zh : en; } // ═══════════════════════════════════════════════════════════════ // MAIN WINDOW v2.0 // ═══════════════════════════════════════════════════════════════ public partial class MainWindow : Window { // ── Services ─────────────────────────────────────────────── private readonly ProcessCollector _processCollector = new(); private readonly MemoryScanner _memoryScanner = new(); private AIAnalyzer? _aiAnalyzer; // ── v2.0: 检测管线 ── private DetectionPipeline? _pipeline; // ── Application state ────────────────────────────────────── private AppSettings _settings = null!; private readonly ObservableCollection _results = new(); private readonly ObservableCollection _scanLog = new(); private List _signatures = new(); private CancellationTokenSource? _cts; private bool _isScanning; // ═══════════════════════════════════════════════════════════ // CONSTRUCTOR // ═══════════════════════════════════════════════════════════ public MainWindow() { try { InitializeComponent(); // Bind observable collections DgAlerts.ItemsSource = _results; DgProcesses.ItemsSource = _results; LstScanLog.ItemsSource = _scanLog; // Load settings & initialize _settings = AppSettings.Load(); _signatures = _memoryScanner.LoadSignatures(_settings.RulesDir); var scanMode = _settings.ScanProfile switch { "fast" => ScanMode.Fast, "deep" => ScanMode.Deep, _ => ScanMode.Balanced }; _aiAnalyzer = new AIAnalyzer(_settings.AI, _signatures, scanMode); _pipeline = new DetectionPipeline(scanMode); // Apply saved settings to the UI LoadSettingsToUI(); ApplyLanguage(); AddLog(Lang.T("[init] MemScan-EDR v2.0 AI 内存检测引擎已加载", "[init] MemScan-EDR v2.0 AI memory detection engine loaded")); AddLog(Lang.T($"[init] 扫描模式: {scanMode} | 本地AI: {(_settings.AI.EnableLocalAI ? "启用" : "禁用")}", $"[init] Scan mode: {scanMode} | Local AI: {(_settings.AI.EnableLocalAI ? "enabled" : "disabled")}")); } catch (Exception ex) { System.IO.File.AppendAllText( System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "crash.log"), $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] MainWindow init error\n{ex}\n\n"); System.Windows.MessageBox.Show($"Init error (see crash.log):\n\n{ex.Message}", "MemScan-EDR v2.0", MessageBoxButton.OK, MessageBoxImage.Error); } } // ═══════════════════════════════════════════════════════════ // LANGUAGE TOGGLE // ═══════════════════════════════════════════════════════════ private void BtnLangToggle_Click(object sender, RoutedEventArgs e) { Lang.SetChinese(!Lang.IsChinese); ApplyLanguage(); } private void ApplyLanguage() { BtnLang.Content = Lang.T("EN", "中"); // Sidebar TbMonHeader.Text = Lang.T("监控", "MONITORING"); TbSysHeader.Text = Lang.T("系统", "SYSTEM"); TbNavDashboard.Text = Lang.T("仪表盘", "Dashboard"); TbNavProcess.Text = Lang.T("进程列表", "Processes"); TbNavScan.Text = Lang.T("扫描进度", "Scan Progress"); TbNavSettings.Text = Lang.T("设置", "Settings"); TbQuickScan.Text = Lang.T("AI 扫描", "AI SCAN"); TbQuickScanDesc.Text = Lang.T("AI 管线分析所有进程", "Pipeline analyze all"); BtnStartScan.Content = Lang.T("开始扫描", "Start Scan"); // Dashboard TbDashboardTitle.Text = Lang.T("安全仪表盘", "Security Dashboard"); TbStatTotal.Text = Lang.T("已扫描", "TOTAL"); TbStatTotalDesc.Text = Lang.T("进程", "Processes"); TbStatHigh.Text = Lang.T("高风险", "HIGH"); TbStatHighDesc.Text = Lang.T("严重告警", "Critical"); TbStatSusp.Text = Lang.T("可疑", "SUSP"); TbStatSuspDesc.Text = Lang.T("警告", "Warnings"); TbStatSafe.Text = Lang.T("安全", "SAFE"); TbStatSafeDesc.Text = Lang.T("正常", "Clean"); TbRecentAlerts.Text = Lang.T("最近告警", "Recent Alerts"); // Scan view TbScanTitle.Text = Lang.T("AI 扫描进度", "AI Scan Progress"); BtnStopScan.Content = Lang.T("停止扫描", "Stop Scan"); TbScanLog.Text = Lang.T("扫描日志", "Scan Log"); // Process view TbProcTitle.Text = Lang.T("AI 扫描结果", "AI Scan Results"); // Settings TbSettingsTitle.Text = Lang.T("设置", "Settings"); TbAiConfig.Text = Lang.T("AI 配置", "AI Configuration"); TbAiProvider.Text = Lang.T("AI 提供商", "AI Provider"); TbBaseUrl.Text = Lang.T("API 地址", "API Base URL"); TbApiKey.Text = Lang.T("API 密钥", "API Key"); TbModel.Text = Lang.T("模型名称", "Model Name"); TbScanProfile.Text = Lang.T("扫描配置", "Scan Profile"); BtnSaveSettings.Content = Lang.T("保存设置", "Save Settings"); // ComboBox items CmbAIProvider.Items[0] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("启发式规则 (无需 API)", "Heuristic (no API)"), Tag = "Heuristic" }; CmbAIProvider.Items[1] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("OpenAI 兼容", "OpenAI-Compatible"), Tag = "OpenAICompatible" }; CmbAIProvider.Items[2] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("Claude (Anthropic)", "Claude (Anthropic)"), Tag = "Claude" }; CmbAIProvider.Items[3] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("本地模型 (Ollama)", "Local (Ollama)"), Tag = "Local" }; CmbScanProfile.Items[0] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("快速 - 元数据筛查", "Fast - metadata only"), Tag = "fast" }; CmbScanProfile.Items[1] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("平衡 - 采样分析 (推荐)", "Balanced (recommended)"), Tag = "balanced" }; CmbScanProfile.Items[2] = new System.Windows.Controls.ComboBoxItem { Content = Lang.T("深度 - 全量内存分析", "Deep - full memory"), Tag = "deep" }; LoadSettingsToUI(); } // ═══════════════════════════════════════════════════════════ // SIDEBAR NAVIGATION // ═══════════════════════════════════════════════════════════ private void NavChanged(object sender, RoutedEventArgs e) { if (DashboardView == null || ProcessView == null || ScanView == null || SettingsView == null) return; DashboardView.Visibility = Visibility.Collapsed; ProcessView.Visibility = Visibility.Collapsed; ScanView.Visibility = Visibility.Collapsed; SettingsView.Visibility = Visibility.Collapsed; if (sender == NavDashboard) DashboardView.Visibility = Visibility.Visible; else if (sender == NavProcess) ProcessView.Visibility = Visibility.Visible; else if (sender == NavScan) ScanView.Visibility = Visibility.Visible; else if (sender == NavSettings) SettingsView.Visibility = Visibility.Visible; } // ═══════════════════════════════════════════════════════════ // WINDOW CHROME BUTTONS // ═══════════════════════════════════════════════════════════ private void TitleBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (e.ClickCount == 2) BtnMaximize(sender, e); else DragMove(); } private void BtnMinimize(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized; private void BtnMaximize(object sender, RoutedEventArgs e) => WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; private void BtnClose(object sender, RoutedEventArgs e) { _cts?.Cancel(); Close(); } // ═══════════════════════════════════════════════════════════ // SCAN PROGRESS REPORTING MODEL // ═══════════════════════════════════════════════════════════ private class ScanProgress { public string ProcessName { get; init; } = ""; public string Message { get; init; } = ""; public double Progress { get; init; } public ScanResult? Result { get; init; } public bool IsComplete { get; init; } } // ═══════════════════════════════════════════════════════════ // START SCAN // ═══════════════════════════════════════════════════════════ private async void BtnStartScan_Click(object sender, RoutedEventArgs e) { if (_isScanning) return; _isScanning = true; _cts = new CancellationTokenSource(); var ct = _cts.Token; BtnStartScan.IsEnabled = false; BtnStopScan.IsEnabled = true; _scanLog.Clear(); _results.Clear(); UpdateStats(); ScanProgressBar.Value = 0; LblCurrentProcess.Text = Lang.T("初始化 AI 管线...", "Initializing AI pipeline..."); NavScan.IsChecked = true; SetScanStatus(Lang.T("AI 扫描中", "AI SCANNING"), true); AddLog(Lang.T($"[scan] v2.0 AI 扫描开始 — {DateTime.Now:yyyy-MM-dd HH:mm:ss}", $"[scan] v2.0 AI scan started — {DateTime.Now:yyyy-MM-dd HH:mm:ss}")); var progress = new Progress(OnScanProgress); try { await Task.Run(() => RunScanAsync(progress, ct), ct); } catch (OperationCanceledException) { AddLog(Lang.T("[scan] 扫描已取消", "[scan] Scan cancelled by user")); } catch (Exception ex) { AddLog($"[error] {ex.Message}"); } finally { _isScanning = false; BtnStartScan.IsEnabled = true; BtnStopScan.IsEnabled = false; SetScanStatus(Lang.T("空闲", "IDLE"), false); AddLog(Lang.T($"[scan] 扫描完成 — {DateTime.Now:yyyy-MM-dd HH:mm:ss}", $"[scan] Scan completed — {DateTime.Now:yyyy-MM-dd HH:mm:ss}")); } } private void BtnStopScan_Click(object sender, RoutedEventArgs e) { _cts?.Cancel(); AddLog(Lang.T("[scan] 正在停止...", "[scan] Stop requested...")); } // ═══════════════════════════════════════════════════════════ // CORE SCAN LOGIC v2.0 (background) // ═══════════════════════════════════════════════════════════ private async Task RunScanAsync(IProgress progress, CancellationToken ct) { // Phase 1: 枚举进程 progress.Report(new ScanProgress { Message = Lang.T("[proc] 正在枚举进程...", "[proc] Enumerating processes...") }); var processes = _processCollector.CollectAll(); progress.Report(new ScanProgress { Message = Lang.T($"[proc] 发现 {processes.Count} 个进程", $"[proc] Found {processes.Count} processes") }); var total = processes.Count; var scanned = 0; var highCount = 0; var suspCount = 0; var safeCount = 0; // Phase 2: 逐进程 AI 分析 foreach (var proc in processes) { ct.ThrowIfCancellationRequested(); scanned++; var pct = (double)scanned / total * 100.0; progress.Report(new ScanProgress { ProcessName = $"{proc.Name} (PID {proc.Pid})", Message = Lang.T($"[{scanned}/{total}] AI 分析 {proc.Name} (PID {proc.Pid})...", $"[{scanned}/{total}] AI analyzing {proc.Name} (PID {proc.Pid})..."), Progress = pct }); try { // v2.0: 使用 AI 管线分析 var result = await _aiAnalyzer!.AnalyzeProcessAsync(proc); if (result.RiskLevel == RiskLevel.High) highCount++; else if (result.RiskLevel == RiskLevel.Suspicious) suspCount++; else safeCount++; var riskTag = result.RiskLevel switch { RiskLevel.High => Lang.T("高危", "HIGH"), RiskLevel.Suspicious => Lang.T("可疑", "SUSP"), _ => Lang.T("安全", "SAFE") }; progress.Report(new ScanProgress { Message = $"[{scanned}/{total}] [{riskTag}] {proc.Name} score={result.RiskScore:F2} layer={result.DetectionLayer}", Progress = pct, Result = result }); // 统计更新 progress.Report(new ScanProgress { Message = $"STATS | safe={safeCount} susp={suspCount} high={highCount}", Progress = pct }); } catch (Exception ex) { progress.Report(new ScanProgress { Message = $"[warn] {proc.Name} — {ex.Message}", Progress = pct }); } // 控制扫描速度(避免 CPU 飙升) await Task.Delay(10, ct); } // 结束总结 var summary = Lang.T( $"[done] 扫描完成 — {total}进程 | 安全={safeCount} 可疑={suspCount} 高风险={highCount}", $"[done] Scan complete — {total} processes | Safe={safeCount} Suspicious={suspCount} High={highCount}"); progress.Report(new ScanProgress { Message = summary, Progress = 100, IsComplete = true }); } // ═══════════════════════════════════════════════════════════ // PROGRESS CALLBACK (UI thread) // ═══════════════════════════════════════════════════════════ private void OnScanProgress(ScanProgress sp) { if (sp.Progress > 0) ScanProgressBar.Value = sp.Progress; if (!string.IsNullOrEmpty(sp.ProcessName)) LblCurrentProcess.Text = sp.ProcessName; if (!string.IsNullOrEmpty(sp.Message)) AddLog(sp.Message); if (sp.Result != null) { _results.Add(sp.Result); UpdateStats(); } if (LstScanLog.Items.Count > 0) LstScanLog.ScrollIntoView(LstScanLog.Items[^1]); if (sp.IsComplete) { LblCurrentProcess.Text = Lang.T("AI 扫描完成", "AI scan complete"); LblProcCount.Text = Lang.T($" — {_results.Count} 条结果", $" — {_results.Count} results"); } } // ═══════════════════════════════════════════════════════════ // UI HELPERS // ═══════════════════════════════════════════════════════════ private void AddLog(string message) { var ts = DateTime.Now.ToString("HH:mm:ss"); _scanLog.Add($"{ts} {message}"); } private void UpdateStats() { var total = _results.Count; var high = _results.Count(r => r.RiskLevel == RiskLevel.High); var susp = _results.Count(r => r.RiskLevel == RiskLevel.Suspicious); var safe = total - high - susp; StatTotal.Text = total.ToString(); StatHigh.Text = high.ToString(); StatSuspicious.Text = susp.ToString(); StatSafe.Text = safe.ToString(); if (high > 0) { LblDashboardStatus.Text = Lang.T($"● {high} 条告警", $"● {high} ALERT{(high > 1 ? "S" : "")}"); LblDashboardStatus.Foreground = TryFindResource("AccentRedBrush") as System.Windows.Media.Brush; } else if (susp > 0) { LblDashboardStatus.Text = Lang.T($"● {susp} 条警告", $"● {susp} WARNING{(susp > 1 ? "S" : "")}"); LblDashboardStatus.Foreground = TryFindResource("AccentYellowBrush") as System.Windows.Media.Brush; } else { LblDashboardStatus.Text = Lang.T("● AI 监控中", "● AI MONITORING"); LblDashboardStatus.Foreground = TryFindResource("AccentBlueBrush") as System.Windows.Media.Brush; } } private void SetScanStatus(string text, bool isScanning) { LblScanStatus.Text = $"● {text}"; if (isScanning) { LblScanStatus.Foreground = TryFindResource("AccentPurpleBrush") as System.Windows.Media.Brush; ScanStatusBadge.Background = TryFindResource("HighlightBlueBrush") as System.Windows.Media.Brush; } else { LblScanStatus.Foreground = TryFindResource("AccentGreenBrush") as System.Windows.Media.Brush; ScanStatusBadge.Background = new System.Windows.Media.SolidColorBrush( System.Windows.Media.Color.FromRgb(0xf0, 0xfd, 0xf4)); } } // ═══════════════════════════════════════════════════════════ // SETTINGS VIEW // ═══════════════════════════════════════════════════════════ private void LoadSettingsToUI() { CmbAIProvider.SelectedIndex = _settings.AI.Provider switch { AIProvider.OpenAICompatible => 1, AIProvider.Claude => 2, AIProvider.Local => 3, _ => 0 }; TxtApiKey.Password = _settings.AI.ApiKey; TxtBaseUrl.Text = _settings.AI.BaseUrl; TxtModel.Text = _settings.AI.Model; // v2.0: AI 管线开关 ChkEnableLocalAI.IsChecked = _settings.AI.EnableLocalAI; ChkEnableLLM.IsChecked = _settings.AI.EnableLLMAssist; CmbScanProfile.SelectedIndex = _settings.ScanProfile switch { "fast" => 0, "deep" => 2, _ => 1 }; } private void BtnSaveSettings_Click(object sender, RoutedEventArgs e) { var providerTag = (CmbAIProvider.SelectedItem as System.Windows.Controls.ComboBoxItem)?.Tag?.ToString() ?? "Heuristic"; var provider = providerTag switch { "OpenAICompatible" => AIProvider.OpenAICompatible, "Claude" => AIProvider.Claude, "Local" => AIProvider.Local, _ => AIProvider.Heuristic }; _settings.AI = AIConfig.DefaultFor(provider); _settings.AI.ApiKey = TxtApiKey.Password; _settings.AI.BaseUrl = TxtBaseUrl.Text.Trim(); _settings.AI.Model = TxtModel.Text.Trim(); _settings.AI.EnableLocalAI = ChkEnableLocalAI.IsChecked ?? true; _settings.AI.EnableLLMAssist = ChkEnableLLM.IsChecked ?? false; var profileTag = (CmbScanProfile.SelectedItem as System.Windows.Controls.ComboBoxItem)?.Tag?.ToString() ?? "balanced"; _settings.ScanProfile = profileTag; _settings.Save(); // 重新初始化 var scanMode = _settings.ScanProfile switch { "fast" => ScanMode.Fast, "deep" => ScanMode.Deep, _ => ScanMode.Balanced }; _signatures = _memoryScanner.LoadSignatures(_settings.RulesDir); _aiAnalyzer = new AIAnalyzer(_settings.AI, _signatures, scanMode); _pipeline = new DetectionPipeline(scanMode); AddLog(Lang.T($"[settings] 设置已保存 (扫描模式: {scanMode}, 本地AI: {(_settings.AI.EnableLocalAI ? "启用" : "禁用")})", $"[settings] Settings saved (mode: {scanMode}, local AI: {(_settings.AI.EnableLocalAI ? "on" : "off")})")); System.Windows.MessageBox.Show( Lang.T("设置已保存。AI 分析引擎已重新初始化。", "Settings saved. AI engine re-initialized."), "MemScan-EDR v2.0", MessageBoxButton.OK, MessageBoxImage.Information); } private void BtnBrowseRules_Click(object sender, RoutedEventArgs e) { } private void BtnBrowseLog_Click(object sender, RoutedEventArgs e) { } }