using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using MemScanEDR.Models; namespace MemScanEDR.Services; /// /// 特征提取器 —— 核心模块 /// 从进程内存中提取全部多维特征,作为 AI 模型的输入向量 /// 包含:元数据、页权限分布、代码特征、熵值、网络行为、进程树关系 /// public class FeatureExtractor { private readonly EntropyAnalyzer _entropyAnalyzer = new(); private readonly PEHeaderAnalyzer _peAnalyzer = new(); private const uint PROCESS_VM_READ = 0x0010; private const uint PROCESS_QUERY_INFORMATION = 0x0400; /// /// 从进程信息中提取完整的 MemoryFeatures 特征向量 /// public MemoryFeatures Extract(ProcessInfo proc, List regions, List matches, List signatures) { var features = new MemoryFeatures(); // ─── 一、进程元数据 ─── features.ProcessName = proc.Name; features.ExePath = proc.ExePath; features.ParentPid = proc.Ppid; features.ParentName = proc.ParentName; features.CmdLine = proc.CmdLine; features.StartTime = proc.StartTime; features.WorkingSetMB = proc.WorkingSetMB; features.ThreadCount = proc.ThreadCount; features.IsSigned = proc.IsSigned; features.Signer = proc.Signer; features.IsSystemPath = proc.IsSystemPath; features.IsTempPath = proc.IsTempPath; // ─── 二、内存页权限分布 ─── ExtractPagePermissions(features, regions); // ─── 三、内存代码特征 ─── ExtractCodeFeatures(features, proc, regions, matches); // ─── 四、熵值特征 ─── ExtractEntropyFeatures(features, proc.Pid, regions); // ─── 五、网络行为特征 ─── ExtractNetworkFeatures(features, proc.Connections); // ─── 六、进程树关系 ─── features.IsSystemProcessName = IsSystemProcessName(proc.Name); features.HasLegitimateParent = HasLegitimateParent(proc.ParentName, proc.Name); // ─── 七、DLL 加载特征 ─── features.LoadedModuleCount = proc.LoadedModules.Count; features.UnmappedModules = CountUnmappedModules(regions); features.TempPathModules = CountTempModules(proc.LoadedModules); return features; } /// /// 快速特征提取 —— 仅提取元数据和权限分布 (不读内存,速度最快) /// 用于快速预过滤阶段 /// public MemoryFeatures QuickExtract(ProcessInfo proc, List regions) { var features = new MemoryFeatures { ProcessName = proc.Name, ExePath = proc.ExePath, ParentPid = proc.Ppid, ParentName = proc.ParentName, IsSigned = proc.IsSigned, IsSystemPath = proc.IsSystemPath, IsTempPath = proc.IsTempPath, WorkingSetMB = proc.WorkingSetMB, ThreadCount = proc.ThreadCount, IsSystemProcessName = IsSystemProcessName(proc.Name), HasLegitimateParent = HasLegitimateParent(proc.ParentName, proc.Name) }; ExtractPagePermissions(features, regions); ExtractNetworkFeatures(features, proc.Connections); return features; } // ─── 内存页权限提取 ─── private static void ExtractPagePermissions(MemoryFeatures f, List regions) { f.TotalRegions = regions.Count; foreach (var r in regions) { switch (r.Protect) { case "READONLY": f.ReadOnlyPages++; break; case "READWRITE": f.ReadWritePages++; break; case "EXECUTE": case "EXECUTE_READ": f.ExecuteReadPages++; break; case "EXECUTE_READWRITE": f.ExecuteReadWritePages++; break; } switch (r.Type) { case "PRIVATE": f.PrivatePages++; break; case "MAPPED": f.MappedPages++; break; case "IMAGE": f.ImagePages++; break; } // 无文件匿名可执行内存 if (r.Type != "IMAGE" && (r.Protect.Contains("EXECUTE"))) { f.AnonymousExecPages++; } // RWX 私有页 —— 核心恶意指标 if (r.Protect.Contains("EXECUTE_READWRITE") && r.Type == "PRIVATE") { f.RwxPrivatePages++; f.RwxTotalSize += r.Size; } } } // ─── 代码特征提取 ─── private void ExtractCodeFeatures(MemoryFeatures f, ProcessInfo proc, List regions, List matches) { // 脚本引擎/运行时自身是合法进程,跳过内存字符串扫描避免误报 if (IsScriptingEngine(proc)) { f.SignatureMatchCount = matches.Count; f.HighSeverityMatches = matches.Count(m => m.Severity == "high"); return; } // 打开进程读取内存 var hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, proc.Pid); if (hProcess == IntPtr.Zero) return; try { // PE 头检测 foreach (var region in regions.Where(r => r.Type == "IMAGE" && r.State == "COMMIT")) { if (_peAnalyzer.HasInMemoryPE(hProcess, region.BaseAddress)) { f.HasInMemoryPE = true; // 检查内存PE与磁盘是否一致 if (!string.IsNullOrEmpty(proc.ExePath)) { var integrity = _peAnalyzer.CheckPeIntegrity(proc.ExePath, hProcess, region.BaseAddress); f.PeMismatchDisk = !integrity.IsValid; } break; } } // 敏感字符串提取 (只在可疑区域提取以提升速度) var suspiciousRegions = regions.Where(r => r.Type == "PRIVATE" && r.Protect.Contains("EXECUTE") && r.Size < 10 * 1024 * 1024 ).Take(5); foreach (var region in suspiciousRegions) { var strings = _peAnalyzer.ExtractSuspiciousStrings(hProcess, region.BaseAddress, region.Size); f.SuspiciousStrings.AddRange(strings.Take(5)); f.SuspiciousStringHits += strings.Count; var apis = _peAnalyzer.ExtractSuspiciousApis(hProcess, region.BaseAddress, Math.Min(region.Size, 32768)); f.SuspiciousApiStrings.AddRange(apis); } // 去重 f.SuspiciousStrings = f.SuspiciousStrings.Distinct().ToList(); f.SuspiciousApiStrings = f.SuspiciousApiStrings.Distinct().ToList(); } finally { CloseHandle(hProcess); } // 签名匹配 f.SignatureMatchCount = matches.Count; f.HighSeverityMatches = matches.Count(m => m.Severity == "high"); } // ─── 熵值特征提取 ─── private void ExtractEntropyFeatures(MemoryFeatures f, int pid, List regions) { var hProcess = OpenProcess(PROCESS_VM_READ, false, pid); if (hProcess == IntPtr.Zero) return; try { var result = _entropyAnalyzer.AnalyzeRegions(hProcess, regions); f.AvgCodeEntropy = result.AvgEntropy; f.MaxEntropy = result.MaxEntropy; f.HighEntropyRegions = result.HighEntropyRegions; f.HighEntropyTotalSize = result.HighEntropyTotalSize; f.EntropyStdDev = result.EntropyStdDev; } finally { CloseHandle(hProcess); } } // ─── 网络特征提取 ─── private static void ExtractNetworkFeatures(MemoryFeatures f, List connections) { f.ConnectionCount = connections.Count; foreach (var conn in connections) { if (conn.State == "Listen") f.ListeningPorts++; // 非标准端口 if (conn.RemotePort > 0 && conn.RemotePort != 80 && conn.RemotePort != 443 && conn.RemotePort != 8080 && conn.RemotePort != 8443) { f.NonStandardPortConnections++; } // 已知C2端口 if (IsSuspiciousPort(conn.RemotePort)) f.SuspiciousPortConnections++; // 境外连接检测 (简化判断) if (IsForeignAddress(conn.RemoteAddr)) f.ForeignConnections++; } } private static bool IsSuspiciousPort(int port) => port switch { 4444 or 5555 or 6666 or 7777 or 1337 or 31337 or 9999 or 12345 or 27015 => true, _ => false }; private static bool IsForeignAddress(string addr) { if (string.IsNullOrEmpty(addr)) return false; // 私有地址范围 var ip = addr.Split(':')[0]; return !(ip.StartsWith("10.") || ip.StartsWith("172.16.") || ip.StartsWith("192.168.") || ip == "127.0.0.1" || ip == "0.0.0.0"); } // ─── 进程树辅助方法 ─── public static bool IsSystemProcessName(string name) => new HashSet(StringComparer.OrdinalIgnoreCase) { "svchost.exe", "csrss.exe", "winlogon.exe", "services.exe", "lsass.exe", "spoolsv.exe", "smss.exe", "wininit.exe", "dwm.exe", "taskhostw.exe", "sihost.exe", "runtimebroker.exe" }.Contains(name); public static bool HasLegitimateParent(string parentName, string childName) { // 正常父子关系 if (string.IsNullOrEmpty(parentName)) return false; var childLower = childName.ToLowerInvariant(); var parentLower = parentName.ToLowerInvariant(); // 系统进程的正常父进程 if (parentLower is "services.exe" or "wininit.exe" or "smss.exe") return true; // Explorer 启动用户程序 if (parentLower == "explorer.exe") return true; // 浏览器多进程 if ((parentLower.Contains("chrome") || parentLower.Contains("msedge") || parentLower.Contains("firefox")) && (childLower.Contains("chrome") || childLower.Contains("msedge") || childLower.Contains("firefox"))) return true; // svchost 只由 services.exe 启动 if (childLower == "svchost.exe" && parentLower == "services.exe") return true; return false; } /// /// 判断是否为脚本引擎/Shell —— 这类进程自身包含大量"可疑"字符串 /// 但属于正常功能(如 PowerShell 的 Invoke-Expression, IEX 等是合法的内置cmdlet) /// public static bool IsScriptingEngine(ProcessInfo proc) { if (!proc.IsSigned || !proc.IsSystemPath) return false; var nameLower = proc.Name.ToLowerInvariant(); return nameLower switch { "powershell.exe" or "pwsh.exe" or "powershell_ise.exe" => true, "cmd.exe" => true, "wscript.exe" or "cscript.exe" => true, "conhost.exe" => true, "python.exe" or "python3.exe" or "pythonw.exe" => true, "wsl.exe" or "bash.exe" => true, _ => false }; } private static int CountUnmappedModules(List regions) => regions.Count(r => r.Type == "PRIVATE" && r.Protect.Contains("EXECUTE") && r.Size > 4096); private static int CountTempModules(List modules) => modules.Count(m => m.Contains("\\Temp\\", StringComparison.OrdinalIgnoreCase) || m.Contains("\\tmp\\", StringComparison.OrdinalIgnoreCase) || m.Contains("\\AppData\\Local\\Temp\\", StringComparison.OrdinalIgnoreCase)); // ─── P/Invoke ─── [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); }