check.go 1.87 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package logic

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"sshd-tool/utils"
	"strconv"
	"strings"
)

var (
	// 需要的二进制文件
16
	NeedExec = []string{"clamscan", "clamdscan"}
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

	ReInfectedFile = regexp.MustCompile(`(?i)^infected\s+files:\s+([0-9]+)\s*$`)

	ErrNoLineMatch = errors.New("no line match")
)

func CheckExec() error {
	for _, v := range NeedExec {
		p := utils.FindCmd(v)
		if p == nil {
			return fmt.Errorf("%s not found", v)
		}
	}
	return nil
}

33
34
35
// todo 添加对tar、tgz等压缩文件的优化
// 压缩文件大小在500MB内,解压出流,让clamdscan扫描流

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// ScanFile 扫描文件,返回是否有毒,如果有毒会删除
func ScanFile(f string) (bool, error) {
	finfo, err := os.Stat(f)
	if err != nil {
		return false, err
	}
	if finfo.IsDir() {
		return false, errors.New("error: dest is directory")
	}
	sf := fmt.Sprintf("%s.scanning", f)
	err = os.Rename(f, sf)
	if err != nil {
		return false, err
	}

	cmd := exec.Command("clamdscan", "-i", sf)
	output, err := cmd.CombinedOutput()
	if err != nil {
		if cmd.ProcessState.ExitCode() != 1 {
			return false, fmt.Errorf("run clamdscan failed, output: %s", string(output))
		}
	}
	if cmd.ProcessState.ExitCode() == 1 {
		// 返回值不为0,有异常
		str := string(output)
		num, err := parseClamOutput(str)
		if err != nil {
			return false, fmt.Errorf("parse clamdscan output error, output: %s", str)
		}
		if num > 0 {
			// todo:可以在这里插入日志
			os.Remove(sf)
		}
		return true, nil
	} else {
		// 无异常,直接返回
		err = os.Rename(sf, f)
		return false, err
	}
}

// parseClamOutput 解析clamdscan输出
func parseClamOutput(str string) (int, error) {
	for _, line := range strings.Split(strings.Trim(str, "\n"), "\n") {
		if ReInfectedFile.MatchString(line) {
			f := ReInfectedFile.FindStringSubmatch(line)
			return strconv.Atoi(f[1])
		}
	}
	return 0, ErrNoLineMatch
}