569 lines
26 KiB
C#
569 lines
26 KiB
C#
|
|
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<string> → comma-separated string ───
|
||
|
|
public class ListToStringConverter : IValueConverter
|
||
|
|
{
|
||
|
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
|
||
|
|
value is List<string> 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<ScanResult> _results = new();
|
||
|
|
private readonly ObservableCollection<string> _scanLog = new();
|
||
|
|
private List<Signature> _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<ScanProgress>(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<ScanProgress> 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) { }
|
||
|
|
}
|