mirror of
				https://github.com/gnh1201/welsonjs.git
				synced 2025-10-31 04:51:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			257 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| // FileEventMonitor.cs
 | |
| // SPDX-License-Identifier: MS-RL
 | |
| // SPDX-FileCopyrightText: 2025 Catswords OSS and WelsonJS Contributors
 | |
| // 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
 | |
|             }));
 | |
|         }
 | |
|     }
 | |
| } |