Go+Bark实现服务器进程实时推送到手机

Go+Bark实现服务器进程监控推送

0x00 基本思路

不断扫描进程,识别到有新增进程则立即推送相关信息,包括进程名、进程ID、如有命令则显示命令行、父进程追溯等(目前只通过Linux下的ps命令实现)。

0x01 Bark服务部署

项目地址:https://github.com/Finb/bark-server

部署文档:https://day.app/2018/06/bark-server-document/

除了Bark还可以使用其他推送服务,比如钉钉机器人等。

0x02 Go语言进程监控

这里使用了第三方库go-ps:https://github.com/mitchellh/go-ps

0x03 实现

首先在服务器上运行Bark服务,手机上安装Bark客户端,在客户端上添加Bark服务的URL,获得一串带KEYURL,可以从客户端复制下来,稍后会用到。

没做过开发的业余Go代码如下:

package main

import (
	ps "github.com/mitchellh/go-ps"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
)

type psMap struct {
	ParentPid      int
	ExecutableName string
}

func main() {
	var oldProcessList map[int]psMap
	var isFirstRun bool = true
	ch := make(chan []ps.Process, 1)
	processList, err := ps.Processes()
	if err != nil {
		log.Fatalln(err)
	}
	go func() {
		for {
			tmpProcessList, err := ps.Processes()
			if err != nil {
				log.Fatalln(err)
			}
			ch <- tmpProcessList
		}
	}()
	go func() {
		for {
			for _, v := range <- ch {
				processList = append(processList, v)
			}
			time.Sleep(100 * time.Millisecond)  //检测新进程的时间间隔
		}
	}()

	for {
		time.Sleep(time.Second)
		ch <- processList
		processList = unique(processList)
		var newProcessList map[int]psMap
		newProcessList = make(map[int]psMap, len(processList))
		for _, v := range processList {
			var newPsMap psMap
			newPsMap.ParentPid = v.PPid()
			newPsMap.ExecutableName = v.Executable()
			newProcessList[v.Pid()] = newPsMap
		}
		if isFirstRun {
			oldProcessList = newProcessList
			isFirstRun = false
		} else {
			var isNew bool = false
			var diffLists []int
			for i, _ := range newProcessList {
				if newProcessList[i] != oldProcessList[i] {
					if runtime.GOOS == "linux" && newProcessList[i].ExecutableName != "xxx" { //Linux下需要忽略的进程名
						if isNew == false {
							isNew = true
						}
						diffLists = append(diffLists, i)
						continue
					}
					if runtime.GOOS == "windows" && newProcessList[i].ExecutableName != "xxx" { //Windows下需要忽略的进程名
						if isNew == false {
							isNew = true
						}
						diffLists = append(diffLists, i)
						continue
					}
					if runtime.GOOS != "linux" && runtime.GOOS != "windows" {
						if isNew == false {
							isNew = true
						}
						diffLists = append(diffLists, i)
					}
				}
			}
			if len(diffLists) > 0 {
				sort.Ints(diffLists)
				var noneLittleChildList []int
				for i := len(diffLists) - 1; i >= 0 && !isExists(noneLittleChildList, diffLists[i]); i-- {
					messageText := "[" + strconv.Itoa(diffLists[i]) + "] " + newProcessList[diffLists[i]].ExecutableName
					currPpid := newProcessList[diffLists[i]].ParentPid
					for currPpid >= 1 {
						messageText = "[" + strconv.Itoa(currPpid) + "] " + newProcessList[currPpid].ExecutableName + " -> " + messageText
						noneLittleChildList = append(noneLittleChildList, currPpid)
						currPpid = newProcessList[currPpid].ParentPid
					}
					if len(messageText) > 0 {
						if runtime.GOOS == "linux" {
							grepCurrProcess, err := exec.Command("ps", "h", "-eo", "user,group,stime,tty,pcpu,%mem,args", "-q", strconv.Itoa(diffLists[i])).CombinedOutput()  //使用ps命令获取当前进程的用户、用户组、开始时间、tty、cpu使用率、内存使用率、参数
							if err != nil {
								log.Println("ps cmd error:", i, err, string(grepCurrProcess))
							} else {
								grepCurrProcessString := strings.TrimSpace(strings.Replace(string(grepCurrProcess), "\n", " ", -1))
								for strings.Contains(grepCurrProcessString, "  ") {
									grepCurrProcessString = strings.Replace(grepCurrProcessString, "  ", " ", -1)
								}
								messageText = messageText + " {" + grepCurrProcessString + "}"
							}
						}
						if len(os.Args) == 2 {
							sendPush(messageText)
						}
						log.Println(messageText)
					}
				}
			}
			if isNew {
				oldProcessList = newProcessList
				isNew = false
			}
		}
		processList = nil
		<- ch
	}
}

func sendPush(body string) {
	form := url.Values{}
	form.Add("sound", "shake")  //设置消息铃声
	form.Add("group", os.Args[1]+"主机进程监控")  //设置分组
	form.Add("title", os.Args[1]+" 进程监控")  //设置标题
	form.Add("body", body)  //推送内容
	client := &http.Client{}
	req, err := http.NewRequest("POST", 客户端添加Bark服务URL后的得到的那串带KEY的URL, strings.NewReader(form.Encode()))
	if err != nil {
		log.Println("Failure :", err)
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	resp, err := client.Do(req)
	if err != nil {
		log.Println("Failure :", err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	log.Println("response Status :", resp.Status)
	log.Println("response Headers :", resp.Header)
	log.Println("response Body :", string(respBody))
}

func isExists(arr []int, currItem int) bool {
	for _, v := range arr {
		if v == currItem {
			return true
		}
	}
	return false
}

func unique(arr []ps.Process) []ps.Process {
	occured := map[ps.Process]bool{}
	result := []ps.Process{}
	for e := range arr {
		if occured[arr[e]] != true {
			occured[arr[e]] = true
			result = append(result, arr[e])
		}
	}
	return result
}

编译完以后运行:

./main [自定义名称,比如服务器名字]

0x04 遇到的问题

这些问题还请请哥哥姐姐们指点指点~

  1. 不管扫描间隔时间多小,总会有一些立马执行完的命令(如ls)会逃过检测

  2. windows下如何通过cmd或者powershell获取指定进程的指定参数(我没有去查,也不太想写)

0x05 实际效果

很烦人,特别是有人在爆破我的ssh时,就算有登录失败处理策略,但每次ssh连接的时候就会产生新进程,在凌晨时候手机跟疯狗一样…
Go+Bark实现服务器进程实时推送到手机_第1张图片

不过Bark是不错的,也可以结合其他工具使用,强烈推荐。

你可能感兴趣的:(go语言,网络安全)