Ejemplo n.º 1
0
        /// <summary>
        /// Returns a single XPath query string from the Events, Levels, and Sources found in a LogQuery object.
        /// </summary>
        /// <param name="log">The LogQuery object to be parsed.</param>
        /// <returns></returns>
        public static string BuildXPathQuery(GevLog.LogQuery log)
        {
            string queryStringPrefix  = "";
            string queryStringSuffix  = "";
            string queryStringSources = "";
            string queryStringIds     = "";
            string queryStringLevels  = "";

            if (log.LevelFilter.Count > 0 || log.IdFilter.Count > 0 || log.SourceFilter.Count > 0)
            {
                queryStringPrefix = "*[System[";
                queryStringSuffix = "]]";
            }
            else
            {
                return("*");
            }

            if (log.SourceFilter.Count > 0)
            {
                queryStringSources = PrepareXPathStringFromList(ParseProviders(log.SourceFilter), "Provider[", "Name", "]", true);
            }

            if (log.IdFilter.Count > 0)
            {
                queryStringIds = PrepareXPathStringFromList(log.IdFilter, "(", "EventID", ")");
            }

            if (log.LevelFilter.Count > 0)
            {
                queryStringLevels = PrepareXPathStringFromList(log.LevelFilter, "(", "Level", ")");
            }

            List <string> queryStringList = new List <string>();

            if (queryStringSources.Length > 0)
            {
                queryStringList.Add(queryStringSources);
            }
            if (queryStringIds.Length > 0)
            {
                queryStringList.Add(queryStringIds);
            }
            if (queryStringLevels.Length > 0)
            {
                queryStringList.Add(queryStringLevels);
            }

            // return the final string, when all is said and done it will look something like:
            // *[System[Provider[@Name='Microsoft-Windows-Ntfs' or @Name='Microsoft-Windows-Ntfs-UBPM' or @Name='Ntfs'] and (EventID=1 or EventID=2 or EventID=3) and (Level=1 or Level=2)]]
            return(queryStringPrefix + queryStringList.Aggregate((a, b) => a + " and " + b) + queryStringSuffix);
        }
