mirror of
https://github.com/gnh1201/welsonjs.git
synced 2024-11-26 15:31:42 +00:00
255 lines
10 KiB
C#
255 lines
10 KiB
C#
// FileEventMonitor.cs
|
|
// Namhyeon Go <abuse@catswords.net>
|
|
// https://github.com/gnh1201/welsonjs
|
|
using ClamAV.Net.Client;
|
|
using ClamAV.Net.Client.Results;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Diagnostics.Eventing.Reader;
|
|
using System.ServiceProcess;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace WelsonJS.Service
|
|
{
|
|
public class FileEventMonitor
|
|
{
|
|
private EventLogWatcher eventLogWatcher;
|
|
private ServiceMain parent;
|
|
private ILogger logger;
|
|
private enum EventType: int
|
|
{
|
|
FileCreate = 11,
|
|
NetworkConnection = 3,
|
|
RegistryEvent_1 = 12,
|
|
RegistryEvent_2 = 13,
|
|
RegistryEvent_3 = 14
|
|
};
|
|
private enum FileCreateEvent: int {
|
|
RuleName,
|
|
UtcTime,
|
|
ProcessGuid,
|
|
ProcessId,
|
|
Image,
|
|
TargetFilename,
|
|
CreationUtcTime,
|
|
User
|
|
};
|
|
private enum NetworkConnectionEvent: int
|
|
{
|
|
RuleName,
|
|
UtcTime,
|
|
ProcessGuid,
|
|
ProcessId,
|
|
Image,
|
|
User,
|
|
Protocol,
|
|
Initiated,
|
|
SourceIsIpv6,
|
|
SourceIp,
|
|
SourceHostname,
|
|
SourcePort,
|
|
SourcePortName,
|
|
DestinationIsIpv6,
|
|
DestinationIp,
|
|
DestinationHostname,
|
|
DestinationPort,
|
|
DestinationPortName,
|
|
};
|
|
private enum RegistryEvent: int
|
|
{
|
|
RuleName,
|
|
EventType,
|
|
UtcTime,
|
|
ProcessGuid,
|
|
ProcessId,
|
|
Image,
|
|
TargetObject,
|
|
Details,
|
|
User
|
|
}
|
|
private string clamAvConenctionString;
|
|
private IClamAvClient clamAvClient;
|
|
|
|
public FileEventMonitor(ServiceBase _parent, string workingDirectory, ILogger _logger)
|
|
{
|
|
parent = (ServiceMain)_parent;
|
|
logger = _logger;
|
|
|
|
try
|
|
{
|
|
clamAvConenctionString = parent.ReadSettingsValue("CLAMAV_HOST");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
clamAvConenctionString = "tcp://127.0.0.1:3310";
|
|
logger.LogInformation($"Failed to read the address because of {ex.Message}. Set default: {clamAvConenctionString}");
|
|
}
|
|
ConnectToClamAv().Start();
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
try
|
|
{
|
|
string query = @"<QueryList>
|
|
<Query Id='0' Path='Microsoft-Windows-Sysmon/Operational'>
|
|
<Select Path='Microsoft-Windows-Sysmon/Operational'>*[System/EventID=11 or System/EventID=3 or System/EventID=12 or System/EventID=13 or System/EventID=14]</Select>
|
|
</Query>
|
|
</QueryList>";
|
|
|
|
EventLogQuery eventLogQuery = new EventLogQuery("Microsoft-Windows-Sysmon/Operational", PathType.LogName, query);
|
|
eventLogWatcher = new EventLogWatcher(eventLogQuery);
|
|
|
|
eventLogWatcher.EventRecordWritten += new EventHandler<EventRecordWrittenEventArgs>(OnEventRecordWritten);
|
|
eventLogWatcher.Enabled = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogInformation($"Could not reach to the Sysmon service: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (eventLogWatcher != null)
|
|
{
|
|
try
|
|
{
|
|
eventLogWatcher.Dispose();
|
|
eventLogWatcher = null;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
eventLogWatcher = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnEventRecordWritten(object sender, EventRecordWrittenEventArgs e)
|
|
{
|
|
if (e.EventRecord != null)
|
|
{
|
|
int eventId = e.EventRecord.Id;
|
|
|
|
try
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case (int)EventType.FileCreate:
|
|
{
|
|
string ruleName = e.EventRecord.Properties[(int)FileCreateEvent.RuleName]?.Value?.ToString();
|
|
string processId = e.EventRecord.Properties[(int)FileCreateEvent.ProcessId]?.Value?.ToString();
|
|
string image = e.EventRecord.Properties[(int)FileCreateEvent.Image]?.Value?.ToString();
|
|
string fileName = e.EventRecord.Properties[(int)FileCreateEvent.TargetFilename]?.Value?.ToString();
|
|
|
|
logger.LogInformation($"> Detected the file creation: {fileName}");
|
|
logger.LogInformation(parent.DispatchServiceEvent("fileCreated", new string[] {
|
|
ruleName,
|
|
processId,
|
|
image,
|
|
fileName
|
|
}));
|
|
|
|
if (clamAvClient != null)
|
|
{
|
|
logger.LogInformation($"> Starting the ClamAV scan: {fileName}");
|
|
Task.Run(async () =>
|
|
{
|
|
await ScanWithClamAv(fileName);
|
|
});
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case (int)EventType.NetworkConnection:
|
|
{
|
|
string ruleName = e.EventRecord.Properties[(int)NetworkConnectionEvent.RuleName]?.Value?.ToString();
|
|
string processId = e.EventRecord.Properties[(int)NetworkConnectionEvent.ProcessId]?.Value?.ToString();
|
|
string image = e.EventRecord.Properties[(int)NetworkConnectionEvent.Image]?.Value?.ToString();
|
|
string protocol = e.EventRecord.Properties[(int)NetworkConnectionEvent.Protocol]?.Value?.ToString();
|
|
string destinationIp = e.EventRecord.Properties[(int)NetworkConnectionEvent.DestinationIp]?.Value?.ToString();
|
|
string desinationPort = e.EventRecord.Properties[(int)NetworkConnectionEvent.DestinationPort]?.Value?.ToString();
|
|
string dstinationAddress = $"{protocol}://{destinationIp}:{desinationPort}";
|
|
|
|
logger.LogInformation($"> Detected the network connection: {dstinationAddress}");
|
|
logger.LogInformation(parent.DispatchServiceEvent("networkConnected", new string[] {
|
|
ruleName,
|
|
processId,
|
|
image,
|
|
dstinationAddress
|
|
}));
|
|
|
|
break;
|
|
}
|
|
|
|
case (int)EventType.RegistryEvent_1:
|
|
case (int)EventType.RegistryEvent_2:
|
|
case (int)EventType.RegistryEvent_3:
|
|
{
|
|
string ruleName = e.EventRecord.Properties[(int)RegistryEvent.RuleName]?.Value?.ToString();
|
|
string processId = e.EventRecord.Properties[(int)RegistryEvent.ProcessId]?.Value?.ToString();
|
|
string image = e.EventRecord.Properties[(int)RegistryEvent.Image]?.Value?.ToString();
|
|
string eventType = e.EventRecord.Properties[(int)RegistryEvent.EventType]?.Value?.ToString();
|
|
string targetObject = e.EventRecord.Properties[(int)RegistryEvent.TargetObject]?.Value?.ToString();
|
|
|
|
logger.LogInformation($"> Detected the registry modification: {targetObject}");
|
|
logger.LogInformation(parent.DispatchServiceEvent("registryModified", new string[] {
|
|
ruleName,
|
|
processId,
|
|
image,
|
|
eventType,
|
|
targetObject
|
|
}));
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
throw new ArgumentException("Not supported event type");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogInformation($"Failed to process the event bacause of {ex.Message}.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.LogInformation("The event instance was null.");
|
|
}
|
|
}
|
|
|
|
private async Task ConnectToClamAv()
|
|
{
|
|
try {
|
|
// Create a client
|
|
clamAvClient = ClamAvClient.Create(new Uri(clamAvConenctionString));
|
|
|
|
// Send PING command to ClamAV
|
|
await clamAvClient.PingAsync().ConfigureAwait(false);
|
|
|
|
// Get ClamAV engine and virus database version
|
|
VersionResult result = await clamAvClient.GetVersionAsync().ConfigureAwait(false);
|
|
|
|
logger.LogInformation($"ClamAV version {result.ProgramVersion}, Virus database version {result.VirusDbVersion}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogInformation($"Failed to read the address because of {ex.Message}. {clamAvConenctionString}");
|
|
clamAvClient = null;
|
|
}
|
|
}
|
|
|
|
private async Task ScanWithClamAv(string remotePath)
|
|
{
|
|
ScanResult res = await clamAvClient.ScanRemotePathAsync(remotePath).ConfigureAwait(false);
|
|
|
|
logger.LogInformation($"> Scan result: Infected={res.Infected}, VirusName={res.VirusName}");
|
|
logger.LogInformation(parent.DispatchServiceEvent("avScanResult", new string[] {
|
|
res.Infected.ToString(),
|
|
res.VirusName
|
|
}));
|
|
}
|
|
}
|
|
} |