package scanner import ( "log" "os" "path/filepath" "time" "github.com/shirou/gopsutil/v4/cpu" "github.com/wuko233/sysmonitord/internal/network" "github.com/wuko233/sysmonitord/internal/whitelist" ) type Scanner struct { wlManager *whitelist.Manager client *network.WSClient cpuLimit float64 scanPaths []string stopChan chan struct{} } func NewScanner(wl *whitelist.Manager, client *network.WSClient) *Scanner { return &Scanner{ wlManager: wl, client: client, cpuLimit: 50.0, scanPaths: []string{"/bin", "/sbin", "/usr/bin", "/usr/sbin", "/etc", "/tmp", "/home"}, stopChan: make(chan struct{}), } } func (s *Scanner) Start() { log.Println("[扫描器] 启动文件完整性扫描...") go s.scanLoop() } func (s *Scanner) scanLoop() { ticker := time.NewTicker(10 * time.Minute) defer ticker.Stop() for { select { case <-s.stopChan: return case <-ticker.C: s.performScan() } } } func (s *Scanner) performScan() { log.Println("[扫描器] 开始新一轮全盘扫描") for _, root := range s.scanPaths { err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { select { case <-s.stopChan: return filepath.SkipDir default: } if err != nil { return nil } s.checkCPUAndSleep() if info.IsDir() { if s.wlManager.IsPathIgnored(path) { return filepath.SkipDir } return nil } isWhitelisted, isHashMatch, err := s.wlManager.CheckFileStatus(path) if err != nil { log.Printf("[扫描器] 检查文件状态失败: %v", err) return nil } if !isWhitelisted { log.Printf("[扫描器] 发现未在白名单文件: %s", path) s.reportFile(path, "NON_WHITELISTED_FILE") } else if !isHashMatch { log.Printf("[扫描器] 警告!文件Hash不匹配(可能被篡改): %s", path) s.reportFile(path, "FILE_HASH_MISMATCH") } return nil }) if err != nil { log.Printf("[扫描器] 扫描目录 %s 出错: %v", root, err) } } } func (s *Scanner) checkCPUAndSleep() { percent, err := cpu.Percent(time.Second, false) if err != nil || len(percent) == 0 { log.Printf("[扫描器] 获取CPU使用率失败: %v", err) return } if percent[0] > s.cpuLimit { log.Printf("[扫描器] CPU使用率过高 (%.2f%%),暂停扫描5秒", percent[0]) time.Sleep(5 * time.Second) } time.Sleep(10 * time.Millisecond) } func (s *Scanner) reportFile(path string, alertType string) { payload := map[string]interface{}{ "filepath": path, "status": "detected", } packet := network.NewPactet(alertType, payload) s.client.SendQueue(packet) } func (s *Scanner) Stop() { log.Println("[扫描器] 停止文件完整性扫描...") close(s.stopChan) }