Ejemplo n.º 2
0
 /// <summary> Display the debugging information about command-line arguments </summary>
 private static void DisplayArguments(GevLog.LogQuery log)
 {
     Console.WriteLine($"\nParsed arguments:\n");
     Console.WriteLine($"Path = \"{log.LogPath}\"");
     Console.WriteLine($"Outfile = \"{log.OutputFile}\"");
     Console.WriteLine($"Date = \"{log.DateRangeFilter}\"");
     Console.WriteLine($"Offset = \"{log.DateOffsetFilter}\"");
     Console.Write("Levels = \"");
     for (int i = 0; i < log.LevelFilter.Count; i++)
     {
         Console.Write(log.LevelFilter[i]);
         if (i < (log.LevelFilter.Count - 1))
         {
             Console.Write(", ");
         }
     }
     Console.Write("\"\n");
     Console.Write("IDs = \"");
     for (int i = 0; i < log.IdFilter.Count; i++)
     {
         Console.Write(log.IdFilter[i]);
         if (i < (log.IdFilter.Count - 1))
         {
             Console.Write(", ");
         }
     }
     Console.Write("\"\n");
     Console.WriteLine($"IDCount = \"{log.IdFilter.Count}\"");
     Console.Write("Sources = \"");
     for (int i = 0; i < log.SourceFilter.Count; i++)
     {
         Console.Write(log.SourceFilter[i]);
         if (i < (log.SourceFilter.Count - 1))
         {
             Console.Write(", ");
         }
     }
     Console.Write("\"");
     Console.WriteLine($"\nSource count: {log.SourceFilter.Count}");
     Console.Write("\n");
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Parses command-line arguments.
        /// </summary>
        /// <param name="arguments">args[] from main()</param>
        /// <param name="log">LogQuery class which stores user filters parsed from arguments.</param>
        /// <returns>The LogQuery object.</returns>
        private static GevLog.LogQuery ParseArguments(string[] arguments, GevLog.LogQuery log)
        {
            // Check for help
            if (arguments.Contains("--help"))
            {
                DisplayHelp(); // This also aborts
            }

            // Dictionary which contains list of acceptable options as keys, and whether the
            // option requires arguments
            // For instance, in: `--path .\hello.evtx`, ".\hello.evtx" is an argument
            var argsDict = new Dictionary <string, bool>()
            {
                { "--path", true },
                { "--debug", false },
                { "--query", false },
                { "--id", true },
                { "--source", true },
                { "--level", true },
                { "--format", true },
                { "--out-file", true },
                { "--direction", true },
                { "--max", true }
            };

            for (int i = 0; i < arguments.Length; i++)
            {
                string key = arguments[i].ToLower();
                if (argsDict.ContainsKey(key))
                {
                    string secondaryParameter = "";

                    // if the Value for the Key is "true", it means we need to look for arguments
                    if (argsDict[key])
                    {
                        secondaryParameter = arguments[i + 1];

                        i++; // In the event that a secondary parameter is needed and found, we need to increment
                             // `i` again so the next iteration will find the next key, and not the previous
                             // key's parameter
                    }

                    switch (key)
                    {
                    case "--":
                        return(log);    // Stop parsing arguments

                    ///////////////////// Path //////////////////////
                    case "--path":
                        string pathArgument = secondaryParameter;

                        if (File.Exists(pathArgument))
                        {
                            // Looks good, assign value
                            log.LogPath = pathArgument;
                        }
                        else
                        {
                            if (File.Exists($"{pathArgument}.evtx"))
                            {
                                // Append .evtx
                                log.LogPath = ($"{pathArgument}.evtx");
                            }
                            else
                            {
                                // It's not valid
                                AbortGev($"Invalid path: \"{pathArgument}\"");
                            }
                        }
                        break;

                    //////////////////// Debug /////////////////////
                    case "--debug":
                        log.DebugMode = true;
                        break;

                    //////////////////// Query /////////////////////
                    case "--query":
                        log.QuerySet = true;
                        return(log);    // Stop parsing arguments

                    ///////////////////// IDs //////////////////////
                    case "--id":
                        string idArgument = secondaryParameter;
                        if (!string.IsNullOrEmpty(idArgument))
                        {
                            if (idArgument.Split(',').Length > 5)
                            {
                                // Too many IDs (max 5), abort
                                AbortGev("Too many IDs. Max 5.");
                            }
                            else
                            {
                                // Iterate through each
                                foreach (var id in idArgument.Split(','))
                                {
                                    if (int.TryParse(id, out int idSubValue))
                                    {
                                        if (idSubValue < 1 || idSubValue > 65535)
                                        {
                                            // It is an int, but it's outside 1-65535
                                            AbortGev($"Invalid ID: \"{idSubValue}\" Please input a value between 1 and 65535.");
                                        }
                                        else
                                        {
                                            // All is good, add to list
                                            log.IdFilter.Add(idSubValue);
                                        }
                                    }
                                    else
                                    {
                                        // It isn't an int, abort
                                        AbortGev($"Invalid ID. \"{id}\" is not an integer.");
                                    }
                                }
                            }
                            // Check to see we ended up with any ids
                            if (log.IdFilter.Count < 1)
                            {
                                // Something broke (we had an --id flag but there was a problem parsing IDs)
                                AbortGev("There was a problem parsing IDs. Valid IDs are separated by commas without spaces.");
                            }
                        }
                        break;

                    /////////////////// Sources ////////////////////
                    case "--source":
                        string sourceArgument = secondaryParameter;

                        if (!string.IsNullOrEmpty(sourceArgument))
                        {
                            if (sourceArgument.Split(',').Length > 5)
                            {
                                // Too many sources (max 5), abort
                                AbortGev("Too many sources. Max 5.");
                            }

                            // Iterate through each and add to the source list
                            foreach (var source in sourceArgument.Split(','))
                            {
                                log.SourceFilter.Add(source);
                            }

                            // Check to see we ended up with any ids
                            if (log.SourceFilter.Count < 1)
                            {
                                // Something broke (we had a --source flag but there was a problem parsing sources)
                                AbortGev("There was a problem parsing sources. Valid sources are separated by commas without spaces.");
                            }
                        }
                        break;

                    //////////////////// Level /////////////////////
                    case "--level":
                        string levelArgument = secondaryParameter;

                        if (!String.IsNullOrEmpty(levelArgument))
                        {
                            if (levelArgument.Split(',').Length > 5)
                            {
                                // Too many levels (max 5), abort
                                AbortGev("Too many levels.  Max 5.");
                            }
                            else
                            {
                                // Iterate through each
                                foreach (var level in levelArgument.Split(','))
                                {
                                    if (int.TryParse(level, out int levelSubValue))
                                    {
                                        if (levelSubValue < 1 || levelSubValue > 5)
                                        {
                                            // It is an int, but it's outside 1-5
                                            AbortGev($"Invalid event level: \"{levelSubValue}\" Please input a value between 1 and 5.");
                                        }
                                        else
                                        {
                                            // All is good, add to list
                                            log.LevelFilter.Add(levelSubValue);
                                        }
                                    }
                                    else
                                    {
                                        // It isn't an int, abort
                                        AbortGev($"Invalid level. \"{level}\" is not an integer.");
                                    }
                                }
                            }

                            // Check to see we ended up with any levels
                            if (log.LevelFilter.Count < 1)
                            {
                                // Something broke (we had an --level flag but there was a problem parsing levels)
                                AbortGev("There was a problem parsing IDs.  Valid IDs are separated by commas without spaces.");
                            }
                        }
                        break;

                    /////////////////// Format /////////////////////
                    case "--format":
                        string formatArgument = secondaryParameter;

                        if (!string.IsNullOrEmpty(formatArgument))
                        {
                            // Check for case-insensitive strings
                            if (formatArgument.Equals("xml", StringComparison.InvariantCultureIgnoreCase))
                            {
                                log.Format = "xml";     // xml
                            }
                            else if (formatArgument.Equals("html", StringComparison.InvariantCultureIgnoreCase))
                            {
                                log.Format = "html";     // html
                            }
                            else if (formatArgument.Equals("text", StringComparison.InvariantCultureIgnoreCase))
                            {
                                log.Format = "text";     // text
                            }
                            else if (formatArgument.Equals("json", StringComparison.InvariantCultureIgnoreCase))
                            {
                                log.Format = "json";     // json
                            }
                            else
                            {
                                // They didn't supply 'xml', 'html', 'text', or 'json'
                                AbortGev("There was a problem parsing format.  Valid formats are 'xml', 'html', 'text', or 'json'.");
                            }
                        }
                        break;

                    ///////////////// Output File //////////////////
                    case "--out-file":
                        string outfileArgument = secondaryParameter;

                        if (!string.IsNullOrEmpty(outfileArgument))
                        {
                            // Parse format:
                            switch (log.Format)
                            {
                            case "text":          // Defaults to .txt
                                if (outfileArgument.EndsWith(".txt"))
                                {
                                    log.OutputFile = outfileArgument;
                                }
                                else
                                {
                                    log.OutputFile = (outfileArgument + ".txt");
                                }
                                break;

                            case "xml":         // xml
                                if (outfileArgument.EndsWith(".xml"))
                                {
                                    log.OutputFile = outfileArgument;
                                }
                                else
                                {
                                    log.OutputFile = (outfileArgument + ".xml");
                                }
                                break;

                            case "html":         // html
                                if (outfileArgument.EndsWith(".html"))
                                {
                                    log.OutputFile = outfileArgument;
                                }
                                else
                                {
                                    log.OutputFile = (outfileArgument + ".html");
                                }
                                break;

                            case "json":         // json
                                if (outfileArgument.EndsWith(".json"))
                                {
                                    log.OutputFile = outfileArgument;
                                }
                                else
                                {
                                    log.OutputFile = (outfileArgument + ".json");
                                }
                                break;

                            default:         // Something broke
                                AbortGev("There was an error when parsing output file.");
                                break;
                            }

                            // If the file exists, back it up
                            if (File.Exists(log.OutputFile))
                            {
                                // Grab file name (base + extension)
                                string fileBase = Path.GetFileNameWithoutExtension(log.OutputFile);
                                string fileExt  = Path.GetExtension(log.OutputFile);

                                // Random number
                                string randomNumber;
                                Random random = new Random();

                                // Keep trying to rename the file until we get one that doesn't exist.
                                do
                                {
                                    // Create string to prepend
                                    string prependString = String.Empty;

                                    // Create random number between 1-10000
                                    randomNumber = random.Next(10000).ToString();

                                    // If the nmber is < 10000, prepend '0's to the string to make it 5 characters long.
                                    if (randomNumber.Length < 5)
                                    {
                                        for (int x = 0; x + randomNumber.Length < 5; x++)
                                        {
                                            prependString += "0";
                                        }
                                    }
                                    log.OutputFile = (fileBase + "-" + prependString + randomNumber + fileExt);
                                } while (File.Exists(log.OutputFile));

                                // Create it
                                File.Create(log.OutputFile).Close();
                            }
                            else
                            {
                                File.Create(log.OutputFile).Close();
                            }
                        }
                        break;

                    ////////////////// Direction ///////////////////
                    case "--direction":
                        string directionArgument = secondaryParameter;

                        if (!String.IsNullOrEmpty(directionArgument))
                        {
                            if (int.TryParse(directionArgument, out int direction))
                            {
                                if (direction < 1 || direction > 2)
                                {
                                    // It is an int, but outside 1-2
                                    AbortGev($"Invalid direction: \"{direction}\" Please input either 1 or 2.");
                                }
                                else
                                {
                                    // Everything looks good, we've gotten a valid direction.
                                    log.Direction = direction;
                                }
                            }
                            else
                            {
                                // It's not an integer
                                AbortGev($"Invalid direction. \"{directionArgument}\" is not an integer.");
                            }
                        }
                        break;

                    //////////////// Maximum Events ////////////////
                    case "--max":
                        string maxEventsArgument = secondaryParameter;

                        if (!String.IsNullOrEmpty(maxEventsArgument))
                        {
                            if (int.TryParse(maxEventsArgument, out int maxEvents))
                            {
                                if (maxEvents < 1)
                                {
                                    // Doesn't make sense
                                    AbortGev($"Invalid maximum events: \"{maxEvents}\" Please input a number greater than 0.");
                                }
                                else
                                {
                                    // Everything looks good, we've gotten a valid maximum
                                    Globals.MaxEvents = maxEvents;
                                }
                            }
                        }
                        break;
                    }
                }
                else
                {
                    // Unknown argument
                    AbortGev($"Unknown argument {arguments[i]}");
                }
            }

            // All done
            return(log);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Outputs event log records
        /// </summary>
        /// <param name="record">Event log record to output</param>
        /// <param name="outFile">File to output it to</param>
        /// <param name="format">Type of output -> 1 = XML - 2 = HTML - 3 = TEXT</param>
        /// <returns>Number of records found so far</returns>
        private static void OutputRecords(List <GevLog.Record> eventLogRecords, GevLog.LogQuery log)
        {
            string outString = "";

            /* JsonSerializer and XmlSerializer can turn the whole object into a JSON string
             * so we don't need to iterate through each record for "xml" or "json", but
             * we will for "text" and "html"
             */

            /*==================== JSON ====================*/
            if (log.Format == "json")
            {
                outString = JsonSerializer.Serialize(eventLogRecords, new JsonSerializerOptions()
                {
                    WriteIndented = true
                });
            }

            /*==================== XML ====================*/
            else if (log.Format == "xml")
            {
                using (var stringWriter = new StringWriter())
                {
                    var xmlSerializer = new XmlSerializer(eventLogRecords.GetType());
                    xmlSerializer.Serialize(stringWriter, eventLogRecords);
                    outString = stringWriter.ToString();
                }
            }
            else
            {
                foreach (GevLog.Record record in eventLogRecords)
                {
                    /*==================== Text ====================*/
                    if (log.Format == "text")
                    {
                        outString +=
                            $"---------------------------------------------------------------------------------\n" +
                            $"Id                 : {record.Id}\n" +
                            $"LevelDisplayName   : {record.LevelDisplayName}\n" +
                            $"Level              : {record.Level}\n" +
                            $"TimeCreated        : {record.TimeCreated}\n" +
                            $"ProviderName       : {record.ProviderName}\n" +
                            $"FormatDescription  : {record.FormatDescription}\n" +
                            $"Records            : {Globals.TotalRecords}\n" +
                            $"---------------------------------------------------------------------------------" +
                            "\n\n";
                    }

                    /*==================== HTML ====================*/
                    else if (log.Format == "html")
                    {
                        // This is specifically used by the gev_web application using a customized CSS style sheet.
                        if (Globals.TotalRecords < 1)
                        {
                            // Formulate output and adds header
                            outString += $"<table class=\"tg\" style=\"width:100%\">" +
                                         $"    <colgroup>" +
                                         $"        <col style=\"width:15%\">\n" +
                                         $"        <col style=\"width:15%\">\n" +
                                         $"        <col style=\"width:30%\">\n" +
                                         $"        <col style=\"width:10%\">\n" +
                                         $"    </colgroup>\n" +
                                         $"    <tr>\n" +
                                         $"        <th class=\"tg-h\">Level</th>\n" +
                                         $"        <th class=\"tg-h\">Date</th>\n" +
                                         $"        <th class=\"tg-h\">Source</th>\n" +
                                         $"        <th class=\"tg-h\">EventID</th>\n" +
                                         $"    </tr>\n" +
                                         $"    <tr>\n" +
                                         $"        <td class=\"tg-1\">{record.LevelDisplayName}</td>\n" +
                                         $"        <td class=\"tg-1\">{record.TimeCreated}</td>\n" +
                                         $"        <td class=\"tg-1\">{record.ProviderName}</td>\n" +
                                         $"        <td class=\"tg-1\">{record.Id}</td>\n" +
                                         $"    </tr>\n" +
                                         $"    <tr>\n" +
                                         $"        <td  class=\"tg-2\" colspan=\"4\"><pre>{record.FormatDescription}</pre></td>\n" +
                                         $"    </tr>\n" +
                                         $"</table>\n" +
                                         $"<br>\n";
                        }
                        else
                        {
                            // Formulates output without header
                            outString += $"<table class=\"tg\" style=\"width:100%\">" +
                                         $"    <colgroup>" +
                                         $"        <col style=\"width:15%\">\n" +
                                         $"        <col style=\"width:15%\">\n" +
                                         $"        <col style=\"width:30%\">\n" +
                                         $"        <col style=\"width:10%\">\n" +
                                         $"    </colgroup>\n" +
                                         $"    <tr>\n" +
                                         $"        <td class=\"tg-1\">{record.LevelDisplayName}</td>\n" +
                                         $"        <td class=\"tg-1\">{record.TimeCreated}</td>\n" +
                                         $"        <td class=\"tg-1\">{record.ProviderName}</td>\n" +
                                         $"        <td class=\"tg-1\">{record.Id}</td>\n" +
                                         $"    </tr>\n" +
                                         $"    <tr>\n" +
                                         $"        <td  class=\"tg-2\" colspan=\"4\"><pre>{record.FormatDescription}</pre></td>\n" +
                                         $"    </tr>\n" +
                                         $"</table>\n" +
                                         $"<br>\n";
                        }
                    }

                    /* Something bad did indeed happen */
                    else
                    {
                        AbortGev("Unable to determine format.");
                    }
                }
            }
            // Either output the records to a file, or to the console
            if (!string.IsNullOrEmpty(log.OutputFile))
            {
                // Write output to a file
                File.AppendAllText(log.OutputFile, outString);
            }
            else
            {
                // Write output to screen
                Console.Write($"{outString}\n");
            }
        }
Ejemplo n.º 5
0
        private static void Main(string[] args)
        {
            // Stopwatch for debugging
            Stopwatch stopWatch = new Stopwatch();

            // Instantiate a new object that holds query information
            GevLog.LogQuery log = new GevLog.LogQuery()
            {
                LogPath = ""
            };

            // Holds a list of all the records we have found
            var eventLogRecords = new List <GevLog.Record>();

            // If user has not supplied any arguments, we'll display the help text
            if (args.Length == 0)
            {
                DisplayHelp();
            }
            else
            {
                // parse arguments
                log = ParseArguments(args, log);
            }

            // DEBUG MODE
            if (log.DebugMode)
            {
                // Start the System.Diagnostics.Stopwatch to see how long the program takes to run
                stopWatch.Start();

                // Print all arguments to the console
                DisplayArguments(log);
            }

            // Since we have parsed arguments, check to see if they supplied a path
            if (log.LogPath.Length < 1)
            {
                if (log.QuerySet)
                {
                    AbortGev("Query flag was found, but no path was given.  Please set the path before the query flag.");
                }
                else
                {
                    AbortGev("No path to archived log found.");
                }
            }

            // If we have any Levels or IDs we'll need a prefix and suffix, otherwise the queryString should just be '*'
            string queryString = XPathBuilder.BuildXPathQuery(log);

            if (log.DebugMode)
            {
                Console.WriteLine($"\n{queryString}");
            }

            // Get our records that match our XPath query
            EventRecords(LogRecordCollection(log.LogPath, log.Direction, queryString), eventLogRecords);

            // If there were no records found, we'll alert the user
            if (Globals.TotalRecords < 1)
            {
                Console.WriteLine("No records found matching search criteria.");
            }
            else
            {
                OutputRecords(eventLogRecords, log);

                // Metrics -- only for debugging
                if (log.DebugMode)
                {
                    stopWatch.Stop();
                    Console.WriteLine("Program run in " + stopWatch.ElapsedMilliseconds.ToString() +
                                      "ms with " + Globals.TotalRecords + " records found.");
                }
            }
        }