Compare commits
4 Commits
92b8ae9648
...
cfa92618a8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfa92618a8 | ||
|
|
828d7c6d56 | ||
|
|
8f16df10dd | ||
|
|
880cbb6db9 |
|
|
@ -5,7 +5,6 @@ import (
|
|||
"os"
|
||||
"sysmonitord/internal/config"
|
||||
"sysmonitord/internal/scanner/file"
|
||||
"sysmonitord/internal/scanner/hash"
|
||||
"sysmonitord/internal/scanner/process"
|
||||
"sysmonitord/internal/storage"
|
||||
"sysmonitord/pkg/logger"
|
||||
|
|
@ -32,12 +31,6 @@ var StartCmd = &cobra.Command{
|
|||
zap.String("审计服务器地址", fmt.Sprintf("%s:%d", cfg.Audit.Server, cfg.Audit.Port)),
|
||||
)
|
||||
|
||||
hashCfg := &hash.Config{
|
||||
UseFastHash: cfg.Scanner.File.FastHash,
|
||||
Threshold: cfg.Scanner.File.FastHashSize,
|
||||
ChunkSize: cfg.Scanner.File.FastHashChunk,
|
||||
}
|
||||
|
||||
storageCfg := &storage.Storage{
|
||||
DataDir: cfg.Storage.DataDir,
|
||||
ProcessSystemFile: cfg.Storage.ProcessSystemFile,
|
||||
|
|
@ -46,7 +39,8 @@ var StartCmd = &cobra.Command{
|
|||
|
||||
// ====== 进程扫描和存储 ======
|
||||
|
||||
procs, err := process.ScanAllProcesses(hashCfg)
|
||||
startTime := time.Now()
|
||||
procs, err := process.ScanAllProcesses(cfg)
|
||||
if err != nil {
|
||||
logger.Log.Error("扫描进程失败", zap.Error(err))
|
||||
os.Exit(1)
|
||||
|
|
@ -75,16 +69,9 @@ var StartCmd = &cobra.Command{
|
|||
// ====== 文件扫描和存储 ======
|
||||
logger.Log.Info("正在扫描文件系统...")
|
||||
|
||||
startTime := time.Now()
|
||||
fileCfg := &config.FileScannerConfig{
|
||||
IncludePaths: cfg.Scanner.File.IncludePaths,
|
||||
ExcludePaths: cfg.Scanner.File.ExcludePaths,
|
||||
FastHash: cfg.Scanner.File.FastHash,
|
||||
FastHashSize: cfg.Scanner.File.FastHashSize,
|
||||
FastHashChunk: cfg.Scanner.File.FastHashChunk,
|
||||
}
|
||||
startTime = time.Now()
|
||||
fileScanner := file.NewScanner(cfg)
|
||||
|
||||
fileScanner := file.NewScanner(fileCfg)
|
||||
files, err := fileScanner.Scan()
|
||||
if err != nil {
|
||||
logger.Log.Error("扫描文件系统失败", zap.Error(err))
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ audit:
|
|||
buffer_size: 1000
|
||||
|
||||
scanner:
|
||||
hash:
|
||||
# algorithm: "sha256"
|
||||
# algorithm: "md5"
|
||||
algorithm: "xxhash64"
|
||||
file:
|
||||
include_paths:
|
||||
- /home
|
||||
- /home/wuko233/Downloads
|
||||
exclude_paths:
|
||||
- /proc
|
||||
- /sys
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -3,6 +3,7 @@ module sysmonitord
|
|||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -1,3 +1,5 @@
|
|||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
|
|
|
|||
|
|
@ -1,14 +1,5 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sysmonitord/pkg/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Audit AuditConfig `yaml:"audit"`
|
||||
Scanner ScannerConfig `yaml:"scanner"`
|
||||
|
|
@ -24,6 +15,11 @@ type AuditConfig struct {
|
|||
|
||||
type ScannerConfig struct {
|
||||
File FileScannerConfig `yaml:"file"`
|
||||
Hash hashConfig `yaml:"hash"`
|
||||
}
|
||||
|
||||
type hashConfig struct {
|
||||
Algorithm string `yaml:"algorithm"`
|
||||
}
|
||||
|
||||
type StorageConfig struct {
|
||||
|
|
@ -41,34 +37,3 @@ type FileScannerConfig struct {
|
|||
FastHashSize int64
|
||||
FastHashChunk int64
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无法读取配置文件: %w", err)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("无法解析配置文件: %w", err)
|
||||
}
|
||||
|
||||
// 解析 FastHashSize
|
||||
cfg.Scanner.File.FastHashSize, err = ParseSize(cfg.Scanner.File.FastHashSizeRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析 fast_hash_size 失败: %w", err)
|
||||
}
|
||||
|
||||
// 解析 FastHashChunk
|
||||
cfg.Scanner.File.FastHashChunk, err = ParseSize(cfg.Scanner.File.FastHashChunkRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析 fast_hash_chunk 失败: %w", err)
|
||||
}
|
||||
|
||||
logger.Log.Debug("配置加载完成",
|
||||
zap.Int64("fast_hash_size", cfg.Scanner.File.FastHashSize),
|
||||
zap.Int64("fast_hash_chunk", cfg.Scanner.File.FastHashChunk),
|
||||
)
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,49 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"sysmonitord/internal/scanner/hash"
|
||||
"sysmonitord/pkg/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无法读取配置文件: %w", err)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("无法解析配置文件: %w", err)
|
||||
}
|
||||
|
||||
// 解析 FastHashSize
|
||||
cfg.Scanner.File.FastHashSize, err = ParseSize(cfg.Scanner.File.FastHashSizeRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析 fast_hash_size 失败: %w", err)
|
||||
}
|
||||
|
||||
// 解析 FastHashChunk
|
||||
cfg.Scanner.File.FastHashChunk, err = ParseSize(cfg.Scanner.File.FastHashChunkRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析 fast_hash_chunk 失败: %w", err)
|
||||
}
|
||||
|
||||
logger.Log.Debug("配置加载完成",
|
||||
zap.Int64("fast_hash_size", cfg.Scanner.File.FastHashSize),
|
||||
zap.Int64("fast_hash_chunk", cfg.Scanner.File.FastHashChunk),
|
||||
)
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func ParseSize(sizeStr string) (int64, error) {
|
||||
sizeStr = strings.TrimSpace(sizeStr)
|
||||
if sizeStr == "" {
|
||||
|
|
@ -44,3 +82,36 @@ func ParseSize(sizeStr string) (int64, error) {
|
|||
|
||||
return value * multiplier, nil
|
||||
}
|
||||
|
||||
func (c *Config) GetHashAlgorithm() (hash.HashAlgorithm, error) {
|
||||
algoName := c.Scanner.Hash.Algorithm
|
||||
|
||||
switch strings.ToLower(algoName) {
|
||||
case "sha256":
|
||||
return &hash.SHA256Algorithm{}, nil
|
||||
case "md5":
|
||||
return &hash.MD5Algorithm{}, nil
|
||||
case "xxhash64":
|
||||
return &hash.XXHash64Algorithm{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的哈希算法: %s", algoName)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) GetFileScannerConfig() (*FileScannerConfig, error) {
|
||||
return &c.Scanner.File, nil
|
||||
}
|
||||
|
||||
func (c *Config) GetHashConfig() (*hash.Config, error) {
|
||||
algo, err := c.GetHashAlgorithm()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &hash.Config{
|
||||
UseFastHash: c.Scanner.File.FastHash,
|
||||
Threshold: c.Scanner.File.FastHashSize,
|
||||
ChunkSize: c.Scanner.File.FastHashChunk,
|
||||
Algorithm: algo,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,17 +21,17 @@ type FileInfo struct {
|
|||
}
|
||||
|
||||
type Scanner struct {
|
||||
cfg *config.FileScannerConfig
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func NewScanner(cfg *config.FileScannerConfig) *Scanner {
|
||||
func NewScanner(cfg *config.Config) *Scanner {
|
||||
return &Scanner{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) Scan() ([]FileInfo, error) {
|
||||
targetPaths := s.cfg.IncludePaths
|
||||
targetPaths := s.cfg.Scanner.File.IncludePaths
|
||||
if len(targetPaths) == 0 {
|
||||
targetPaths = []string{"/"}
|
||||
}
|
||||
|
|
@ -65,10 +65,10 @@ func (s *Scanner) WalkFunc(result *[]FileInfo) fs.WalkDirFunc {
|
|||
return nil
|
||||
}
|
||||
|
||||
for _, exclude := range s.cfg.ExcludePaths {
|
||||
for _, exclude := range s.cfg.Scanner.File.ExcludePaths {
|
||||
if strings.HasPrefix(path, exclude) {
|
||||
logger.Log.Debug("[scan]跳过路径", zap.String("path", path), zap.String("reason", "匹配排除路径"))
|
||||
return fs.SkipDir
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +78,14 @@ func (s *Scanner) WalkFunc(result *[]FileInfo) fs.WalkDirFunc {
|
|||
return nil
|
||||
}
|
||||
|
||||
if info.Size() != 0 {
|
||||
hash, err := hash.SHA256(path, &hash.Config{
|
||||
UseFastHash: s.cfg.FastHash,
|
||||
Threshold: s.cfg.FastHashSize,
|
||||
ChunkSize: s.cfg.FastHashChunk,
|
||||
})
|
||||
if info.Size() > 0 {
|
||||
hashCfg, err := s.cfg.GetHashConfig()
|
||||
if err != nil {
|
||||
logger.Log.Debug("[scan]无法获取哈希配置", zap.String("path", path), zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
|
||||
hash, err := hash.Calculate(path, info.Size(), hashCfg)
|
||||
if err != nil {
|
||||
logger.Log.Debug("[scan]无法计算文件哈希", zap.String("path", path), zap.Error(err))
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1,43 +1,144 @@
|
|||
package hash
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
"sysmonitord/pkg/logger"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type HashAlgorithm interface {
|
||||
Hash() hash.Hash
|
||||
Name() string
|
||||
}
|
||||
|
||||
// ==== SHA256 ====
|
||||
|
||||
type SHA256Algorithm struct{}
|
||||
|
||||
func (a *SHA256Algorithm) Hash() hash.Hash {
|
||||
return sha256.New()
|
||||
}
|
||||
|
||||
func (a *SHA256Algorithm) Name() string {
|
||||
return "sha256"
|
||||
}
|
||||
|
||||
// ==== MD5 ====
|
||||
|
||||
type MD5Algorithm struct{}
|
||||
|
||||
func (a *MD5Algorithm) Hash() hash.Hash {
|
||||
return md5.New()
|
||||
}
|
||||
|
||||
func (a *MD5Algorithm) Name() string {
|
||||
return "md5"
|
||||
}
|
||||
|
||||
// ==== xxHash64 ====
|
||||
|
||||
type XXHash64Algorithm struct{}
|
||||
|
||||
func (a *XXHash64Algorithm) Hash() hash.Hash {
|
||||
return &xxHash64Wrapper{
|
||||
xxhash: xxhash.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *XXHash64Algorithm) Name() string {
|
||||
return "xxhash64"
|
||||
}
|
||||
|
||||
type xxHash64Wrapper struct {
|
||||
xxhash *xxhash.Digest
|
||||
}
|
||||
|
||||
func (w *xxHash64Wrapper) Write(p []byte) (n int, err error) {
|
||||
return w.xxhash.Write(p)
|
||||
}
|
||||
|
||||
// Sum 返回当前哈希值,追加到 b 后面
|
||||
// xxHash64 返回 8 字节的哈希值(小端序)
|
||||
func (w *xxHash64Wrapper) Sum(b []byte) []byte {
|
||||
// 获取当前的 64 位哈希值
|
||||
h := w.xxhash.Sum64()
|
||||
|
||||
// 将 uint64 转换为 8 字节的小端序字节数组
|
||||
buf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(buf, h)
|
||||
|
||||
// 追加到输入的 b 后面
|
||||
return append(b, buf...)
|
||||
}
|
||||
|
||||
// Reset 重置哈希状态
|
||||
func (w *xxHash64Wrapper) Reset() {
|
||||
w.xxhash.Reset()
|
||||
}
|
||||
|
||||
// Size 返回哈希值的字节数
|
||||
func (w *xxHash64Wrapper) Size() int {
|
||||
return 8 // xxHash64 输出 64 位 = 8 字节
|
||||
}
|
||||
|
||||
// BlockSize 返回底层哈希的块大小
|
||||
func (w *xxHash64Wrapper) BlockSize() int {
|
||||
return w.xxhash.BlockSize()
|
||||
}
|
||||
|
||||
// Sum64 提供直接获取 uint64 的便捷方法
|
||||
func (w *xxHash64Wrapper) Sum64() uint64 {
|
||||
return w.xxhash.Sum64()
|
||||
}
|
||||
|
||||
// ==== 配置结构体 ====
|
||||
|
||||
type Config struct {
|
||||
UseFastHash bool
|
||||
Threshold int64
|
||||
ChunkSize int64
|
||||
Algorithm HashAlgorithm
|
||||
}
|
||||
|
||||
func SHA256(filePath string, cfg *Config) (string, error) {
|
||||
// ==== 计算文件哈希 ====
|
||||
func Calculate(filePath string, fileSize int64, cfg *Config) (string, error) {
|
||||
if cfg == nil {
|
||||
cfg = &Config{
|
||||
Algorithm: &SHA256Algorithm{},
|
||||
}
|
||||
}
|
||||
|
||||
if fileSize == 0 {
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
logger.Log.Warn("[hash]获取文件信息失败", zap.String("path", filePath), zap.Error(err))
|
||||
logger.Log.Warn("[scanner]获取文件信息失败", zap.String("path", filePath), zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
fileSize := info.Size()
|
||||
|
||||
if cfg != nil && cfg.UseFastHash && fileSize > cfg.Threshold {
|
||||
logger.Log.Debug("[hash] 分层哈希...",
|
||||
zap.String("path", filePath),
|
||||
zap.Int64("fileSize", fileSize),
|
||||
)
|
||||
return calculateFastHash(filePath, fileSize, cfg.ChunkSize)
|
||||
fileSize = info.Size()
|
||||
}
|
||||
|
||||
return calculateFullHash(filePath)
|
||||
if cfg.Algorithm == nil {
|
||||
cfg.Algorithm = &SHA256Algorithm{}
|
||||
}
|
||||
|
||||
logger.Log.Debug("[scanner]计算文件哈希", zap.String("path", filePath), zap.Int64("size", fileSize), zap.String("algorithm", cfg.Algorithm.Name()))
|
||||
|
||||
if cfg.UseFastHash && fileSize > cfg.Threshold {
|
||||
return calculateFast(filePath, fileSize, cfg)
|
||||
} else {
|
||||
return calculateFull(filePath, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
func calculateFullHash(filePath string) (string, error) {
|
||||
func calculateFull(filePath string, cfg *Config) (string, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
logger.Log.Warn("[scanner]打开文件失败", zap.String("path", filePath), zap.Error(err))
|
||||
|
|
@ -45,7 +146,7 @@ func calculateFullHash(filePath string) (string, error) {
|
|||
}
|
||||
defer file.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
hasher := cfg.Algorithm.Hash()
|
||||
if _, err := io.Copy(hasher, file); err != nil {
|
||||
logger.Log.Error("[scanner]读取文件失败", zap.String("path", filePath), zap.Error(err))
|
||||
return "", err
|
||||
|
|
@ -56,7 +157,7 @@ func calculateFullHash(filePath string) (string, error) {
|
|||
return hashString, nil
|
||||
}
|
||||
|
||||
func calculateFastHash(filePath string, fileSize int64, chunkSize int64) (string, error) {
|
||||
func calculateFast(filePath string, fileSize int64, cfg *Config) (string, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
logger.Log.Warn("[scanner]打开文件失败", zap.String("path", filePath), zap.Error(err))
|
||||
|
|
@ -64,7 +165,8 @@ func calculateFastHash(filePath string, fileSize int64, chunkSize int64) (string
|
|||
}
|
||||
defer file.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
hasher := cfg.Algorithm.Hash()
|
||||
chunkSize := cfg.ChunkSize
|
||||
|
||||
if _, err := io.CopyN(hasher, file, chunkSize); err != nil {
|
||||
if err != io.EOF {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package process
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sysmonitord/internal/config"
|
||||
"sysmonitord/internal/scanner/hash"
|
||||
"sysmonitord/pkg/logger"
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ type ProcessInfo struct {
|
|||
FileHash string `json:"file_hash"`
|
||||
}
|
||||
|
||||
func ScanAllProcesses(hashCfg *hash.Config) ([]ProcessInfo, error) {
|
||||
func ScanAllProcesses(cfg *config.Config) ([]ProcessInfo, error) {
|
||||
logger.Log.Info("[scan]正在扫描系统中的所有进程...")
|
||||
|
||||
pids, err := process.Pids()
|
||||
|
|
@ -28,6 +29,13 @@ func ScanAllProcesses(hashCfg *hash.Config) ([]ProcessInfo, error) {
|
|||
}
|
||||
|
||||
var processList []ProcessInfo
|
||||
|
||||
hashCfg, err := cfg.GetHashConfig()
|
||||
if err != nil {
|
||||
logger.Log.Error("[scan]获取哈希配置失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
p, err := process.NewProcess(pid)
|
||||
if err != nil {
|
||||
|
|
@ -58,7 +66,7 @@ func ScanAllProcesses(hashCfg *hash.Config) ([]ProcessInfo, error) {
|
|||
|
||||
if exePath != "" {
|
||||
if _, err := os.Stat(exePath); err == nil {
|
||||
fileHash, err := hash.SHA256(exePath, hashCfg)
|
||||
fileHash, err := hash.Calculate(exePath, 0, hashCfg)
|
||||
if err == nil {
|
||||
info.FileHash = fileHash
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user