mirror of
https://github.com/gnh1201/welsonjs.git
synced 2024-11-26 15:31:42 +00:00
Add Yara File Rule Matcher #131
This commit is contained in:
parent
1a4931e283
commit
58d1829c9c
167
WelsonJS.Toolkit/WelsonJS.Service/FileEventMonitor.cs
Normal file
167
WelsonJS.Toolkit/WelsonJS.Service/FileEventMonitor.cs
Normal file
|
@ -0,0 +1,167 @@
|
|||
// 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;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class FileEventMonitor
|
||||
{
|
||||
private Rules rules;
|
||||
private EventLogWatcher eventLogWatcher;
|
||||
private ServiceMain parent;
|
||||
private string ruleFolderPath;
|
||||
|
||||
public FileEventMonitor(ServiceBase parent, string workingDirectory)
|
||||
{
|
||||
this.parent = (ServiceMain)parent;
|
||||
this.ruleFolderPath = Path.Combine(workingDirectory, "app/assets/yar");
|
||||
|
||||
AddYaraRules(new List<string>(Directory.GetFiles(this.ruleFolderPath, "*.yar")));
|
||||
}
|
||||
|
||||
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);
|
||||
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()
|
||||
{
|
||||
string query = @"<QueryList>
|
||||
<Query Id='0' Path='Microsoft-Windows-Sysmon/Operational'>
|
||||
<Select Path='Microsoft-Windows-Sysmon/Operational'>*[System/EventID=11]</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;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (eventLogWatcher != null)
|
||||
{
|
||||
eventLogWatcher.Dispose();
|
||||
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)
|
||||
{
|
||||
var matches = result.Matches;
|
||||
foreach (var match in matches)
|
||||
{
|
||||
parent.Log($"YARA matched: {match.ToString()}");
|
||||
|
||||
parent.DispatchServiceEvent("fileRuleMatched", new string[] { filePath, match.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Log($"No YARA match found in file {filePath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose()
|
||||
{
|
||||
if (rules != null)
|
||||
{
|
||||
rules.Dispose();
|
||||
rules = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
// ScreenMatching.cs
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
@ -49,17 +51,17 @@ public class ScreenMatching
|
|||
public int Bottom;
|
||||
}
|
||||
|
||||
ServiceMain parent;
|
||||
public List<Bitmap> templateImages;
|
||||
string templateFolderPath;
|
||||
int currentTemplateIndex = 0;
|
||||
private List<Bitmap> templateImages;
|
||||
private string templateFolderPath;
|
||||
private int currentTemplateIndex = 0;
|
||||
private ServiceMain parent;
|
||||
|
||||
public ScreenMatching(ServiceBase _parent, string workingDirectory)
|
||||
public ScreenMatching(ServiceBase parent, string workingDirectory)
|
||||
{
|
||||
parent = (ServiceMain)_parent;
|
||||
this.parent = (ServiceMain)parent;
|
||||
this.templateFolderPath = Path.Combine(workingDirectory, "app/assets/img/_templates");
|
||||
this.templateImages = new List<Bitmap>();
|
||||
|
||||
templateFolderPath = Path.Combine(workingDirectory, "app/assets/img/_templates");
|
||||
templateImages = new List<Bitmap>();
|
||||
LoadTemplateImages();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,11 @@
|
|||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
//
|
||||
// ServiceMain
|
||||
//
|
||||
this.ServiceName = "ServiceMain";
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -31,7 +31,6 @@ using System.Runtime.InteropServices;
|
|||
using MSScriptControl;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
|
@ -47,7 +46,9 @@ namespace WelsonJS.Service
|
|||
private readonly string appName = "WelsonJS";
|
||||
private string[] _args;
|
||||
private bool disabledScreenTime = false;
|
||||
private bool disabledFileMonitor = false;
|
||||
private ScreenMatching screenMatcher;
|
||||
private FileEventMonitor fileEventMonitor;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetSystemMetrics(int nIndex);
|
||||
|
@ -77,6 +78,10 @@ namespace WelsonJS.Service
|
|||
case "disable-screen-time":
|
||||
disabledScreenTime = true;
|
||||
break;
|
||||
|
||||
case "disable-file-monitor":
|
||||
disabledFileMonitor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +120,15 @@ namespace WelsonJS.Service
|
|||
defaultTimer.Elapsed += OnElapsedTime;
|
||||
timers.Add(defaultTimer);
|
||||
|
||||
// Trace an event of file creation
|
||||
if (!disabledFileMonitor)
|
||||
{
|
||||
fileEventMonitor = new FileEventMonitor(this, workingDirectory);
|
||||
fileEventMonitor.Start();
|
||||
|
||||
Log("File Event Monitor started.");
|
||||
}
|
||||
|
||||
// check this session is the user interactive mode
|
||||
if (Environment.UserInteractive) {
|
||||
this.OnUserInteractiveEnvironment();
|
||||
|
@ -170,15 +184,20 @@ namespace WelsonJS.Service
|
|||
Log($"Script file not found: {scriptFilePath}");
|
||||
}
|
||||
|
||||
timers.ForEach(timer => timer.Start()); // start
|
||||
timers.ForEach(timer => timer?.Start()); // start
|
||||
|
||||
Log(appName + " Service Started");
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
timers.ForEach(timer => timer.Stop()); // stop
|
||||
// stop timers
|
||||
timers.ForEach(timer => timer?.Stop());
|
||||
|
||||
// stop the File Event Monitor
|
||||
fileEventMonitor?.Stop();
|
||||
|
||||
// dispatch stop callback
|
||||
try
|
||||
{
|
||||
Log(DispatchServiceEvent("stop"));
|
||||
|
@ -261,19 +280,6 @@ namespace WelsonJS.Service
|
|||
}
|
||||
}
|
||||
|
||||
private string DispatchServiceEvent(string eventType, string[] args = null)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
return InvokeScriptMethod("dispatchServiceEvent", scriptName, eventType, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
return InvokeScriptMethod("dispatchServiceEvent", scriptName, eventType, String.Join("; ", args));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private string InvokeScriptMethod(string methodName, params object[] parameters)
|
||||
{
|
||||
if (scriptControl != null)
|
||||
|
@ -314,6 +320,19 @@ namespace WelsonJS.Service
|
|||
return arguments;
|
||||
}
|
||||
|
||||
public string DispatchServiceEvent(string eventType, string[] args = null)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
return InvokeScriptMethod("dispatchServiceEvent", scriptName, eventType, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
return InvokeScriptMethod("dispatchServiceEvent", scriptName, eventType, String.Join("; ", args));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
string _message = $"{DateTime.Now}: {message}";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Microsoft.O365.Security.Native.libyara.NET.4.5.1\build\net462\Microsoft.O365.Security.Native.libyara.NET.props" Condition="Exists('..\packages\Microsoft.O365.Security.Native.libyara.NET.4.5.1\build\net462\Microsoft.O365.Security.Native.libyara.NET.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -12,6 +13,8 @@
|
|||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
|
@ -90,6 +93,7 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileEventMonitor.cs" />
|
||||
<Compile Include="ServiceMain.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
|
@ -126,6 +130,13 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.O365.Security.Native.libyara.NET.4.5.1\build\net462\Microsoft.O365.Security.Native.libyara.NET.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.O365.Security.Native.libyara.NET.4.5.1\build\net462\Microsoft.O365.Security.Native.libyara.NET.props'))" />
|
||||
</Target>
|
||||
</Project>
|
4
WelsonJS.Toolkit/WelsonJS.Service/packages.config
Normal file
4
WelsonJS.Toolkit/WelsonJS.Service/packages.config
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.O365.Security.Native.libyara.NET" version="4.5.1" targetFramework="net48" />
|
||||
</packages>
|
19
app.js
19
app.js
|
@ -595,6 +595,15 @@ function dispatchServiceEvent(name, eventType, _args) {
|
|||
|
||||
// load the service
|
||||
if (app) {
|
||||
var bind = function(eventType) {
|
||||
var event_callback_name = "on" + eventType;
|
||||
|
||||
if (event_callback_name in app && typeof app[event_callback_name] === "function")
|
||||
return app[event_callback_name];
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (function(action) {
|
||||
if (eventType in action) {
|
||||
try {
|
||||
|
@ -606,10 +615,12 @@ function dispatchServiceEvent(name, eventType, _args) {
|
|||
}
|
||||
}
|
||||
})({
|
||||
start: app.onServiceStart,
|
||||
stop: app.onServiceStop,
|
||||
elapsedTime: app.onServiceElapsedTime,
|
||||
screenTime: app.onServiceScreenTime
|
||||
start: bind("ServiceStart"),
|
||||
stop: bind("ServiceStop"),
|
||||
elapsedTime: bind("ServiceElapsedTime"),
|
||||
screenTime: bind("ServiceScreenTime"),
|
||||
fileCreated: bind("FileCreated"),
|
||||
fileRuleMatched: bind("FileRuleMatched")
|
||||
});
|
||||
} else {
|
||||
console.error("Could not find", name + ".js");
|
||||
|
|
22
app/assets/yar/MALW_Eicar.yar
Normal file
22
app/assets/yar/MALW_Eicar.yar
Normal file
|
@ -0,0 +1,22 @@
|
|||
rule malw_eicar {
|
||||
|
||||
meta:
|
||||
|
||||
description = "Rule to detect the EICAR pattern"
|
||||
author = "Marc Rivero | McAfee ATR Team"
|
||||
reference = "https://www.eicar.org/"
|
||||
rule_version = "v1"
|
||||
malware_type = "eicar"
|
||||
malware_family = "W32/Eicar"
|
||||
actor_type = "Unknown"
|
||||
actor_group = "Unknown"
|
||||
hash = "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f"
|
||||
|
||||
strings:
|
||||
|
||||
$s1 = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" fullword ascii
|
||||
|
||||
condition:
|
||||
|
||||
any of them
|
||||
}
|
Loading…
Reference in New Issue
Block a user