From 187978c35f12d696bdaf25d5c0c5e7c8d40793c8 Mon Sep 17 00:00:00 2001 From: Jean-Pierre GARNIER Date: Thu, 31 Dec 2020 12:01:14 +0100 Subject: [PATCH] code refactoring --- analysis.go | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ filehelper.go | 93 ---------------------------- main.go | 11 ++-- procsmemory.go | 45 +++----------- 4 files changed, 175 insertions(+), 134 deletions(-) create mode 100644 analysis.go diff --git a/analysis.go b/analysis.go new file mode 100644 index 0000000..bd179d9 --- /dev/null +++ b/analysis.go @@ -0,0 +1,160 @@ +package main + +import ( + "crypto/md5" + "crypto/rc4" + b64 "encoding/base64" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + + "github.com/hillu/go-yara" +) + +// FileAnalysis sub-routine for file analysis (used in registry / task scheduler / startmenu scan) +func FileAnalysis(path string, pQuarantine string, pKill bool, pAggressive bool, pNotifications bool, pVerbose bool, rules *yara.Rules, sourceIndex string) { + var err error + var content []byte + var result yara.MatchRules + + content, err = ioutil.ReadFile(path) + if err != nil && pVerbose { + log.Println(path, err) + } + + fileHash := fmt.Sprintf("%x", md5.Sum(content)) + if !StringInSlice(fileHash, filescanHistory) { + if pVerbose { + log.Println("[INFO] ["+sourceIndex+"] Analyzing", path) + } + + result = PerformYaraScan(content, rules, pVerbose) + + if len(result) > 0 { + // windows notifications + if pNotifications { + NotifyUser("YARA match", path+" match "+fmt.Sprint(len(result))+" rules") + } + + // logging + for _, match := range result { + log.Println("[ALERT]", "["+sourceIndex+"] YARA match", path, match.Namespace, match.Rule) + } + + // kill + if pKill { + killQueue = append(killQueue, path) + } + + // dump matching file to quarantine + if len(pQuarantine) > 0 { + log.Println("[INFO]", "Dumping file", path) + err := QuarantineFile(filepath.Base(path), pQuarantine) + if err != nil { + log.Println("[ERROR]", "Cannot quarantine file", path, err) + } + } + } else { + filescanHistory = append(filescanHistory, fileHash) + } + } +} + +// MemoryAnalysis sub-routine for running processes analysis +func MemoryAnalysis(proc ProcessInformation, pQuarantine string, pKill bool, pAggressive bool, pNotifications bool, pVerbose bool, rules *yara.Rules) { + memoryHash := fmt.Sprintf("%x", md5.Sum(proc.ProcessMemory)) + + // if hash isn't already whitelisted, yara scan it + if !StringInSlice(memoryHash, memoryscanHistory) { + if pVerbose { + log.Println("[INFO] [MEMORY] Analyzing", proc.ProcessName, "PID:", proc.PID) + } + + result := PerformYaraScan(proc.ProcessMemory, rules, pVerbose) + if len(result) > 0 { + // windows notifications + if pNotifications { + NotifyUser("YARA match", proc.ProcessName+" - PID:"+fmt.Sprint(proc.PID)+" match "+fmt.Sprint(len(result))+" rules") + } + + // logging + for _, match := range result { + log.Println("[ALERT]", "[MEMORY] YARA match", proc.ProcessName, "PID:", fmt.Sprint(proc.PID), match.Namespace, match.Rule) + } + + // dump matching process to quarantine + if len(pQuarantine) > 0 { + log.Println("[INFO]", "DUMPING PID", proc.PID) + err := QuarantineProcess(proc, pQuarantine) + if err != nil { + log.Println("[ERROR]", "Cannot quarantine PID", proc.PID, err) + } + } + + // killing process + if pKill { + log.Println("[INFO]", "KILLING PID", proc.PID) + KillProcessByID(proc.PID, pVerbose) + } + } else { + memoryscanHistory = append(memoryscanHistory, memoryHash) + } + } +} + +// QuarantineProcess dump process memory and cipher them in quarantine folder +func QuarantineProcess(proc ProcessInformation, quarantinePath string) (err error) { + + err = quarantineContent(proc.ProcessMemory, proc.ProcessName+fmt.Sprint(proc.PID)+".mem", quarantinePath) + if err != nil { + return err + } + + err = QuarantineFile(proc.ProcessPath, quarantinePath) + if err != nil { + return err + } + + return nil +} + +// QuarantineFile dump specified file and cipher them in quarantine folder +func QuarantineFile(path, quarantinePath string) (err error) { + fileContent, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + err = quarantineContent(fileContent, filepath.Base(path), quarantinePath) + if err != nil { + return err + } + + return nil +} + +// quarantineContent copy and encrypt suspicious content +func quarantineContent(content []byte, filename string, quarantinePath string) (err error) { + _, err = os.Stat(quarantinePath) + if os.IsNotExist(err) { + if err := os.MkdirAll(quarantinePath, 0600); err != nil { + return err + } + } + + c, err := rc4.NewCipher([]byte("irma")) + if err != nil { + return err + } + + xPE := make([]byte, len(content)) + c.XORKeyStream(xPE, content) + err = ioutil.WriteFile(quarantinePath+"/"+filename+".irma", []byte(b64.StdEncoding.EncodeToString(xPE)), 0644) + if err != nil { + return err + } + + return nil +} diff --git a/filehelper.go b/filehelper.go index 094c1f7..f34675e 100644 --- a/filehelper.go +++ b/filehelper.go @@ -1,11 +1,7 @@ package main import ( - "crypto/md5" - "crypto/rc4" - b64 "encoding/base64" "errors" - "fmt" "io/ioutil" "log" "os" @@ -60,50 +56,6 @@ func ListUserWorkspaceFiles(verbose bool) (files []string) { return files } -// FileAnalysis sub-routine for file analysis (used in registry / task scheduler / startmenu scan) -func FileAnalysis(path string, pQuarantine string, pKill bool, pAggressive bool, pNotifications bool, pVerbose bool, rules *yara.Rules, sourceIndex string) { - var err error - var content []byte - var result yara.MatchRules - - content, err = ioutil.ReadFile(path) - if err != nil && pVerbose { - log.Println(path, err) - } - - fileHash := fmt.Sprintf("%x", md5.Sum(content)) - if !StringInSlice(fileHash, filescanHistory) { - if pVerbose { - log.Println("[INFO] ["+sourceIndex+"] Analyzing", path) - } - - result, err = YaraScan(content, rules) - if len(result) > 0 { - // windows notifications - if pNotifications { - NotifyUser("YARA match", path+" match "+fmt.Sprint(len(result))+" rules") - } - - // logging - for _, match := range result { - log.Println("[ALERT]", "YARA match", path, match.Namespace, match.Rule) - } - - // dump matching process to quarantine - if len(pQuarantine) > 0 { - log.Println("[INFO]", "DUMPING FILE", path) - err := QuarantineFile(content, filepath.Base(path), pQuarantine) - if err != nil { - log.Println("[ERROR]", "Cannot quarantine file", path, err) - } - } - } - - filescanHistory = append(filescanHistory, fileHash) - } - -} - // ListEnvironmentPathFiles list all files in PATH directories func ListEnvironmentPathFiles(verbose bool) (files []string) { env := os.Getenv("PATH") @@ -232,48 +184,3 @@ func RetrivesFilesFromUserPath(path string, listFiles bool, includeFileExtension return p, nil } - -// QuarantineFile copy and encrypt suspicious file -func QuarantineFile(content []byte, filename string, quarantinePath string) (err error) { - _, err = os.Stat(quarantinePath) - if os.IsNotExist(err) { - if err := os.MkdirAll(quarantinePath, 0600); err != nil { - return err - } - } - - c, err := rc4.NewCipher([]byte("irma")) - if err != nil { - return err - } - - xPE := make([]byte, len(content)) - c.XORKeyStream(xPE, content) - err = ioutil.WriteFile(quarantinePath+"/"+filename+".irma", []byte(b64.StdEncoding.EncodeToString(xPE)), 0644) - if err != nil { - return err - } - - return nil -} - -// QuarantineProcess dump process executable and memory and cipher them in quarantine folder -func QuarantineProcess(proc ProcessInformation, quarantinePath string) (err error) { - - err = QuarantineFile(proc.ProcessMemory, proc.ProcessName+fmt.Sprint(proc.PID)+".mem", quarantinePath) - if err != nil { - return err - } - - procPEContent, err := ioutil.ReadFile(proc.ProcessPath) - if err != nil { - return err - } - - err = QuarantineFile(procPEContent, proc.ProcessName+fmt.Sprint(proc.PID)+".pe", proc.ProcessPath) - if err != nil { - return err - } - - return nil -} diff --git a/main.go b/main.go index 93a0934..b0b11ed 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ var ( notificationsHistory []string filescanHistory []string memoryscanHistory []string + killQueue []string ) var defaultScannedFileExtensions = []string{".txt", ".csv", ".htm", ".html", ".flv", ".f4v", ".avi", ".3gp", ".3g2", ".3gp2", ".3p2", ".divx", ".mp4", ".mkv", ".mov", ".qt", ".asf", ".wmv", ".rm", ".rmvb", ".vob", ".dat", ".mpg", ".mpeg", ".bik", ".fcs", ".mp3", ".mpeg3", ".flac", ".ape", ".ogg", ".aac", ".m4a", ".wma", ".ac3", ".wav", ".mka", ".rm", ".ra", ".ravb", ".mid", ".midi", ".cda", ".jpg", ".jpe", ".jpeg", ".jff", ".gif", ".png", ".bmp", ".tif", ".tiff", ".emf", ".wmf", ".eps", ".psd", ".cdr", ".swf", ".exe", ".lnk", ".dll", ".ps1", ".scr", ".ocx", ".com", ".sys", ".class", ".o", ".so", ".elf", ".prx", ".vb", ".vbs", ".js", ".bat", ".cmd", ".msi", ".msp", ".deb", ".rpm", ".sh", ".pl", ".dylib", ".doc", ".dot", ".docx", ".dotx", ".docm", ".dotm", ".xsl", ".xls", ".xlsx", ".xltx", ".xlsm", ".xltm", ".xlam", ".xlsb", ".ppt", ".pot", ".pps", ".pptx", ".potx", ".pptm", ".potm", ".ppsx", ".ppsm", ".rtf", ".pdf", ".msg", ".eml", ".vsd", ".vss", ".vst", ".vdx", ".vsx", ".vtx", ".xps", ".oxps", ".one", ".onepkg", ".xsn", ".odt", ".ods", ".odp", ".sxw", ".pub", ".mdb", ".accdb", ".accde", ".accdr", ".accdc", ".chm", ".mht", ".zip", ".7z", ".7-z", ".rar", ".iso", ".cab", ".jar", ".bz", ".bz2", ".tbz", ".tbz2", ".gz", ".tgz", ".arj", ".dmg", ".smi", ".img", ".xar"} @@ -68,11 +69,11 @@ func main() { log.Println("[INIT]", len(rules.GetRules()), "YARA rules compiled") log.Println("[INFO] Start scanning Memory / Registry / StartMenu / Task Scheduler / Filesystem") go MemoryAnalysisRoutine(*pDump, *pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) - go RegistryAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) - go StartMenuAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) - go TaskSchedulerAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) - go WindowsFileSystemAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) - go UserFileSystemAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) + //go RegistryAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) + //go StartMenuAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) + //go TaskSchedulerAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) + //go WindowsFileSystemAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) + //go UserFileSystemAnalysisRoutine(*pQuarantine, *pKill, *pAggressive, *pNotifications, *pVerbose, rules) for true { time.Sleep(3600 * time.Second) diff --git a/procsmemory.go b/procsmemory.go index b403f55..5dba289 100644 --- a/procsmemory.go +++ b/procsmemory.go @@ -41,45 +41,18 @@ func MemoryAnalysisRoutine(pDump string, pQuarantine string, pKill bool, pAggres // analyze process memory and executable for _, proc := range procs { - result := PerformYaraScan(proc.ProcessMemory, rules, pVerbose) - if len(result) == 0 { - procPE, err := ioutil.ReadFile(proc.ProcessPath) - if err != nil && pVerbose { - log.Println("[ERROR]", err) - } - result = PerformYaraScan(procPE, rules, pVerbose) - } - - if len(result) > 0 { - // windows notifications - if pNotifications { - NotifyUser("YARA match", proc.ProcessName+" - PID:"+fmt.Sprint(proc.PID)+" match "+fmt.Sprint(len(result))+" rules") - } - - // logging - for _, match := range result { - log.Println("[ALERT]", "YARA match", proc.ProcessName, "PID:", fmt.Sprint(proc.PID), match.Namespace, match.Rule) - } - - // dump matching process to quarantine - if len(pQuarantine) > 0 { - log.Println("[INFO]", "DUMPING PID", proc.PID) - err := QuarantineProcess(proc, pQuarantine) - if err != nil { - log.Println("[ERROR]", "Cannot quarantine PID", proc.PID, err) - } - } - - // killing process - if pKill { - if pVerbose { - log.Println("[INFO]", "KILLING PID", proc.PID) - } - KillProcessByID(proc.PID, pVerbose) - } + // parsing kill queue + if StringInSlice(proc.ProcessPath, killQueue) && pKill { + log.Println("[INFO]", "KILLING PID", proc.PID) + KillProcessByID(proc.PID, pVerbose) + } else { + MemoryAnalysis(proc, pQuarantine, pKill, pAggressive, pNotifications, pVerbose, rules) + FileAnalysis(proc.ProcessPath, pQuarantine, pKill, pAggressive, pNotifications, pVerbose, rules, "MEMORY") } } + killQueue = nil + time.Sleep(5 * time.Second) } }