安全開發Go 學習:馭龍 HIDS 源碼學習(一)

 

馭龍HIDS是一款由 YSRC 開源的入侵檢測系統,由 Agent, Daemon, Server 和 Web 四個部分組成,集異常檢測、監控管理爲一體,擁有異常行爲發現、快速阻斷、高級分析等功能,可從多個維度行爲信息中發現入侵行爲。

分析源碼首先了解代碼結構即目錄結構,瞭解各個目錄文件下的具體功能

GitHub 上已經給出相關文件的具體功能了。

首先分析agent功能,爲什麼要先分析agent呢,因爲最近自己在寫一個agent 的功能,先從它下手。

 

從入口文件agnet/anget.go 文件看

if len(os.Args) <= 1 {
		fmt.Println("Usage: agent[.exe] ServerIP [debug]")
		fmt.Println("Example: agent 8.8.8.8 debug")
		return
	}

開頭這段代碼主要功能是判斷 在運行agent文件中 是否輸入參數,未輸入參數時提示輸入信息並退出程序。

正常啓動命令是:./agent.ext 192.168.3.2 debug

os.Args 功能:

繼續往下看

if runtime.GOOS == "linux" {
		out, _ := common.CmdExec(fmt.Sprintf("lsmod|grep syshook_execve"))
		if out == "" {
			common.CmdExec(fmt.Sprintf("insmod %s/syshook_execve.ko", common.InstallPath))
		}
	}
runtime.GOOS 功能

該方法的功能主要是返回當前程序執行環境所在的操作系統。

這段代碼主要功能:

如果agent在linux系統上運行時:

執行一段linux命令

lsmod|grep syshook_execve

主要用來查看syshook_execve這個模塊是否被載入了這個系統。syshook_execve 模塊後期再繼續深入研究。

如果查不到linux系統中如果沒有載入這個模塊,就使用命令載入。

var agent client.Agent
	agent.ServerNetLoc = os.Args[1]
	if len(os.Args) == 3 && os.Args[2] == "debug" {
		log.Println("DEBUG MODE")
		agent.IsDebug = true
	}
	agent.Run()

分析上面這段代碼

首先定義變量 類型爲  client.Agent,查看agent/client/agent.go 文件中Agent 結構

繼續分析 主文件代碼 

agent.ServerNetLoc = os.Args[1]

os.Args[1] 存儲的是獲取的啓動命令:./agent.exe 192.168.192.13 debug 的ip地址。

後面if代碼判斷第三個參數 是否是debug,猜測應該是做是否開啓Debug模式標誌使用。

agent.Run() 執行Run方法,在agent/client/agent.go文件

// Run 啓動agent
func (a *Agent) Run() {

	// agent 初始化
	// 請求Web API,獲取Server地址,初始化RPC客戶端,獲取客戶端IP等
	a.init()

	// 每隔一段時間更新初始化配置
	a.configRefresh()
	
	// 開啓各個監控流程 文件監控,網絡監控,進程監控
	a.monitor()
	
	// 每隔一段時間獲取系統信息
	// 監聽端口,服務信息,用戶信息,開機啓動項,計劃任務,登錄信息,進程列表等
	a.getInfo()
}

Run方法中依次執行了4個方法

首先看init() 主要功能是初始化

Run是Agent 結構的方法。

init() 源碼

func (a *Agent) init() {
	a.ServerList, err = a.getServerList()
	if err != nil {
		a.log("GetServerList error:", err)
		panic(1)
	}
	a.ctx = context.WithValue(context.Background(), share.ReqMetaDataKey, make(map[string]string))
	a.log("Available server node:", a.ServerList)
	if len(a.ServerList) == 0 {
		time.Sleep(time.Second * 30)
		a.log("No server node available")
		panic(1)
	}
	a.newClient()
	if common.LocalIP == "" {
		a.log("Can not get local address")
		panic(1)
	}
	a.Mutex = new(sync.Mutex)
	err := a.Client.Call(a.ctx, "GetInfo", &common.ServerInfo, &common.Config)
	if err != nil {
		a.log("RPC Client Call Error:", err.Error())
		panic(1)
	}
	a.log("Common Client Config:", common.Config)
}

a.ServerList, err = a.getServerList()

調用a.getServerList() 方法,將返回值結果賦值給a.ServerList 。a是Agent結構。

a.ServerList表示存活服務端集羣列表。(馭龍應該是支持分佈式部署的,同時擁有多個服務接受端。防止大量agent併發的性能問題)

分析 a.getServerList() 方法

func (a Agent) getServerList() ([]string, error) {
	var serlist []string
	var url string
	if TESTMODE {
		url = "http://" + a.ServerNetLoc + SERVER_API
	} else {
		url = "https://" + a.ServerNetLoc + SERVER_API
	}
	a.log("Web API:", url)
	request, _ := http.NewRequest("GET", url, nil)
	request.Close = true
	resp, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	result, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal([]byte(result), &serlist)
	if err != nil {
		return nil, err
	}
	return serlist, nil
}

該方法 不需要傳入參數,返回的是字符串型的切片。

首先聲明瞭一個 名爲 serlist的字符串型切片,其次定義了一個名爲url 的字符串。

判斷TESTMODE ,TESTMODE 爲常量。在agent/client/config.go中

bool類型,當前爲false。

如果爲false

url變量等於https+a.ServerNetloc+SERVER_API 

a.ServerNetloc 上面已經分析過了,爲啓動命令的第一個參數,屬於IP地址。當我們輸入./agent.exe 127.0.01 debug 命令時

a.ServerNetloc = 127.0.0.1。SERVER_API 爲常量存儲在config.go 文件中,這段代碼執行下來結果爲:

url 變量 等於 https://127.0.0.1/json/serverlist

該判斷代碼塊主要作用是如果TESTMODE 爲true 服務端地址爲http,false爲https。

a.log("Web API:", url)

查看a.log代碼

如果啓動命令輸入了DEBUG 則。調用log.Println()函數並寫入數據

該功能屬於日誌記錄功能。運行後在控制檯輸出:

Web API: https://127.0.0.1/json/serverlist

主要功能顯示當前接口地址。

繼續分析剩下代碼邏輯,主要是請求https://127.0.0.1/json/serverlist 接口地址。

 

json/serverlist 接口在web目錄下,暫不做分析。

 大致內容就是請求該接口返回一段json 字符串。

繼續回到a.init()方法

未完待續。。。。。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章