-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathProgram.cs
More file actions
391 lines (352 loc) · 17.2 KB
/
Program.cs
File metadata and controls
391 lines (352 loc) · 17.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
using System;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using YAMAGoya.Core;
namespace YAMAGoya
{
/// <summary>
/// The main entry point for the YAMAGoya application.
/// Processes command-line arguments to start or stop an ETW session,
/// perform detection based on YAML rule files, and optionally enable verbose logging.
/// </summary>
[SupportedOSPlatform("windows")]
internal static class CommandLineProcessor
{
// Define all valid options in a set
private static readonly HashSet<string> ValidOptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"--help", "-h",
"--session", "-s",
"--stop", "-x",
"--detect", "-d",
"--process", "-p",
"--load", "-l",
"--file", "-f",
"--delfile", "-df",
"--registry", "-r",
"--open", "-o",
"--dns", "-n",
"--ipv4", "-i4",
"--ipv6", "-i6",
"--powershell", "-ps1",
"--shell", "-sh",
"--wmi", "-w",
"--sigma", "-si",
"--all", "-a",
"--kill", "-k",
"--yara", "-y",
"--session_name",
"--no_text_log",
"--no_event_log",
"--check_interval",
"--memory_scan_interval",
"--log_path",
"--verbose"
};
/// <summary>
/// Validates and sanitizes session name to prevent injection attacks
/// </summary>
private static string ValidateSessionName(string sessionName)
{
if (string.IsNullOrWhiteSpace(sessionName))
{
throw new ArgumentException("Session name cannot be null, empty, or whitespace.");
}
// Session name should only contain alphanumeric characters, hyphens, and underscores
if (!Regex.IsMatch(sessionName, @"^[a-zA-Z0-9_-]+$"))
{
throw new ArgumentException("Session name can only contain alphanumeric characters, hyphens, and underscores.");
}
if (sessionName.Length > 20)
{
throw new ArgumentException("Session name cannot exceed 20 characters.");
}
return sessionName;
}
/// <summary>
/// Validates and sanitizes file/folder paths to prevent directory traversal attacks
/// </summary>
private static string ValidateAndSanitizePath(string path, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Path cannot be null, empty, or whitespace.");
}
// Remove any potential directory traversal sequences with explicit StringComparison
string sanitizedPath = path;
while (sanitizedPath.Contains("../", StringComparison.Ordinal) ||
sanitizedPath.Contains("..\\", StringComparison.Ordinal))
{
sanitizedPath = sanitizedPath.Replace("../", "", StringComparison.Ordinal)
.Replace("..\\", "", StringComparison.Ordinal);
}
try
{
// Get the full path to resolve any relative paths
string fullPath = Path.GetFullPath(sanitizedPath);
// Ensure the path doesn't contain any remaining traversal attempts
if (fullPath.Contains("..", StringComparison.Ordinal))
{
throw new ArgumentException("Path contains invalid directory traversal sequences.");
}
// Check if path exists when required
if (mustExist && !Directory.Exists(fullPath) && !File.Exists(fullPath))
{
throw new ArgumentException($"The specified path does not exist: {fullPath}");
}
return fullPath;
}
catch (Exception ex) when (!(ex is ArgumentException))
{
throw new ArgumentException($"Invalid path format: {path}", ex);
}
}
/// <summary>
/// Validates integer input within specified range
/// </summary>
private static int ValidateIntegerInput(string input, string parameterName, int minValue = 1, int maxValue = int.MaxValue)
{
if (string.IsNullOrWhiteSpace(input))
{
throw new ArgumentException($"{parameterName} cannot be null, empty, or whitespace.");
}
if (!int.TryParse(input, out int value))
{
throw new ArgumentException($"{parameterName} must be a valid integer.");
}
if (value < minValue || value > maxValue)
{
throw new ArgumentException($"{parameterName} must be between {minValue} and {maxValue}.");
}
return value;
}
/// <summary>
/// The main entry point for the YAMAGoya application.
/// </summary>
public static void Process(string[] args)
{
string sessionName = "";
// If session name is set, use it
if (args.Contains("--session_name"))
{
int sessionNameIndex = Array.IndexOf(args, "--session_name");
if (sessionNameIndex < 0 || sessionNameIndex == args.Length - 1)
{
throw new ArgumentException("You must specify a session name after --session_name.");
}
sessionName = ValidateSessionName(args[sessionNameIndex + 1]);
Console.WriteLine($"[INFO] Using session name: {sessionName}");
} else {
sessionName = ValidateSessionName(Config.sessionName);
Console.WriteLine($"[INFO] Using default session name: {sessionName}");
}
if (args.Contains("--no_text_log"))
{
Config.isTextLog = false;
Console.WriteLine("[INFO] Text logging disabled.");
}
if (args.Contains("--no_event_log"))
{
Config.isEventLog = false;
Console.WriteLine("[INFO] Windows Event Log disabled.");
}
if (args.Contains("--check_interval"))
{
int index = Array.IndexOf(args, "--check_interval");
if (index >= 0 && index < args.Length - 1)
{
int interval = ValidateIntegerInput(args[index + 1], "Check interval", 1, 3600);
Config.checkInterval = interval;
Console.WriteLine($"[INFO] Check interval set to: {Config.checkInterval} seconds.");
}
else
{
throw new ArgumentException("You must specify a valid integer after --check_interval.");
}
}
if (args.Contains("--memory_scan_interval"))
{
int index = Array.IndexOf(args, "--memory_scan_interval");
if (index >= 0 && index < args.Length - 1)
{
int interval = ValidateIntegerInput(args[index + 1], "Memory scan interval", 1, 24);
Config.memoryScanInterval = interval;
Console.WriteLine($"[INFO] Memory scan interval set to: {Config.memoryScanInterval} hour(s).");
}
else
{
throw new ArgumentException("You must specify a valid integer after --memory_scan_interval.");
}
}
if (args.Contains("--log_path"))
{
int index = Array.IndexOf(args, "--log_path");
if (index >= 0 && index < args.Length - 1)
{
string validatedPath = ValidateAndSanitizePath(args[index + 1], false);
// Ensure the directory exists or can be created
string? directory = Path.GetDirectoryName(validatedPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
try
{
Directory.CreateDirectory(directory);
}
catch (Exception ex)
{
throw new ArgumentException($"Cannot create log directory: {directory}", ex);
}
}
Config.logDirectory = validatedPath;
Console.WriteLine($"[INFO] Log folder set to: {Config.logDirectory}");
}
else
{
throw new ArgumentException("You must specify a valid path after --log_path.");
}
}
// If there's no args or help is requested, show usage
if (args.Length == 0 || args.Contains("--help") || args.Contains("-h"))
{
ShowUsageDetail();
return;
}
// Check each argument. If it starts with "-" (or "--"), validate it.
var invalidOption = args.FirstOrDefault(arg => arg.StartsWith('-') && !ValidOptions.Contains(arg));
if (invalidOption != null)
{
ShowUsage();
throw new ArgumentException($"Unknown option: {invalidOption}");
}
// If --verbose is specified, you can handle it here
bool isVerbose = args.Contains("--verbose");
if (isVerbose)
{
Console.WriteLine("[VERBOSE] Verbose mode enabled.");
Config.logLevel = Config.LogLevel.Debug;
}
// Handle recognized options
if (args.Contains("--session") || args.Contains("-s"))
{
EtwSession.StartEtwSession(sessionName);
}
if (args.Contains("--stop") || args.Contains("-x"))
{
EtwSession.StopEtwSession(sessionName);
}
if (args.Contains("--detect") || args.Contains("-d"))
{
// Find the index of the option
int detectIndex = Array.IndexOf(args, "--detect");
if (detectIndex < 0)
{
detectIndex = Array.IndexOf(args, "-d");
}
// The folder path should be the argument after the option
if (detectIndex < 0 || detectIndex == args.Length - 1)
{
throw new ArgumentException("You must specify a folder path after --detect or -d.");
}
string folder = ValidateAndSanitizePath(args[detectIndex + 1]);
using (var newDetect = new Detect())
{
newDetect.StartEtwDetection(folder, args, sessionName, CancellationToken.None);
}
}
if (args.Contains("--sigma") || args.Contains("-si"))
{
// Find the index of the option
int sigmaIndex = Array.IndexOf(args, "--sigma");
if (sigmaIndex < 0)
{
sigmaIndex = Array.IndexOf(args, "-si");
}
// The folder path should be the argument after the option
if (sigmaIndex < 0 || sigmaIndex == args.Length - 1)
{
throw new ArgumentException("You must specify a folder path after --sigma or -si.");
}
string folder = ValidateAndSanitizePath(args[sigmaIndex + 1]);
using (var newDetect = new Detect())
{
newDetect.StartEtwDetection(folder, args, sessionName, CancellationToken.None);
}
}
if (args.Contains("--yara") || args.Contains("-y"))
{
int yaraIndex = Array.IndexOf(args, "--yara");
if (yaraIndex < 0)
{
yaraIndex = Array.IndexOf(args, "-y");
}
if (yaraIndex < 0 || yaraIndex == args.Length - 1)
{
throw new ArgumentException("You must specify a folder path after --yara or -y.");
}
string folder = ValidateAndSanitizePath(args[yaraIndex + 1]);
using (var newDetect = new Detect())
{
newDetect.StartEtwDetection(folder, args, sessionName, CancellationToken.None);
}
}
}
private static void ShowUsage()
{
Console.WriteLine("YAMAGoya - A simple ETW tool for detecting malicious activities");
Console.WriteLine("Usage: YAMAGoya.exe [--help|-h] [--session|-s]");
Console.WriteLine(" [--detect folder|-d folder]");
Console.WriteLine(" [--sigma folder|-si folder]");
Console.WriteLine(" [--yara folder|-y folder]");
Console.WriteLine(" [--process|-p] [--file|-f] [--registry|-r]");
Console.WriteLine(" [--dns|-n] [--ipv4|-i4] [--ipv6|-i6] [--powershell|-ps1]");
Console.WriteLine(" [--shell|-sh] [--stop|-x] [--load|-l]");
Console.WriteLine(" [--open|-o]");
Console.WriteLine(" [--delfile|-df] [--all|-a] [--kill|-k]");
Console.WriteLine(" [--session_name sessionName] [--no_text_log] [--no_event_log]");
Console.WriteLine(" [--check_interval interval] [--memory_scan_interval interval] [--log_path path]");
Console.WriteLine(" [--wmi|-w] [--verbose]");
Console.WriteLine();
Console.WriteLine("Example:");
Console.WriteLine(" YAMAGoya.exe --session --detect C:\\Rules --file --verbose");
}
private static void ShowUsageDetail()
{
Console.WriteLine("YAMAGoya - A simple ETW tool for detecting malicious activities");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(" --help, -h Show this help message and exit");
Console.WriteLine(" --session, -s Start an ETW session");
Console.WriteLine(" --stop, -x Stop the current ETW session");
Console.WriteLine(" --detect [filder], -d [folder] Detect using default YAML rules");
Console.WriteLine(" --sigma [filder], -si [filder] Detect using Sigma rules");
Console.WriteLine(" --yara [filder], -y [filder] Detect using YARA rules");
Console.WriteLine(" --kill, -k Terminate the detected process");
Console.WriteLine(" --all, -a Detect all activities");
Console.WriteLine(" --process, -p Detect process creation and termination");
Console.WriteLine(" --load, -l Detect DLL loading");
Console.WriteLine(" --file, -f Detect file creation");
Console.WriteLine(" --delfile, -df Detect file deletion");
Console.WriteLine(" --registry, -r Detect registry key creation");
Console.WriteLine(" --open, -o Detect OpenProcess calls");
Console.WriteLine(" --dns, -n Detect DNS queries and responses");
Console.WriteLine(" --ipv4, -i4 Detect IPv4 network activities");
Console.WriteLine(" --ipv6, -i6 Detect IPv6 network activities");
Console.WriteLine(" --powershell, -ps1 Detect PowerShell script execution");
Console.WriteLine(" --wmi, -w Detect WMI command execution");
Console.WriteLine(" --shell, -sh Detect shell commands execution(startup and Runkey)");
Console.WriteLine(" --session_name [sessionName] Set the name of the ETW session");
Console.WriteLine(" --no_text_log Disable text log file");
Console.WriteLine(" --no_event_log Disable logging to the Windows Event Log");
Console.WriteLine(" --check_interval [interval] Set the time interval for checking rule timeouts");
Console.WriteLine(" --memory_scan_interval [interval] Set the memory scan interval for YARA rules (1-24 hours)");
Console.WriteLine(" --log_path [path] Set the path to the text log file");
Console.WriteLine(" --verbose Enable verbose mode");
Console.WriteLine();
Console.WriteLine("Examples:");
Console.WriteLine(" YAMAGoya.exe --session --detect C:\\Rules --file --verbose");
}
}
}