├── .idea ├── .gitignore ├── vcs.xml ├── modules.xml └── monitor-Go.iml ├── go.mod ├── README.md ├── .github ├── workflows │ └── release1.yml └── conf │ └── .goreleaser.yml ├── go.sum └── main.go /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module monitor-Go 2 | 3 | go 1.21 4 | 5 | require github.com/gookit/color v1.5.4 6 | 7 | require ( 8 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect 9 | golang.org/x/sys v0.10.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/monitor-Go.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 文件监控 2 | 3 | 用来监控文件以及其子目录下的所有文件的增删改 4 | 5 | 此脚本[基于](https://gitee.com/piri47/AWD-1/blob/master/%E6%96%87%E4%BB%B6%E7%9B%91%E6%8E%A7.py) 6 | 7 | 做出的改进: 8 | 9 | 1. 不会删除原文件,避免因为某些意外情况而导致的原文件被删除 10 | 2. 前端打印所有详细信息,包括文件路径、被修改的时间、内容等 11 | 12 | + 用法 13 | 14 | 在需要监控的目录下直接启动即可 15 | ```go 16 | monitor-Go.exe 17 | ``` 18 | 19 | + 效果图 20 | 21 | ![](https://gallery-1304405887.cos.ap-nanjing.myqcloud.com/markdown33-14-23-133303.png) -------------------------------------------------------------------------------- /.github/workflows/release1.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | goreleaser: 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 60 15 | steps: 16 | - 17 | name: Checkout 18 | uses: actions/checkout@v2 19 | with: 20 | fetch-depth: 0 21 | - 22 | name: Set up Go 23 | uses: actions/setup-go@v2 24 | with: 25 | go-version: 1.21 26 | - 27 | name: Run GoReleaser 28 | uses: goreleaser/goreleaser-action@v2 29 | with: 30 | distribution: goreleaser 31 | version: latest 32 | args: -f .github/conf/.goreleaser.yml 33 | workdir: . 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= 4 | github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 8 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 9 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= 10 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= 11 | golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= 12 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 13 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 14 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 15 | -------------------------------------------------------------------------------- /.github/conf/.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - sudo apt -y install libprotobuf-dev protobuf-compiler protoc-gen-go 4 | - go mod tidy 5 | - go generate ./... 6 | builds: 7 | - id: "with-upx" 8 | env: 9 | - CGO_ENABLED=0 10 | goos: 11 | - linux 12 | - windows 13 | - darwin 14 | goarch: 15 | - amd64 16 | - arm64 17 | - arm 18 | - "386" 19 | goarm: 20 | - "6" 21 | - "7" 22 | flags: 23 | - -trimpath 24 | ldflags: 25 | - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X main.builtBy=goreleaser 26 | ignore: 27 | - goos: windows 28 | goarch: arm64 29 | - goos: windows 30 | goarch: arm 31 | - goos: linux 32 | goarch: mips64 33 | hooks: 34 | post: upx --best -f -q "{{ .Path }}" 35 | 36 | # UnknownExecutableFormatException 37 | # CantPackException: can't pack new-exe 38 | - id: "without-upx" 39 | env: 40 | - CGO_ENABLED=0 41 | goos: 42 | - linux 43 | - windows 44 | - darwin 45 | goarch: 46 | - mips64 47 | - arm 48 | goarm: 49 | - "6" 50 | - "7" 51 | flags: 52 | - -trimpath 53 | ldflags: 54 | - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X main.builtBy=goreleaser 55 | ignore: 56 | - goos: linux 57 | goarch: arm 58 | 59 | 60 | archives: 61 | - format: zip 62 | name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' 63 | checksum: 64 | name_template: 'checksums.txt' 65 | snapshot: 66 | name_template: "{{ incpatch .Version }}-next" 67 | changelog: 68 | sort: asc 69 | filters: 70 | exclude: 71 | - '^docs:' 72 | - '^test:' -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | "github.com/gookit/color" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | var specialPathStr = "drops_B0503373BDA6E3C5CD4E5118C02ED13A" 16 | var specialString = "drops_log" 17 | var fileMD5Dict = make(map[string]string) 18 | var originFileList []string 19 | 20 | func calcMD5(filePath string) string { 21 | data, err := os.ReadFile(filePath) 22 | if err != nil { 23 | fmt.Println("[*] 文件被删除 :", filePath) 24 | return "" 25 | } 26 | hash := md5.Sum(data) 27 | return hex.EncodeToString(hash[:]) 28 | } 29 | 30 | func getFilesList(root string) []string { 31 | var files []string 32 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 33 | if err != nil { 34 | return err 35 | } 36 | if !info.IsDir() && !strings.Contains(path, specialPathStr) { 37 | files = append(files, path) 38 | } 39 | return nil 40 | }) 41 | if err != nil { 42 | fmt.Println("[*] 错误:", err) 43 | } 44 | return files 45 | } 46 | 47 | func backupDirectory(srcDir string) error { 48 | // 创建备份目录 49 | backupDir := filepath.Join(srcDir, "backup") 50 | if err := os.MkdirAll(backupDir, 0755); err != nil { 51 | return err 52 | } 53 | 54 | // 读取源目录下的所有文件 55 | files, err := os.ReadDir(srcDir) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | for _, file := range files { 61 | if !file.IsDir() { 62 | srcFile := filepath.Join(srcDir, file.Name()) 63 | dstFile := filepath.Join(backupDir, file.Name()) 64 | 65 | if err := copyFile(srcFile, dstFile); err != nil { 66 | return err 67 | } 68 | } 69 | } 70 | 71 | return nil 72 | } 73 | 74 | func copyFile(src, dst string) error { 75 | srcFile, err := os.Open(src) 76 | if err != nil { 77 | return err 78 | } 79 | defer srcFile.Close() 80 | 81 | dstFile, err := os.Create(dst) 82 | if err != nil { 83 | return err 84 | } 85 | defer dstFile.Close() 86 | 87 | _, err = io.Copy(dstFile, srcFile) 88 | return err 89 | } 90 | 91 | func replaceFileContent(targetFilePath, sourceFilePath string) error { 92 | sourceFile, err := os.Open(sourceFilePath) 93 | if err != nil { 94 | return err 95 | } 96 | defer sourceFile.Close() 97 | targetFile, err := os.Create(targetFilePath) 98 | if err != nil { 99 | return err 100 | } 101 | defer targetFile.Close() 102 | _, err = io.Copy(targetFile, sourceFile) 103 | return err 104 | } 105 | 106 | func main() { 107 | fmt.Println("---------持续监测文件中------------") 108 | cwd, _ := os.Getwd() 109 | if err := backupDirectory(cwd); err != nil { 110 | fmt.Println("Error:", err) 111 | } else { 112 | fmt.Println("Backup completed successfully!") 113 | } 114 | originFileList = getFilesList(cwd) 115 | for _, filePath := range originFileList { 116 | fileMD5Dict[filePath] = calcMD5(filePath) 117 | } 118 | 119 | for { 120 | fileList := getFilesList(cwd) 121 | diffFileList := []string{} 122 | for _, file := range fileList { 123 | if _, ok := fileMD5Dict[file]; !ok { 124 | diffFileList = append(diffFileList, file) 125 | } 126 | } 127 | 128 | // 移除新上传文件 129 | if len(diffFileList) != 0 { 130 | for _, filePath := range diffFileList { 131 | data, err := os.ReadFile(filePath) 132 | if err != nil { 133 | break 134 | } 135 | content := string(data) 136 | if !strings.Contains(content, specialString) { 137 | fmt.Println("[*] 发现疑似WebShell上传文件:", filePath, "时间为:", time.Now(), "内容为:", content) 138 | // 自动删除新上传的文件 139 | err1 := os.Remove(filePath) 140 | if err1 != nil { 141 | color.Red.Printf("删除文件时出错:%s\n", err) 142 | } 143 | color.Green.Println("[+] 新上传的文件已删除") 144 | } 145 | } 146 | } 147 | 148 | // 防止任意文件被修改,还原被修改文件 149 | for _, filePath := range originFileList { 150 | newMD5 := calcMD5(filePath) 151 | if newMD5 != fileMD5Dict[filePath] { 152 | data, err1 := os.ReadFile(filePath) 153 | if err1 != nil { 154 | break 155 | } 156 | content := string(data) 157 | if !strings.Contains(content, specialString) { 158 | fmt.Println("[*] 该文件被修改 :", filePath, "时间为:", time.Now(), "内容为:", content) 159 | // 自动还原被删除的文件 160 | dir, file := filepath.Split(filePath) 161 | backupFilePath := filepath.Join(dir, "backup", file) 162 | replaceFileContent(filePath, backupFilePath) 163 | color.Green.Println("[+] 被修改的文件已还原") 164 | } 165 | } 166 | } 167 | 168 | time.Sleep(5 * time.Second) 169 | } 170 | } 171 | --------------------------------------------------------------------------------