// FileEventMonitor.cs // https://github.com/gnh1201/welsonjs using System; using System.Diagnostics.Eventing.Reader; using System.IO; using libyaraNET; using System.Collections.Generic; using System.ServiceProcess; using WelsonJS.Service.Model; namespace WelsonJS.Service { public class FileEventMonitor { private Rules rules; private EventLogWatcher eventLogWatcher; private ServiceMain parent; private string ruleDirectoryPath; public FileEventMonitor(ServiceBase parent, string workingDirectory) { this.parent = (ServiceMain)parent; ruleDirectoryPath = Path.Combine(workingDirectory, "app/assets/yar"); try { AddYaraRules(new List(Directory.GetFiles(ruleDirectoryPath, "*.yar"))); } catch (Exception ex) { this.parent.Log($"Exception (FileEventMonitor): {ex.Message}"); } } public void AddYaraRulesFromDirectory(string directoryPath) { if (!Directory.Exists(directoryPath)) { Console.WriteLine($"Directory not found: {directoryPath}"); return; } var yarFiles = Directory.GetFiles(directoryPath, "*.yar"); AddYaraRules(new List(yarFiles)); } public void AddYaraRules(List ruleFiles) { Dispose(); using (var ctx = new YaraContext()) { try { using (var compiler = new Compiler()) { foreach (var ruleFile in ruleFiles) { if (File.Exists(ruleFile)) { compiler.AddRuleFile(ruleFile); parent.Log($"Loaded YARA rule from {ruleFile}"); } else { parent.Log($"File not found: {ruleFile}"); } } rules = compiler.GetRules(); } } catch (Exception ex) { parent.Log($"Error loading YARA rules: {ex.Message}"); } } } public void Start() { try { string query = @" "; EventLogQuery eventLogQuery = new EventLogQuery("Microsoft-Windows-Sysmon/Operational", PathType.LogName, query); eventLogWatcher = new EventLogWatcher(eventLogQuery); eventLogWatcher.EventRecordWritten += new EventHandler(OnEventRecordWritten); eventLogWatcher.Enabled = true; } catch (Exception ex) { parent.Log($"Exception (FileEventMonitor): {ex.Message}"); } } public void Stop() { if (eventLogWatcher != null) { try { eventLogWatcher.Dispose(); eventLogWatcher = null; } catch (Exception) { eventLogWatcher = null; } } Dispose(); } private void OnEventRecordWritten(object sender, EventRecordWrittenEventArgs e) { if (e.EventRecord != null) { try { string fileName = e.EventRecord.Properties[7]?.Value?.ToString(); if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) { parent.Log($"File created: {fileName}"); parent.DispatchServiceEvent("fileCreated", new string[] { fileName }); ScanFileWithYara(fileName); } } catch (Exception ex) { parent.Log($"Error processing event: {ex.Message}"); } } else { parent.Log("The event instance was null."); } } private void ScanFileWithYara(string filePath) { if (rules == null) { parent.Log("No YARA rules loaded. Skipping file scan."); return; } using (var ctx = new YaraContext()) { var scanner = new Scanner(); var results = scanner.ScanFile(filePath, rules); if (results.Count > 0) { parent.Log($"YARA match found in file {filePath}:"); foreach (var result in results) { Dictionary> matches = result.Matches; foreach (KeyValuePair> match in matches) { string ruleName = match.Key; List ruleMatches = match.Value; ruleMatches.ForEach((x) => { parent.Log($"YARA rule matched: {ruleName}, {filePath}, Offset {x.Offset}"); parent.DispatchServiceEvent("fileRuleMatched", new string[] { ruleName, filePath, x.Offset.ToString() }); IndexFileRuleMatched(new FileRuleMatched { FilePath = filePath, Offset = x.Offset, RuleName = ruleName, LastChecked = DateTime.Now }); }); } } } else { parent.Log($"No YARA match found in file {filePath}."); } } } private void IndexFileRuleMatched(FileRuleMatched match) { // todo (save result to the search engine) } private void Dispose() { if (rules != null) { try { //rules.Dispose(); rules = null; } catch (Exception) { rules = null; } } } } }