public static void Main(string[] args) { SilkUtility.PrintLogo(); if (ArgumentEscaper.EscapeAndConcatenate(args).Length == 0) { SilkUtility.PrintHelp(); return; } CommandLineApplication.Execute <Silk>(args); }
public static void SetupCtrlCHandler(Action action) { ctrlCExecuted = false; if (ctrlCHandler != null) { Console.CancelKeyPress -= ctrlCHandler; } ctrlCHandler = (object sender, ConsoleCancelEventArgs cancelArgs) => { if (!ctrlCExecuted) { ctrlCExecuted = true; SilkUtility.ReturnStatusMessage("[>] Stopping trace collector..", ConsoleColor.Yellow); action(); cancelArgs.Cancel = true; } }; Console.CancelKeyPress += ctrlCHandler; }
// Print help public static void PrintHelp() { string HelpText = "\n >--~~--> Args? <--~~--<\n\n" + "-h (--help) This help menu\n" + "-s (--silk) Trivia about Silk\n" + "-t (--type) Specify if we are using a Kernel or User collector\n" + "-kk (--kernelkeyword) Valid keywords: Process, Thread, ImageLoad, ProcessCounters, ContextSwitch,\n" + " DeferedProcedureCalls, Interrupt, SystemCall, DiskIO, DiskFileIO, DiskIOInit,\n" + " Dispatcher, Memory, MemoryHardFaults, VirtualAlloc, VAMap, NetworkTCPIP, Registry,\n" + " AdvancedLocalProcedureCalls, SplitIO, Handle, Driver, OS, Profile, Default,\n" + " ThreadTime, FileIO, FileIOInit, Verbose, All, IOQueue, ThreadPriority,\n" + " ReferenceSet, PMCProfile, NonContainer\n" + "-uk (--userkeyword) Define a mask of valid keywords, eg 0x2038 -> JitKeyword|InteropKeyword|\n" + " LoaderKeyword|NGenKeyword\n" + "-pn (--providername) User ETW provider name, eg \"Microsoft-Windows-DotNETRuntime\" or its\n" + " corresponding GUID eg \"e13c0d23-ccbc-4e12-931b-d9cc2eee27e4\"\n" + "-l (--level) Logging level: Always, Critical, Error, Warning, Informational, Verbose\n" + "-ot (--outputtype) Output type: POST to \"URL\", write to \"file\" or write to \"eventlog\"\n" + "-p (--path) Full output file path or URL. Event logs are automatically written to\n" + " \"Applications and Services Logs\\SilkETW-Log\"\n" + "-f (--filter) Filter types: None, EventName, ProcessID, ProcessName, Opcode\n" + "-fv (--filtervalue) Filter type capture value, eg \"svchost\" for ProcessName\n" + "-y (--yara) Full path to folder containing Yara rules\n" + "-yo (--yaraoptions) Either record \"All\" events or only \"Matches\"\n\n" + " >--~~--> Usage? <--~~--<\n"; Console.WriteLine(HelpText); SilkUtility.ReturnStatusMessage("# Use a VirtualAlloc Kernel collector, POST results to Elasticsearch", ConsoleColor.Green); Console.WriteLine("SilkETW.exe -t kernel -kk VirtualAlloc -ot url -p https://some.elk:9200/valloc/_doc/\n"); SilkUtility.ReturnStatusMessage("# Use a Process Kernel collector, filter on PID", ConsoleColor.Green); Console.WriteLine("SilkETW.exe -t kernel -kk Process -ot url -p https://some.elk:9200/kproc/_doc/ -f ProcessID -fv 11223\n"); SilkUtility.ReturnStatusMessage("# Use a .Net User collector, specify mask, filter on EventName, write to file", ConsoleColor.Green); Console.WriteLine("SilkETW.exe -t user -pn Microsoft-Windows-DotNETRuntime -uk 0x2038 -ot file -p C:\\Some\\Path\\out.json -f EventName -fv Method/LoadVerbose\n"); SilkUtility.ReturnStatusMessage("# Use a DNS User collector, specify log level, write to file", ConsoleColor.Green); Console.WriteLine("SilkETW.exe -t user -pn Microsoft-Windows-DNS-Client -l Always -ot file -p C:\\Some\\Path\\out.json\n"); SilkUtility.ReturnStatusMessage("# Use an LDAP User collector, perform Yara matching, POST matches to Elasticsearch", ConsoleColor.Green); Console.WriteLine("SilkETW.exe -t user -pn Microsoft-Windows-Ldap-Client -ot url -p https://some.elk:9200/ldap/_doc/ -y C:\\Some\\Yara\\Rule\\Folder -yo matches\n"); SilkUtility.ReturnStatusMessage("# Specify \"Microsoft-Windows-COM-Perf\" by its GUID, write results to the event log", ConsoleColor.Green); Console.WriteLine("SilkETW.exe -t user -pn b8d6861b-d20f-4eec-bbae-87e0dd80602b -ot eventlog"); }
public static int ProcessJSONEventData(String JSONData, OutputType OutputType, String Path, String YaraScan, YaraOptions YaraOptions) { // Yara options if (YaraScan != String.Empty) { byte[] JSONByteArray = Encoding.ASCII.GetBytes(JSONData); List <YSMatches> Matches = SilkUtility.YaraInstance.ScanMemory(JSONByteArray, SilkUtility.YaraRules, null, 0); SilkUtility.YaraRuleMatches.Clear(); if (Matches.Count != 0) { foreach (YSMatches Match in Matches) { SilkUtility.YaraRuleMatches.Add(Match.Rule.Identifier); lock (ConsoleWriterLock) { SilkUtility.ReturnStatusMessage($" -> Yara match: {Match.Rule.Identifier}", ConsoleColor.Magenta); } } // Dynamically update the JSON object -> List<String> YaraRuleMatches JObject obj = JObject.Parse(JSONData); ((JArray)obj["YaraMatch"]).Add(SilkUtility.YaraRuleMatches); JSONData = obj.ToString(Newtonsoft.Json.Formatting.None); } } if (YaraOptions == YaraOptions.All || YaraOptions == YaraOptions.None || (YaraScan != String.Empty && SilkUtility.YaraRuleMatches.Count > 0)) { //--[Return Codes] // 0 == OK // 1 == File write failed // 2 == URL POST request failed // 3 == Eventlog write failed //-- // Process JSON if (OutputType == OutputType.file) { try { if (!File.Exists(Path)) { File.WriteAllText(Path, (JSONData + Environment.NewLine)); } else { File.AppendAllText(Path, (JSONData + Environment.NewLine)); } return(0); } catch { return(1); } } else if (OutputType == OutputType.url) { try { string responseFromServer = string.Empty; HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(Path); webRequest.Timeout = 10000; // 10 second timeout webRequest.Method = "POST"; webRequest.ContentType = "application/json"; webRequest.Accept = "application/json"; using (var streamWriter = new StreamWriter(webRequest.GetRequestStream())) { streamWriter.Write(JSONData); streamWriter.Flush(); streamWriter.Close(); } var httpResponse = (HttpWebResponse)webRequest.GetResponse(); using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { var result = streamReader.ReadToEnd(); } return(0); } catch { return(2); } } else { Boolean WriteEvent = WriteEventLogEntry(JSONData, EventLogEntryType.Information, EventIds.Event, Path); if (WriteEvent) { return(0); } else { return(3); } } } else { return(0); } }
public static void StartTrace(CollectorType CollectorType, ulong TraceKeywords, OutputType OutputType, String Path, FilterOption FilterOption, Object FilterValue, String YaraScan, YaraOptions YaraOptions, String ProviderName = "", UserTraceEventLevel UserTraceEventLevel = UserTraceEventLevel.Informational) { // Is elevated? if (TraceEventSession.IsElevated() != true) { SilkUtility.ReturnStatusMessage("[!] The collector must be run as Administrator..", ConsoleColor.Red); return; } // Print status SilkUtility.ReturnStatusMessage("[>] Starting trace collector (Ctrl-c to stop)..", ConsoleColor.Yellow); SilkUtility.ReturnStatusMessage("[?] Events captured: 0", ConsoleColor.Green); // We will update this dynamically // The kernel collector has naming requirements if (CollectorType == CollectorType.Kernel) { SilkUtility.EventParseSessionName = KernelTraceEventParser.KernelSessionName; } else { // We add a GUID in case of concurrent SilkETW execution String RandId = Guid.NewGuid().ToString(); SilkUtility.EventParseSessionName = ("SilkETWUserCollector_" + RandId); } // Create trace session using (var TraceSession = new TraceEventSession(SilkUtility.EventParseSessionName)) { // The collector cannot survive process termination (safeguard) TraceSession.StopOnDispose = true; // Create event source using (var EventSource = new ETWTraceEventSource(SilkUtility.EventParseSessionName, TraceEventSourceType.Session)) { // Ctrl-c callback handler SilkUtility.SetupCtrlCHandler(() => { TerminateCollector(); }); // A DynamicTraceEventParser can understand how to read the embedded manifests that occur in the dataStream var EventParser = new DynamicTraceEventParser(EventSource); // Loop events as they arrive EventParser.All += delegate(TraceEvent data) { // It's a bit ugly but ... ¯\_(ツ)_/¯ if (FilterOption != FilterOption.None) { if (FilterOption == FilterOption.Opcode && (byte)data.Opcode != (byte)FilterValue) { SilkUtility.ProcessEventData = false; } else if (FilterOption == FilterOption.ProcessID && data.ProcessID != (UInt32)FilterValue) { SilkUtility.ProcessEventData = false; } else if (FilterOption == FilterOption.ProcessName && data.ProcessName != (String)FilterValue) { SilkUtility.ProcessEventData = false; } else if (FilterOption == FilterOption.EventName && data.EventName != (String)FilterValue) { SilkUtility.ProcessEventData = false; } else { SilkUtility.ProcessEventData = true; } } else { SilkUtility.ProcessEventData = true; } // Only process/serialize events if they match our filter if (SilkUtility.ProcessEventData) { // Display running event count SilkUtility.RunningEventCount += 1; SilkUtility.UpdateEventCount("[?] Events captured: " + SilkUtility.RunningEventCount); var eRecord = new EventRecordStruct { ProviderGuid = data.ProviderGuid, YaraMatch = new List <String>(), ProviderName = data.ProviderName, EventName = data.EventName, Opcode = data.Opcode, OpcodeName = data.OpcodeName, TimeStamp = data.TimeStamp, ThreadID = data.ThreadID, ProcessID = data.ProcessID, ProcessName = data.ProcessName, PointerSize = data.PointerSize, EventDataLength = data.EventDataLength }; var EventProperties = new Hashtable(); // Try to parse event XML try { StringReader XmlStringContent = new StringReader(data.ToString()); XmlTextReader EventElementReader = new XmlTextReader(XmlStringContent); while (EventElementReader.Read()) { for (int AttribIndex = 0; AttribIndex < EventElementReader.AttributeCount; AttribIndex++) { EventElementReader.MoveToAttribute(AttribIndex); EventProperties.Add(EventElementReader.Name, EventElementReader.Value); } } } catch { // For debugging (?), never seen this fail EventProperties.Add("XmlEventParsing", "false"); } eRecord.XmlEventData = EventProperties; // Serialize to JSON String JSONEventData = Newtonsoft.Json.JsonConvert.SerializeObject(eRecord); int ProcessResult = SilkUtility.ProcessJSONEventData(JSONEventData, OutputType, Path, YaraScan, YaraOptions); // Verify that we processed the result successfully if (ProcessResult != 0) { if (ProcessResult == 1) { SilkUtility.ReturnStatusMessage("[!] The collector failed to write to file", ConsoleColor.Red); } else { SilkUtility.ReturnStatusMessage("[!] The collector failed to POST the result", ConsoleColor.Red); } // Shut down the collector TerminateCollector(); } } }; // Specify the providers details if (CollectorType == CollectorType.Kernel) { TraceSession.EnableKernelProvider((KernelTraceEventParser.Keywords)TraceKeywords); } else { // Note that the collector doesn't know if you specified a wrong provider name, // the only tell is that you won't get any events ;) TraceSession.EnableProvider(ProviderName, (TraceEventLevel)UserTraceEventLevel, TraceKeywords); } // Continuously process all new events in the data source EventSource.Process(); // Helper to clean up colloector void TerminateCollector() { EventSource.StopProcessing(); TraceSession?.Stop(); Console.CursorVisible = true; SilkUtility.ReturnStatusMessage("[+] Collector terminated", ConsoleColor.Green); } } } }
private void OnExecute() { // Print custom help if (Help) { SilkUtility.PrintHelp(); return; } // Print trivia if (Trivia) { SilkUtility.PrintTrivia(); return; } // What type of collector are we creating? if (CollectorType == CollectorType.None) { SilkUtility.ReturnStatusMessage("[!] Select valid collector type (-t|--type)", ConsoleColor.Red); return; } else if (CollectorType == CollectorType.Kernel) { if (KernelKeywords == KernelKeywords.None) { SilkUtility.ReturnStatusMessage("[!] Select valid Kernel keyword (-kk|--kernelkeyword)", ConsoleColor.Red); return; } } else if (CollectorType == CollectorType.User) { if (String.IsNullOrEmpty(ProviderName)) { SilkUtility.ReturnStatusMessage("[!] Specify valid provider name (-pn|--providername)", ConsoleColor.Red); return; } // Check and convert UserKeywords to ulong if (String.IsNullOrEmpty(UserKeywords)) { SilkUtility.ReturnStatusMessage("[!] Specify valid keywords mask (-uk|--userkeyword)", ConsoleColor.Red); return; } else { try { if (UserKeywords.StartsWith("0x")) { SilkUtility.UlongUserKeywords = Convert.ToUInt64(UserKeywords, 16); } else { SilkUtility.UlongUserKeywords = Convert.ToUInt64(UserKeywords); } } catch { SilkUtility.ReturnStatusMessage("[!] Specify valid keywords mask (-uk|--userkeyword)", ConsoleColor.Red); return; } } } // Validate output parameters if (OutputType == OutputType.None) { SilkUtility.ReturnStatusMessage("[!] Select valid output type (-ot|--outputtype)", ConsoleColor.Red); return; } else { if (OutputType == OutputType.file) { if (String.IsNullOrEmpty(Path)) { SilkUtility.ReturnStatusMessage("[!] Specify valid output file (-p|--path)", ConsoleColor.Red); return; } else { try { FileAttributes CheckAttrib = File.GetAttributes(Path); if (CheckAttrib.HasFlag(FileAttributes.Directory)) { SilkUtility.ReturnStatusMessage("[!] Specify an output filepath not a directory (-p|--path)", ConsoleColor.Red); return; } } catch { } if (!(Directory.Exists(System.IO.Path.GetDirectoryName(Path)))) { SilkUtility.ReturnStatusMessage("[!] Invalid path specified (-p|--path)", ConsoleColor.Red); return; } else { if (!(SilkUtility.DirectoryHasPermission(System.IO.Path.GetDirectoryName(Path), System.Security.AccessControl.FileSystemRights.Write))) { SilkUtility.ReturnStatusMessage("[!] No write access to output path (-p|--path)", ConsoleColor.Red); return; } } } } else { if (String.IsNullOrEmpty(Path)) { SilkUtility.ReturnStatusMessage("[!] Specify valid URL (-p|--path)", ConsoleColor.Red); return; } else { Uri uriResult; bool UrlResult = Uri.TryCreate(Path, UriKind.Absolute, out uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); if (!UrlResult) { SilkUtility.ReturnStatusMessage("[!] Invalid URL specified (-p|--path)", ConsoleColor.Red); return; } } } } // Validate filter options // None, EventName, ProcessID, ProcessName, Opcode if (FilterOption != FilterOption.None) { if (String.IsNullOrEmpty(FilterValue)) { SilkUtility.ReturnStatusMessage("[!] Specify a valid filter value (-fv|--filtervalue) in conjunction with -f", ConsoleColor.Red); return; } if (FilterOption == FilterOption.ProcessID) { try { SilkUtility.FilterValueObject = Convert.ToUInt32(FilterValue); } catch { SilkUtility.ReturnStatusMessage("[!] Specify a valid ProcessID", ConsoleColor.Red); return; } } else if (FilterOption == FilterOption.Opcode) { try { SilkUtility.FilterValueObject = byte.Parse(FilterValue); if ((byte)SilkUtility.FilterValueObject > 9) { SilkUtility.ReturnStatusMessage("[!] Opcode outside valid range (0-9)", ConsoleColor.Red); return; } } catch { SilkUtility.ReturnStatusMessage("[!] Specify a valid Opcode", ConsoleColor.Red); return; } } else { SilkUtility.FilterValueObject = FilterValue; } } // Validate Yara folder path if (YaraScan != String.Empty) { try { FileAttributes CheckAttrib = File.GetAttributes(YaraScan); if (!(CheckAttrib.HasFlag(FileAttributes.Directory))) { SilkUtility.ReturnStatusMessage("[!] Specified path is not a folder (-y|--yara)", ConsoleColor.Red); return; } else { List <string> YaraRuleCollection = Directory.GetFiles(YaraScan, "*.yar", SearchOption.AllDirectories).ToList(); if (YaraRuleCollection.Count == 0) { SilkUtility.ReturnStatusMessage("[!] Yara folder path does not contain any *.yar files (-y|--yara)", ConsoleColor.Red); return; } else { // We already initialize yara for performace, // new rules can not be added at runtime. SilkUtility.YaraInstance = new YSInstance(); SilkUtility.YaraContext = new YSContext(); SilkUtility.YaraCompiler = SilkUtility.YaraInstance.CompileFromFiles(YaraRuleCollection, null); SilkUtility.YaraRules = SilkUtility.YaraCompiler.GetRules(); YSReport YaraReport = SilkUtility.YaraCompiler.GetErrors(); if (!(YaraReport.IsEmpty())) { SilkUtility.ReturnStatusMessage("[!] The following yara errors were detected (-y|--yara)", ConsoleColor.Red); Dictionary <string, List <string> > Errors = YaraReport.Dump(); foreach (KeyValuePair <string, List <string> > Error in Errors) { SilkUtility.ReturnStatusMessage("==> " + Error.Key, ConsoleColor.Yellow); foreach (String ErrorMsg in Error.Value) { SilkUtility.ReturnStatusMessage(" + " + ErrorMsg, ConsoleColor.Yellow); } } return; } } } } catch { SilkUtility.ReturnStatusMessage("[!] Specify a valid yara rule folder path (-y|--yara)", ConsoleColor.Red); return; } if (YaraOptions == YaraOptions.None) { SilkUtility.ReturnStatusMessage("[!] Specify a valid yara option (-yo|--yaraoptions)", ConsoleColor.Red); return; } } // We passed all collector parameter checks SilkUtility.ReturnStatusMessage("[+] Collector parameter validation success..", ConsoleColor.Green); // Launch the collector if (CollectorType == CollectorType.Kernel) { ETWCollector.StartTrace(CollectorType, (ulong)KernelKeywords, OutputType, Path, FilterOption, SilkUtility.FilterValueObject, YaraScan, YaraOptions); } else { ETWCollector.StartTrace(CollectorType, SilkUtility.UlongUserKeywords, OutputType, Path, FilterOption, SilkUtility.FilterValueObject, YaraScan, YaraOptions, ProviderName, UserTraceEventLevel); } }