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.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -49,17 +51,17 @@ public class ScreenMatching
|
||||||
public int Bottom;
|
public int Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceMain parent;
|
private List<Bitmap> templateImages;
|
||||||
public List<Bitmap> templateImages;
|
private string templateFolderPath;
|
||||||
string templateFolderPath;
|
private int currentTemplateIndex = 0;
|
||||||
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();
|
LoadTemplateImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,11 @@
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
components = new System.ComponentModel.Container();
|
//
|
||||||
|
// ServiceMain
|
||||||
|
//
|
||||||
this.ServiceName = "ServiceMain";
|
this.ServiceName = "ServiceMain";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
@ -31,7 +31,6 @@ using System.Runtime.InteropServices;
|
||||||
using MSScriptControl;
|
using MSScriptControl;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace WelsonJS.Service
|
namespace WelsonJS.Service
|
||||||
{
|
{
|
||||||
|
@ -47,7 +46,9 @@ namespace WelsonJS.Service
|
||||||
private readonly string appName = "WelsonJS";
|
private readonly string appName = "WelsonJS";
|
||||||
private string[] _args;
|
private string[] _args;
|
||||||
private bool disabledScreenTime = false;
|
private bool disabledScreenTime = false;
|
||||||
|
private bool disabledFileMonitor = false;
|
||||||
private ScreenMatching screenMatcher;
|
private ScreenMatching screenMatcher;
|
||||||
|
private FileEventMonitor fileEventMonitor;
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern int GetSystemMetrics(int nIndex);
|
public static extern int GetSystemMetrics(int nIndex);
|
||||||
|
@ -77,6 +78,10 @@ namespace WelsonJS.Service
|
||||||
case "disable-screen-time":
|
case "disable-screen-time":
|
||||||
disabledScreenTime = true;
|
disabledScreenTime = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "disable-file-monitor":
|
||||||
|
disabledFileMonitor = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +120,15 @@ namespace WelsonJS.Service
|
||||||
defaultTimer.Elapsed += OnElapsedTime;
|
defaultTimer.Elapsed += OnElapsedTime;
|
||||||
timers.Add(defaultTimer);
|
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
|
// check this session is the user interactive mode
|
||||||
if (Environment.UserInteractive) {
|
if (Environment.UserInteractive) {
|
||||||
this.OnUserInteractiveEnvironment();
|
this.OnUserInteractiveEnvironment();
|
||||||
|
@ -170,15 +184,20 @@ namespace WelsonJS.Service
|
||||||
Log($"Script file not found: {scriptFilePath}");
|
Log($"Script file not found: {scriptFilePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
timers.ForEach(timer => timer.Start()); // start
|
timers.ForEach(timer => timer?.Start()); // start
|
||||||
|
|
||||||
Log(appName + " Service Started");
|
Log(appName + " Service Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnStop()
|
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
|
try
|
||||||
{
|
{
|
||||||
Log(DispatchServiceEvent("stop"));
|
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)
|
private string InvokeScriptMethod(string methodName, params object[] parameters)
|
||||||
{
|
{
|
||||||
if (scriptControl != null)
|
if (scriptControl != null)
|
||||||
|
@ -314,6 +320,19 @@ namespace WelsonJS.Service
|
||||||
return arguments;
|
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)
|
public void Log(string message)
|
||||||
{
|
{
|
||||||
string _message = $"{DateTime.Now}: {message}";
|
string _message = $"{DateTime.Now}: {message}";
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<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')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -12,6 +13,8 @@
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
@ -90,6 +93,7 @@
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="FileEventMonitor.cs" />
|
||||||
<Compile Include="ServiceMain.cs">
|
<Compile Include="ServiceMain.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -126,6 +130,13 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<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>
|
</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
|
// load the service
|
||||||
if (app) {
|
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) {
|
return (function(action) {
|
||||||
if (eventType in action) {
|
if (eventType in action) {
|
||||||
try {
|
try {
|
||||||
|
@ -606,10 +615,12 @@ function dispatchServiceEvent(name, eventType, _args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})({
|
})({
|
||||||
start: app.onServiceStart,
|
start: bind("ServiceStart"),
|
||||||
stop: app.onServiceStop,
|
stop: bind("ServiceStop"),
|
||||||
elapsedTime: app.onServiceElapsedTime,
|
elapsedTime: bind("ServiceElapsedTime"),
|
||||||
screenTime: app.onServiceScreenTime
|
screenTime: bind("ServiceScreenTime"),
|
||||||
|
fileCreated: bind("FileCreated"),
|
||||||
|
fileRuleMatched: bind("FileRuleMatched")
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error("Could not find", name + ".js");
|
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