Skip to content

Commit fcced1f

Browse files
committed
Add memory scanning capabilities and related event forwarding functions
1 parent 1495890 commit fcced1f

File tree

7 files changed

+125
-8
lines changed

7 files changed

+125
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ options:
148148
findInRemovableDrives: true # enumerate removable drive content
149149
findInNetworkDrives: true # enumerate network drive content
150150
findInCDRomDrives: true # enumerate physical CD-ROM and mounted iso / vhd...
151+
findInMemory: true # check for results in processes memory
151152
output:
152153
copyMatchingFiles: true # create a copy of every matching file
153154
base64Files: true # base64 matched content before copy

configuration.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type Options struct {
3939
FindInRemovableDrives bool `yaml:"findInRemovableDrives"`
4040
FindInNetworkDrives bool `yaml:"findInNetworkDrives"`
4141
FindInCDRomDrives bool `yaml:"findInCDRomDrives"`
42-
ScanMemory bool `yaml:"scanMemory"`
42+
FindInMemory bool `yaml:"findInMemory"`
4343
}
4444

4545
type Output struct {

event_forwarding.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,30 @@ func ForwardAlertEvent(ruleName, filePath string, fileSize int64, fileHash strin
192192
ForwardEvent("alert", "high", fmt.Sprintf("YARA rule match: %s in %s", ruleName, filePath), metadata)
193193
}
194194

195+
// ForwardGrepMatchEvent forwards a Grep match event
196+
func ForwardGrepMatchEvent(pattern, filePath string, fileSize int64, metadata map[string]string) {
197+
if metadata == nil {
198+
metadata = make(map[string]string)
199+
}
200+
metadata["grep_pattern"] = pattern
201+
metadata["file_path"] = filePath
202+
metadata["file_size"] = fmt.Sprintf("%d", fileSize)
203+
204+
ForwardEvent("alert", "high", fmt.Sprintf("Grep match: %s in %s", pattern, filePath), metadata)
205+
}
206+
207+
// ForwardChecksumMatchEvent forwards a Checksum match event
208+
func ForwardChecksumMatchEvent(checksum, filePath string, fileSize int64, metadata map[string]string) {
209+
if metadata == nil {
210+
metadata = make(map[string]string)
211+
}
212+
metadata["checksum"] = checksum
213+
metadata["file_path"] = filePath
214+
metadata["file_size"] = fmt.Sprintf("%d", fileSize)
215+
216+
ForwardEvent("alert", "high", fmt.Sprintf("Checksum match: %s in %s", checksum, filePath), metadata)
217+
}
218+
195219
// ForwardScanCompleteEvent forwards scan completion statistics
196220
func ForwardScanCompleteEvent(filesScanned, matchesFound, errorsEncountered int, duration time.Duration) {
197221
if eventForwarder == nil {

finder.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ func checkForChecksum(path string, content []byte, hashList []string) (matchingF
174174
for _, c := range hashs {
175175
if Contains(hashList, c) && !Contains(matchingFiles, path) {
176176
LogMessage(LOG_ALERT, "(ALERT)", "Checksum match:", c, "in", path)
177+
ForwardChecksumMatchEvent(c, path, int64(len(content)), nil)
177178
matchingFiles = append(matchingFiles, path)
178179
}
179180
}
@@ -190,7 +191,13 @@ func checkForStringPattern(path string, content []byte, patterns []string) (matc
190191
for _, expression := range patterns {
191192
for i, line := range lines {
192193
if strings.Contains(line, expression) {
193-
LogMessage(LOG_ALERT, "(ALERT)", "Grep match:", expression, "in", path, "at line", fmt.Sprintf("%d:", i+1), strings.TrimSpace(line))
194+
LogMessage(LOG_ALERT, "(ALERT)", "Grep match:", expression, "in", path, "at line", fmt.Sprintf("%d", i+1))
195+
196+
metadata := map[string]string{
197+
"line_number": fmt.Sprintf("%d", i+1),
198+
}
199+
ForwardGrepMatchEvent(expression, path, int64(len(content)), metadata)
200+
194201
matchingFiles = append(matchingFiles, path)
195202
break
196203
}

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func MainFastfinderRoutine(config Configuration, pConfigPath string, pNoAdvUI bo
150150
}
151151

152152
// Memory Scan
153-
if config.Options.ScanMemory {
153+
if config.Options.FindInMemory {
154154
ScanMemory(config, rules)
155155
}
156156

process_scanner.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,31 @@ func ScanMemory(config Configuration, rules *yara.Rules) {
2222
procs := ListProcesses()
2323
LogMessage(LOG_INFO, "(INFO)", fmt.Sprintf("Found %d running processes", len(procs)))
2424

25+
ScanProcesses(procs, config, rules)
26+
27+
LogMessage(LOG_INFO, "(INFO)", "Memory scan finished")
28+
}
29+
30+
// ScanProcesses scans a list of processes for patterns and YARA rules
31+
func ScanProcesses(procs []ProcessInformation, config Configuration, rules *yara.Rules) int {
32+
totalMatches := 0
2533
for _, proc := range procs {
2634
LogMessage(LOG_VERBOSE, "(MEMORY)", "Scanning process:", proc.ProcessName, fmt.Sprintf("(PID: %d)", proc.PID))
2735

2836
// Check memory content with Grep
2937
if len(config.Input.Content.Grep) > 0 {
38+
// checkForStringPattern already handles logging and alerting
3039
matches := checkForStringPattern(fmt.Sprintf("MEMORY:%s:%d", proc.ProcessName, proc.PID), proc.MemoryDump, config.Input.Content.Grep)
31-
for _, m := range matches {
32-
LogMessage(LOG_ALERT, "(ALERT)", "Memory Grep match:", m)
33-
}
40+
totalMatches += len(matches)
3441
}
3542

3643
// Check memory content with YARA
3744
if rules != nil && len(rules.GetRules()) > 0 {
3845
matchs, err := PerformYaraScan(&proc.MemoryDump, rules)
3946
if err != nil {
4047
LogMessage(LOG_ERROR, "(ERROR)", "Memory YARA scan failed for", proc.ProcessName, err)
48+
} else {
49+
totalMatches += len(matchs)
4150
}
4251

4352
for i := 0; i < len(matchs); i++ {
@@ -61,6 +70,5 @@ func ScanMemory(config Configuration, rules *yara.Rules) {
6170
proc.MemoryDump = nil
6271
debug.FreeOSMemory()
6372
}
64-
65-
LogMessage(LOG_INFO, "(INFO)", "Memory scan finished")
73+
return totalMatches
6674
}

process_scanner_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hillu/go-yara/v4"
7+
)
8+
9+
func TestScanProcessesGrep(t *testing.T) {
10+
// Setup
11+
config := Configuration{}
12+
config.Input.Content.Grep = []string{"bad_string"}
13+
14+
proc := ProcessInformation{
15+
PID: 1234,
16+
ProcessName: "test_process.exe",
17+
MemoryDump: []byte("This is a memory dump containing a bad_string here."),
18+
}
19+
procs := []ProcessInformation{proc}
20+
21+
// Execute
22+
matches := ScanProcesses(procs, config, nil)
23+
24+
// Verify
25+
if matches != 1 {
26+
t.Errorf("Expected 1 match, got %d", matches)
27+
}
28+
}
29+
30+
func TestScanProcessesNoMatch(t *testing.T) {
31+
// Setup
32+
config := Configuration{}
33+
config.Input.Content.Grep = []string{"missing_string"}
34+
35+
proc := ProcessInformation{
36+
PID: 1234,
37+
ProcessName: "test_process.exe",
38+
MemoryDump: []byte("This is a memory dump containing a bad_string here."),
39+
}
40+
procs := []ProcessInformation{proc}
41+
42+
// Execute
43+
matches := ScanProcesses(procs, config, nil)
44+
45+
// Verify
46+
if matches != 0 {
47+
t.Errorf("Expected 0 matches, got %d", matches)
48+
}
49+
}
50+
51+
func TestScanProcessesYara(t *testing.T) {
52+
c, err := yara.NewCompiler()
53+
if err != nil {
54+
t.Skip("YARA compiler not available: ", err)
55+
return
56+
}
57+
err = c.AddString(`rule test { strings: $a = "yara_match" condition: $a }`, "test")
58+
if err != nil {
59+
t.Fatal("Failed to compile yara rule:", err)
60+
}
61+
rules, err := c.GetRules()
62+
if err != nil {
63+
t.Fatal("Failed to get rules:", err)
64+
}
65+
66+
config := Configuration{}
67+
proc := ProcessInformation{
68+
PID: 1234,
69+
ProcessName: "test_process.exe",
70+
MemoryDump: []byte("Before yara_match After"),
71+
}
72+
73+
matches := ScanProcesses([]ProcessInformation{proc}, config, rules)
74+
if matches != 1 {
75+
t.Errorf("Expected 1 YARA match, got %d", matches)
76+
}
77+
}

0 commit comments

Comments
 (0)