2024-08-12 03:47:19 +00:00
|
|
|
|
// FileEventMonitor.cs
|
2024-08-26 16:24:11 +00:00
|
|
|
|
// Namhyeon Go <abuse@catswords.net>
|
2024-08-12 03:47:19 +00:00
|
|
|
|
// https://github.com/gnh1201/welsonjs
|
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics.Eventing.Reader;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using libyaraNET;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ServiceProcess;
|
2024-08-12 08:08:32 +00:00
|
|
|
|
using WelsonJS.Service.Model;
|
2024-08-12 03:47:19 +00:00
|
|
|
|
|
|
|
|
|
namespace WelsonJS.Service
|
|
|
|
|
{
|
|
|
|
|
public class FileEventMonitor
|
|
|
|
|
{
|
|
|
|
|
private Rules rules;
|
|
|
|
|
private EventLogWatcher eventLogWatcher;
|
|
|
|
|
private ServiceMain parent;
|
2024-08-12 12:05:17 +00:00
|
|
|
|
private string ruleDirectoryPath;
|
2024-08-12 03:47:19 +00:00
|
|
|
|
|
|
|
|
|
public FileEventMonitor(ServiceBase parent, string workingDirectory)
|
|
|
|
|
{
|
|
|
|
|
this.parent = (ServiceMain)parent;
|
2024-08-12 12:05:17 +00:00
|
|
|
|
ruleDirectoryPath = Path.Combine(workingDirectory, "app/assets/yar");
|
2024-08-12 03:47:19 +00:00
|
|
|
|
|
2024-08-12 04:00:04 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2024-08-12 12:05:17 +00:00
|
|
|
|
AddYaraRules(new List<string>(Directory.GetFiles(ruleDirectoryPath, "*.yar")));
|
2024-08-12 04:00:04 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2024-08-25 14:16:21 +00:00
|
|
|
|
this.parent.Log($"Failed to read the rule files: {ex.Message}");
|
2024-08-12 04:00:04 +00:00
|
|
|
|
}
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<string>(yarFiles));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddYaraRules(List<string> 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);
|
2024-08-26 16:24:11 +00:00
|
|
|
|
parent.Log($"Loaded file: {ruleFile}");
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
parent.Log($"File not found: {ruleFile}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rules = compiler.GetRules();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2024-08-25 14:16:21 +00:00
|
|
|
|
parent.Log($"Error loading the rules: {ex.Message}");
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
2024-08-12 04:00:04 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string query = @"<QueryList>
|
|
|
|
|
<Query Id='0' Path='Microsoft-Windows-Sysmon/Operational'>
|
|
|
|
|
<Select Path='Microsoft-Windows-Sysmon/Operational'>*[System/EventID=11]</Select>
|
|
|
|
|
</Query>
|
|
|
|
|
</QueryList>";
|
2024-08-12 03:47:19 +00:00
|
|
|
|
|
2024-08-12 04:00:04 +00:00
|
|
|
|
EventLogQuery eventLogQuery = new EventLogQuery("Microsoft-Windows-Sysmon/Operational", PathType.LogName, query);
|
|
|
|
|
eventLogWatcher = new EventLogWatcher(eventLogQuery);
|
2024-08-12 03:47:19 +00:00
|
|
|
|
|
2024-08-12 04:00:04 +00:00
|
|
|
|
eventLogWatcher.EventRecordWritten += new EventHandler<EventRecordWrittenEventArgs>(OnEventRecordWritten);
|
|
|
|
|
eventLogWatcher.Enabled = true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2024-08-26 16:24:11 +00:00
|
|
|
|
parent.Log($"Failed to connect the Windows EventLog Service: {ex.Message}");
|
2024-08-12 04:00:04 +00:00
|
|
|
|
}
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
if (eventLogWatcher != null)
|
|
|
|
|
{
|
2024-08-12 04:36:27 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
eventLogWatcher.Dispose();
|
|
|
|
|
eventLogWatcher = null;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
eventLogWatcher = null;
|
|
|
|
|
}
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2024-08-25 14:16:21 +00:00
|
|
|
|
parent.Log("No rules loaded. Skipping file scan.");
|
2024-08-12 03:47:19 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (var ctx = new YaraContext())
|
|
|
|
|
{
|
|
|
|
|
var scanner = new Scanner();
|
|
|
|
|
var results = scanner.ScanFile(filePath, rules);
|
|
|
|
|
|
|
|
|
|
if (results.Count > 0)
|
|
|
|
|
{
|
2024-08-26 16:24:11 +00:00
|
|
|
|
parent.Log($"Match Found: {filePath}");
|
2024-08-12 03:47:19 +00:00
|
|
|
|
|
|
|
|
|
foreach (var result in results)
|
|
|
|
|
{
|
2024-08-12 07:42:37 +00:00
|
|
|
|
Dictionary<string, List<Match>> matches = result.Matches;
|
|
|
|
|
foreach (KeyValuePair<string, List<Match>> match in matches)
|
2024-08-12 03:47:19 +00:00
|
|
|
|
{
|
2024-08-12 07:42:37 +00:00
|
|
|
|
string ruleName = match.Key;
|
|
|
|
|
List<Match> ruleMatches = match.Value;
|
|
|
|
|
ruleMatches.ForEach((x) =>
|
|
|
|
|
{
|
2024-08-26 16:24:11 +00:00
|
|
|
|
parent.Log($"Matched {ruleName}: {filePath}, Offset {x.Offset}");
|
2024-08-12 07:44:25 +00:00
|
|
|
|
parent.DispatchServiceEvent("fileRuleMatched", new string[] { ruleName, filePath, x.Offset.ToString() });
|
2024-08-12 08:08:32 +00:00
|
|
|
|
|
2024-08-24 12:32:44 +00:00
|
|
|
|
IndexFileRuleMatched(new FileMatchResult
|
2024-08-12 08:08:32 +00:00
|
|
|
|
{
|
|
|
|
|
FilePath = filePath,
|
|
|
|
|
Offset = x.Offset,
|
|
|
|
|
RuleName = ruleName,
|
|
|
|
|
LastChecked = DateTime.Now
|
|
|
|
|
});
|
2024-08-12 07:42:37 +00:00
|
|
|
|
});
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-08-26 16:24:11 +00:00
|
|
|
|
parent.Log($"No match found in {filePath}.");
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-24 12:32:44 +00:00
|
|
|
|
private void IndexFileRuleMatched(FileMatchResult match)
|
2024-08-12 08:08:32 +00:00
|
|
|
|
{
|
2024-08-25 14:16:21 +00:00
|
|
|
|
// TODO (Save a result to the document indexer)
|
2024-08-12 08:08:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-12 03:47:19 +00:00
|
|
|
|
private void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (rules != null)
|
|
|
|
|
{
|
2024-08-12 04:36:27 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//rules.Dispose();
|
|
|
|
|
rules = null;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
rules = null;
|
|
|
|
|
}
|
2024-08-12 03:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|