//go:build windows package collector import ( "bytes" "context" "encoding/csv" "errors" "fmt" "math/bits" "net" "os" "os/exec" "runtime" "sort" "strconv" "strings" "syscall" "time" "unsafe" "mfd-monitor-agent/internal/status" ) const ( windowsAFInet = 2 windowsAFInet6 = 23 windowsNoError = 0 windowsErrorInsufficientBuffer = 122 windowsTCPTableOwnerPIDListen = 3 windowsTCPStateListen = 2 windowsDriveFixed = 3 windowsDriveRemote = 4 windowsProcessQueryInformation = 0x0400 windowsProcessQueryLimitedInfo = 0x1000 windowsProcessVMRead = 0x0010 windowsTokenQuery = 0x0008 windowsMaxPath = 260 windowsRegSZ = 1 windowsRelationProcessorCore = 0 ) var ( windowsKernel32 = syscall.NewLazyDLL("kernel32.dll") windowsGlobalMemoryStatusExProc = windowsKernel32.NewProc("GlobalMemoryStatusEx") windowsGetTickCount64Proc = windowsKernel32.NewProc("GetTickCount64") windowsGetLogicalDrivesProc = windowsKernel32.NewProc("GetLogicalDrives") windowsGetDriveTypeWProc = windowsKernel32.NewProc("GetDriveTypeW") windowsGetDiskFreeSpaceExWProc = windowsKernel32.NewProc("GetDiskFreeSpaceExW") windowsGetSystemTimesProc = windowsKernel32.NewProc("GetSystemTimes") windowsGetNativeSystemInfoProc = windowsKernel32.NewProc("GetNativeSystemInfo") windowsGetLogicalProcessorInfoProc = windowsKernel32.NewProc("GetLogicalProcessorInformation") windowsQueryFullProcessImageNameWProc = windowsKernel32.NewProc("QueryFullProcessImageNameW") windowsIphlpapi = syscall.NewLazyDLL("iphlpapi.dll") windowsGetExtendedTCPTableProc = windowsIphlpapi.NewProc("GetExtendedTcpTable") windowsPsapi = syscall.NewLazyDLL("psapi.dll") windowsGetProcessMemoryInfoProc = windowsPsapi.NewProc("GetProcessMemoryInfo") windowsAdvapi32 = syscall.NewLazyDLL("advapi32.dll") windowsOpenProcessTokenProc = windowsAdvapi32.NewProc("OpenProcessToken") windowsGetTokenInformationProc = windowsAdvapi32.NewProc("GetTokenInformation") windowsLookupAccountSidWProc = windowsAdvapi32.NewProc("LookupAccountSidW") ) // windowsMemoryStatus 镜像 GlobalMemoryStatusEx 使用的 Windows MEMORYSTATUSEX 结构。 type windowsMemoryStatus struct { Length uint32 MemoryLoad uint32 TotalPhys uint64 AvailPhys uint64 TotalPageFile uint64 AvailPageFile uint64 TotalVirtual uint64 AvailVirtual uint64 AvailExtendedVirtual uint64 } // windowsMIBTCPRowOwnerPID 镜像 GetExtendedTcpTable 返回的 MIB_TCPROW_OWNER_PID 行。 type windowsMIBTCPRowOwnerPID struct { State uint32 LocalAddr uint32 LocalPort uint32 RemoteAddr uint32 RemotePort uint32 OwningPID uint32 } // windowsMIBTCP6RowOwnerPID 镜像 GetExtendedTcpTable 返回的 MIB_TCP6ROW_OWNER_PID 行。 type windowsMIBTCP6RowOwnerPID struct { State uint32 LocalAddr [16]byte LocalScopeID uint32 LocalPort uint32 RemoteAddr [16]byte RemoteScopeID uint32 RemotePort uint32 OwningPID uint32 } // windowsSystemInfo 镜像 CPU 封装规模计算所需的 SYSTEM_INFO 子集。 type windowsSystemInfo struct { ProcessorArchitecture uint16 Reserved uint16 PageSize uint32 MinimumApplicationAddress uintptr MaximumApplicationAddress uintptr ActiveProcessorMask uintptr NumberOfProcessors uint32 ProcessorType uint32 AllocationGranularity uint32 ProcessorLevel uint16 ProcessorRevision uint16 } // windowsSystemTimes 以 100 纳秒为单位存储系统 CPU 时间计数。 type windowsSystemTimes struct { Idle uint64 Kernel uint64 User uint64 } // windowsLogicalProcessorInformation 镜像用于统计核心的 SYSTEM_LOGICAL_PROCESSOR_INFORMATION。 type windowsLogicalProcessorInformation struct { ProcessorMask uintptr Relationship uint32 Data [16]byte } // windowsProcessMemoryCounters 镜像 PROCESS_MEMORY_COUNTERS。 type windowsProcessMemoryCounters struct { CB uint32 PageFaultCount uint32 PeakWorkingSetSize uintptr WorkingSetSize uintptr QuotaPeakPagedPoolUsage uintptr QuotaPagedPoolUsage uintptr QuotaPeakNonPagedPoolUsage uintptr QuotaNonPagedPoolUsage uintptr PagefileUsage uintptr PeakPagefileUsage uintptr } // windowsProcessDetails 包含需额外句柄或命令才能获取的尽力进程字段。 type windowsProcessDetails struct { Exe string Cmdline string Username string Status string StartTime int64 CPUPercent float64 MemoryRSS uint64 MemoryVMS uint64 } // collectSystem 填充 Windows 主机标识、操作系统与架构字段。 func collectSystem(_ context.Context, hostID string, out *status.HostStatus) error { hostname, err := os.Hostname() if err != nil { return err } out.Host.HostID = hostID if out.Host.HostID == "" { out.Host.HostID = hostname } out.Host.Hostname = hostname out.Host.Platform = runtime.GOOS out.Host.PlatformVersion = windowsVersion() out.Host.Arch = runtime.GOARCH out.Host.UptimeSeconds = windowsUptimeSeconds() return nil } // collectNetwork 使用可移植 net 包填充 Windows 网卡、地址与 MAC 数据。 func collectNetwork(_ context.Context, out *status.HostStatus) error { ifaces, err := net.Interfaces() if err != nil { return err } seenIPs := map[string]struct{}{} seenMACs := map[string]struct{}{} for _, iface := range ifaces { item := status.NetworkInterface{Name: iface.Name, MAC: iface.HardwareAddr.String(), MTU: iface.MTU, Flags: strings.Fields(iface.Flags.String())} addrs, _ := iface.Addrs() for _, addr := range addrs { item.Addresses = append(item.Addresses, addr.String()) seenIPs[addr.String()] = struct{}{} } if item.MAC != "" { seenMACs[item.MAC] = struct{}{} } out.Network.Interfaces = append(out.Network.Interfaces, item) } out.Network.IPv4s, out.Network.IPv6s = networkAddressSets(seenIPs) out.Network.MACs = windowsSortedKeys(seenMACs) return nil } // collectResource 填充 Windows CPU、内存与磁盘资源数据。 func collectResource(ctx context.Context, out *status.HostStatus) error { var errs []error out.CPU.LogicalCores = runtime.NumCPU() out.CPU.PhysicalCores = windowsPhysicalCores() out.CPU.ModelName = windowsCPUModelName() out.CPU.UsagePercent = windowsReadCPUUsagePercent(ctx) memory, err := windowsReadMemory() if err != nil { errs = append(errs, err) } else { out.Memory = memory } disks, err := windowsReadDisks() if err != nil { errs = append(errs, err) } else { out.Disks = disks } return errors.Join(errs...) } // collectProcesses 通过 Toolhelp 枚举 Windows 进程并返回尽力获取的名称等信息。 func collectProcesses(_ context.Context, out *status.HostStatus) error { processes, err := windowsProcessList() out.Processes.Items = processes if err != nil { out.Processes.Status = "partial" return err } out.Processes.Status = "ok" return nil } // collectSockets 读取 Windows TCP 监听表并将监听 PID 映射到进程名。 func collectSockets(_ context.Context, out *status.HostStatus) error { processes, processErr := windowsProcessMap() var errs []error if processErr != nil { errs = append(errs, processErr) } listeners4, err := windowsTCPListeners(windowsAFInet, "ipv4", processes) if err != nil { errs = append(errs, err) } listeners6, err := windowsTCPListeners(windowsAFInet6, "ipv6", processes) if err != nil { errs = append(errs, err) } out.TCPListeners.Items = append(listeners4, listeners6...) sort.Slice(out.TCPListeners.Items, func(i, j int) bool { if out.TCPListeners.Items[i].IP == out.TCPListeners.Items[j].IP { return out.TCPListeners.Items[i].Port < out.TCPListeners.Items[j].Port } return out.TCPListeners.Items[i].IP < out.TCPListeners.Items[j].IP }) if len(errs) > 0 { out.TCPListeners.Status = "partial" return errors.Join(errs...) } out.TCPListeners.Status = "ok" return nil } // windowsReadMemory 调用 GlobalMemoryStatusEx 并将物理内存计数转换为 API 字段。 func windowsReadMemory() (status.MemoryInfo, error) { memory := windowsMemoryStatus{Length: uint32(unsafe.Sizeof(windowsMemoryStatus{}))} ok, _, err := windowsGlobalMemoryStatusExProc.Call(uintptr(unsafe.Pointer(&memory))) if ok == 0 { return status.MemoryInfo{}, err } return windowsMemoryInfoFromStatus(memory), nil } // windowsVersion 返回 GetVersion 暴露的传统 major.minor.build 版本元组。 func windowsVersion() string { version, err := syscall.GetVersion() if err != nil { return "" } major := byte(version) minor := uint8(version >> 8) build := uint16(version >> 16) return fmt.Sprintf("%d.%d.%d", major, minor, build) } // windowsUptimeSeconds 从 GetTickCount64 返回主机运行时长(秒)。 func windowsUptimeSeconds() uint64 { millis, _, _ := windowsGetTickCount64Proc.Call() return uint64(millis) / 1000 } // windowsPhysicalCores 从 Windows 处理器拓扑返回物理 CPU 核心数。 func windowsPhysicalCores() int { var size uint32 result, _, _ := windowsGetLogicalProcessorInfoProc.Call(0, uintptr(unsafe.Pointer(&size))) if result == 0 && size > 0 { buf := make([]byte, size) result, _, _ = windowsGetLogicalProcessorInfoProc.Call(uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size))) if result != 0 { entrySize := int(unsafe.Sizeof(windowsLogicalProcessorInformation{})) cores := 0 for offset := 0; offset+entrySize <= int(size); offset += entrySize { entry := (*windowsLogicalProcessorInformation)(unsafe.Pointer(&buf[offset])) if entry.Relationship == windowsRelationProcessorCore { cores++ } } if cores > 0 { return cores } } } var info windowsSystemInfo windowsGetNativeSystemInfoProc.Call(uintptr(unsafe.Pointer(&info))) if info.NumberOfProcessors > 0 { return int(info.NumberOfProcessors) } if info.ActiveProcessorMask != 0 { return bits.OnesCount64(uint64(info.ActiveProcessorMask)) } return runtime.NumCPU() } // windowsCPUModelName 从注册表读取首个处理器名称。 func windowsCPUModelName() string { keyPath, _ := syscall.UTF16PtrFromString(`HARDWARE\DESCRIPTION\System\CentralProcessor\0`) var key syscall.Handle if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, keyPath, 0, syscall.KEY_READ, &key); err != nil { return "" } defer syscall.RegCloseKey(key) valueName, _ := syscall.UTF16PtrFromString("ProcessorNameString") var valueType uint32 var size uint32 if err := syscall.RegQueryValueEx(key, valueName, nil, &valueType, nil, &size); err != nil || valueType != windowsRegSZ || size == 0 { return "" } buf := make([]byte, size) if err := syscall.RegQueryValueEx(key, valueName, nil, &valueType, &buf[0], &size); err != nil { return "" } utf16s := make([]uint16, 0, len(buf)/2) for i := 0; i+1 < int(size); i += 2 { utf16s = append(utf16s, uint16(buf[i])|uint16(buf[i+1])<<8) } return strings.TrimSpace(syscall.UTF16ToString(utf16s)) } // windowsReadCPUUsagePercent 采样 GetSystemTimes 并返回尽力估算的 CPU 使用率。 func windowsReadCPUUsagePercent(ctx context.Context) float64 { prev, err := windowsReadSystemTimes() if err != nil { return 0 } select { case <-ctx.Done(): return 0 case <-time.After(100 * time.Millisecond): } next, err := windowsReadSystemTimes() if err != nil { return 0 } return windowsCPUUsagePercent(prev, next) } // windowsReadSystemTimes 从 GetSystemTimes 返回空闲、内核与用户系统计数。 func windowsReadSystemTimes() (windowsSystemTimes, error) { var idle, kernel, user syscall.Filetime ok, _, err := windowsGetSystemTimesProc.Call( uintptr(unsafe.Pointer(&idle)), uintptr(unsafe.Pointer(&kernel)), uintptr(unsafe.Pointer(&user)), ) if ok == 0 { return windowsSystemTimes{}, err } return windowsSystemTimes{Idle: windowsFiletimeUint64(idle), Kernel: windowsFiletimeUint64(kernel), User: windowsFiletimeUint64(user)}, nil } // windowsCPUUsagePercent 根据两次 Windows CPU 采样计算非空闲 CPU 百分比。 func windowsCPUUsagePercent(prev windowsSystemTimes, next windowsSystemTimes) float64 { prevTotal := prev.Kernel + prev.User nextTotal := next.Kernel + next.User if nextTotal <= prevTotal || next.Idle < prev.Idle { return 0 } totalDelta := nextTotal - prevTotal idleDelta := next.Idle - prev.Idle if totalDelta == 0 || idleDelta >= totalDelta { return 0 } return float64(totalDelta-idleDelta) / float64(totalDelta) * 100 } // windowsFiletimeUint64 将 FILETIME 转换为可单调比较的计数。 func windowsFiletimeUint64(value syscall.Filetime) uint64 { return uint64(value.HighDateTime)<<32 | uint64(value.LowDateTime) } // windowsReadDisks 枚举 Windows 本地固定盘与远程盘并返回容量数据。 func windowsReadDisks() ([]status.DiskInfo, error) { mask, _, _ := windowsGetLogicalDrivesProc.Call() if mask == 0 { return nil, syscall.GetLastError() } var disks []status.DiskInfo var errs []error for i := 0; i < 26; i++ { if mask&(1<= free { used = total - free } percent := 0.0 if total > 0 { percent = float64(used) / float64(total) * 100 } return status.DiskInfo{ Device: root, MountPoint: root, FSType: windowsDriveTypeName(driveType), Unit: "byte", Total: total, Used: used, Free: free, UsedPercent: percent, } } // windowsDriveTypeName 将 Windows 驱动器类型常量映射为稳定的 API 标签。 func windowsDriveTypeName(driveType uint32) string { switch driveType { case windowsDriveFixed: return "fixed" case windowsDriveRemote: return "remote" default: return "unknown" } } // windowsMemoryInfoFromStatus 将 Windows 物理内存计数转换为 HostStatus 内存数据。 func windowsMemoryInfoFromStatus(memory windowsMemoryStatus) status.MemoryInfo { used := uint64(0) if memory.TotalPhys >= memory.AvailPhys { used = memory.TotalPhys - memory.AvailPhys } percent := 0.0 if memory.TotalPhys > 0 { percent = float64(used) / float64(memory.TotalPhys) * 100 } return status.MemoryInfo{ Unit: "byte", Total: memory.TotalPhys, Used: used, Free: memory.AvailPhys, Available: memory.AvailPhys, UsedPercent: percent, } } // windowsTCPListeners 从 GetExtendedTcpTable 返回 IPv4 或 IPv6 TCP 监听项。 func windowsTCPListeners(family int, familyName string, processes map[int]status.ProcessInfo) ([]status.TCPListenerInfo, error) { table, err := windowsExtendedTCPTable(family) if err != nil { return nil, err } if len(table) < 4 { return nil, fmt.Errorf("windows tcp table for %s is too small", familyName) } count := *(*uint32)(unsafe.Pointer(&table[0])) if family == windowsAFInet { return windowsParseTCP4Rows(table[4:], count, processes), nil } return windowsParseTCP6Rows(table[4:], count, processes), nil } // windowsExtendedTCPTable 调用 GetExtendedTcpTable 并返回原始表字节。 func windowsExtendedTCPTable(family int) ([]byte, error) { var size uint32 result, _, _ := windowsGetExtendedTCPTableProc.Call( 0, uintptr(unsafe.Pointer(&size)), 1, uintptr(family), uintptr(windowsTCPTableOwnerPIDListen), 0, ) if result != windowsErrorInsufficientBuffer && result != windowsNoError { return nil, fmt.Errorf("GetExtendedTcpTable size query failed: %d", result) } if size == 0 { return nil, nil } table := make([]byte, size) result, _, _ = windowsGetExtendedTCPTableProc.Call( uintptr(unsafe.Pointer(&table[0])), uintptr(unsafe.Pointer(&size)), 1, uintptr(family), uintptr(windowsTCPTableOwnerPIDListen), 0, ) if result != windowsNoError { return nil, fmt.Errorf("GetExtendedTcpTable failed: %d", result) } return table, nil } // windowsParseTCP4Rows 将原始 MIB_TCPROW_OWNER_PID 行转换为监听记录。 func windowsParseTCP4Rows(rows []byte, count uint32, processes map[int]status.ProcessInfo) []status.TCPListenerInfo { rowSize := int(unsafe.Sizeof(windowsMIBTCPRowOwnerPID{})) items := make([]status.TCPListenerInfo, 0, count) for i := 0; i < int(count); i++ { offset := i * rowSize if offset+rowSize > len(rows) { break } row := *(*windowsMIBTCPRowOwnerPID)(unsafe.Pointer(&rows[offset])) if row.State != windowsTCPStateListen { continue } process := processes[int(row.OwningPID)] addr := row.LocalAddr items = append(items, status.TCPListenerInfo{ IP: net.IPv4(byte(addr), byte(addr>>8), byte(addr>>16), byte(addr>>24)).String(), Port: windowsTCPPortFromNetworkOrder(row.LocalPort), PID: int(row.OwningPID), ProcessName: process.Name, ProcessExe: process.Exe, Family: "ipv4", }) } return items } // windowsParseTCP6Rows 将原始 MIB_TCP6ROW_OWNER_PID 行转换为监听记录。 func windowsParseTCP6Rows(rows []byte, count uint32, processes map[int]status.ProcessInfo) []status.TCPListenerInfo { rowSize := int(unsafe.Sizeof(windowsMIBTCP6RowOwnerPID{})) items := make([]status.TCPListenerInfo, 0, count) for i := 0; i < int(count); i++ { offset := i * rowSize if offset+rowSize > len(rows) { break } row := *(*windowsMIBTCP6RowOwnerPID)(unsafe.Pointer(&rows[offset])) if row.State != windowsTCPStateListen { continue } process := processes[int(row.OwningPID)] items = append(items, status.TCPListenerInfo{ IP: net.IP(row.LocalAddr[:]).String(), Port: windowsTCPPortFromNetworkOrder(row.LocalPort), PID: int(row.OwningPID), ProcessName: process.Name, ProcessExe: process.Exe, Family: "ipv6", }) } return items } // windowsTCPPortFromNetworkOrder 解码 Windows 以网络字节序存储的低 16 位端口。 func windowsTCPPortFromNetworkOrder(raw uint32) int { port := uint16(raw) return int(port>>8) | int(port&0xff)<<8 } // windowsProcessList 使用 Windows Toolhelp 快照 API 枚举进程。 func windowsProcessList() ([]status.ProcessInfo, error) { cmdlines := windowsProcessCommandLines() snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) if err != nil { return nil, err } defer syscall.CloseHandle(snapshot) var entry syscall.ProcessEntry32 entry.Size = uint32(unsafe.Sizeof(entry)) if err := syscall.Process32First(snapshot, &entry); err != nil { return nil, err } var processes []status.ProcessInfo for { details := windowsReadProcessDetails(entry.ProcessID) if cmdline := cmdlines[int(entry.ProcessID)]; cmdline != "" { details.Cmdline = cmdline } processes = append(processes, windowsProcessInfoFromEntry(entry, details)) err = syscall.Process32Next(snapshot, &entry) if err != nil { if errors.Is(err, syscall.ERROR_NO_MORE_FILES) { return processes, nil } return processes, err } } } // windowsProcessInfoFromEntry 合并 Toolhelp 身份数据与可选进程详情。 func windowsProcessInfoFromEntry(entry syscall.ProcessEntry32, details windowsProcessDetails) status.ProcessInfo { name := syscall.UTF16ToString(entry.ExeFile[:]) exe := details.Exe if exe == "" { exe = name } return status.ProcessInfo{ PID: int(entry.ProcessID), PPID: int(entry.ParentProcessID), Name: name, Exe: exe, Cmdline: details.Cmdline, Username: details.Username, Status: details.Status, StartTime: details.StartTime, CPUPercent: details.CPUPercent, MemoryRSS: details.MemoryRSS, MemoryVMS: details.MemoryVMS, } } // windowsReadProcessDetails 打开进程并读取可能需要提升权限的字段。 func windowsReadProcessDetails(pid uint32) windowsProcessDetails { handle, err := syscall.OpenProcess(windowsProcessQueryLimitedInfo|windowsProcessQueryInformation|windowsProcessVMRead, false, pid) if err != nil { return windowsProcessDetails{Status: "unknown"} } defer syscall.CloseHandle(handle) details := windowsProcessDetails{Status: "running"} details.Exe = windowsQueryProcessImageName(handle) details.Username = windowsQueryProcessUsername(handle) details.StartTime, details.CPUPercent = windowsQueryProcessTimes(handle) details.MemoryRSS, details.MemoryVMS = windowsQueryProcessMemory(handle) return details } // windowsQueryProcessImageName 在访问允许时返回完整进程映像路径。 func windowsQueryProcessImageName(handle syscall.Handle) string { buf := make([]uint16, windowsMaxPath) size := uint32(len(buf)) ok, _, _ := windowsQueryFullProcessImageNameWProc.Call( uintptr(handle), 0, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), ) if ok == 0 || size == 0 { return "" } return syscall.UTF16ToString(buf[:size]) } // windowsQueryProcessTimes 返回启动时间与生命周期平均 CPU 百分比。 func windowsQueryProcessTimes(handle syscall.Handle) (int64, float64) { var creation, exit, kernel, user syscall.Filetime if err := syscall.GetProcessTimes(handle, &creation, &exit, &kernel, &user); err != nil { return 0, 0 } startNanos := creation.Nanoseconds() elapsedNanos := time.Now().UnixNano() - startNanos if elapsedNanos <= 0 { return startNanos / int64(time.Second), 0 } cpuNanos := int64(windowsFiletimeUint64(kernel)+windowsFiletimeUint64(user)) * 100 percent := float64(cpuNanos) / float64(elapsedNanos) * 100 / float64(runtime.NumCPU()) return startNanos / int64(time.Second), percent } // windowsQueryProcessMemory 在访问允许时返回工作集与页面文件占用。 func windowsQueryProcessMemory(handle syscall.Handle) (uint64, uint64) { counters := windowsProcessMemoryCounters{CB: uint32(unsafe.Sizeof(windowsProcessMemoryCounters{}))} ok, _, _ := windowsGetProcessMemoryInfoProc.Call( uintptr(handle), uintptr(unsafe.Pointer(&counters)), uintptr(counters.CB), ) if ok == 0 { return 0, 0 } return uint64(counters.WorkingSetSize), uint64(counters.PagefileUsage) } // windowsQueryProcessUsername 在访问允许时返回进程令牌的 DOMAIN\user。 func windowsQueryProcessUsername(handle syscall.Handle) string { var token syscall.Handle ok, _, _ := windowsOpenProcessTokenProc.Call(uintptr(handle), uintptr(windowsTokenQuery), uintptr(unsafe.Pointer(&token))) if ok == 0 { return "" } defer syscall.CloseHandle(token) var size uint32 windowsGetTokenInformationProc.Call(uintptr(token), 1, 0, 0, uintptr(unsafe.Pointer(&size))) if size == 0 { return "" } buf := make([]byte, size) ok, _, _ = windowsGetTokenInformationProc.Call(uintptr(token), 1, uintptr(unsafe.Pointer(&buf[0])), uintptr(size), uintptr(unsafe.Pointer(&size))) if ok == 0 || len(buf) < int(unsafe.Sizeof(uintptr(0)))+4 { return "" } sid := *(*uintptr)(unsafe.Pointer(&buf[0])) return windowsLookupAccountName(sid) } // windowsLookupAccountName 将 SID 指针解析为 DOMAIN\name。 func windowsLookupAccountName(sid uintptr) string { var nameSize, domainSize uint32 var sidType uint32 windowsLookupAccountSidWProc.Call(0, sid, 0, uintptr(unsafe.Pointer(&nameSize)), 0, uintptr(unsafe.Pointer(&domainSize)), uintptr(unsafe.Pointer(&sidType))) if nameSize == 0 { return "" } name := make([]uint16, nameSize) domain := make([]uint16, domainSize) ok, _, _ := windowsLookupAccountSidWProc.Call( 0, sid, uintptr(unsafe.Pointer(&name[0])), uintptr(unsafe.Pointer(&nameSize)), uintptr(unsafe.Pointer(&domain[0])), uintptr(unsafe.Pointer(&domainSize)), uintptr(unsafe.Pointer(&sidType)), ) if ok == 0 { return "" } account := syscall.UTF16ToString(name[:nameSize]) domainName := syscall.UTF16ToString(domain[:domainSize]) if domainName == "" { return account } return domainName + `\` + account } // windowsProcessCommandLines 通过一次尽力 WMIC 调用读取命令行,避免逐进程 shell。 func windowsProcessCommandLines() map[int]string { out := map[int]string{} ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() output, err := exec.CommandContext(ctx, "wmic", "process", "get", "ProcessId,CommandLine", "/format:csv").Output() if err != nil { return out } reader := csv.NewReader(bytes.NewReader(output)) reader.FieldsPerRecord = -1 records, err := reader.ReadAll() if err != nil { return out } for _, record := range records { if len(record) < 3 || strings.EqualFold(record[1], "CommandLine") { continue } pid, err := strconv.Atoi(strings.TrimSpace(record[2])) if err != nil { continue } out[pid] = strings.TrimSpace(record[1]) } return out } // windowsProcessMap 返回以 PID 为键的进程元数据,供 TCP 属主映射使用。 func windowsProcessMap() (map[int]status.ProcessInfo, error) { processes, err := windowsProcessList() out := make(map[int]status.ProcessInfo, len(processes)) for _, process := range processes { out[process.PID] = process } return out, err } // windowsSortedKeys 从字符串集合返回确定顺序的排序键列表。 func windowsSortedKeys(values map[string]struct{}) []string { keys := make([]string, 0, len(values)) for key := range values { keys = append(keys, key) } sort.Strings(keys) return keys }