diff --git a/RECmd/Program.cs b/RECmd/Program.cs index 00bc4ae..b91edf7 100644 --- a/RECmd/Program.cs +++ b/RECmd/Program.cs @@ -163,7 +163,7 @@ private static async Task Main(string[] args) new Option( "--csv", - "Directory to save CSV formatted results to. Required when -bn is used"), + "Directory to save CSV formatted results to"), new Option( "--csvf", @@ -342,6 +342,13 @@ private static void DoWork(string d, string f, string kn, string vn, string bn, ActiveDateTimeFormat = dt; + ServiceStack.Text.JsConfig.SerializeFn = time => time?.ToString(dt, CultureInfo.InvariantCulture); + ServiceStack.Text.JsConfig.SerializeFn = time => time.ToString(dt, CultureInfo.InvariantCulture); + ServiceStack.Text.JsConfig.SerializeFn = time => time?.ToString(dt, CultureInfo.InvariantCulture); + ServiceStack.Text.JsConfig.SerializeFn = time => time.ToString(dt, CultureInfo.InvariantCulture); + ServiceStack.Text.JsConfig.IncludeNullValues = true; + ServiceStack.Text.JsConfig.IncludeNullValuesInDictionaries = true; + var formatter = new DateTimeOffsetFormatter(CultureInfo.CurrentCulture); @@ -428,9 +435,15 @@ private static void DoWork(string d, string f, string kn, string vn, string bn, return; } - if (csv.IsNullOrEmpty()) + if (csv.IsNullOrEmpty() && json.IsNullOrEmpty()) + { + Log.Error("Either {S1} or {S2} is required when using {S3}. Exiting","--csv","--json","--bn"); + return; + } + + if (csv.IsNullOrEmpty() == false && json.IsNullOrEmpty() == false) { - Log.Error("{S1} is required when using {S2}. Exiting","--csv","--bn"); + Log.Error("Only use one of {S1} or {S2} when using {S3}. Exiting", "--csv", "--json", "--bn"); return; } @@ -1596,7 +1609,7 @@ private static void DoWork(string d, string f, string kn, string vn, string bn, } //end min size option else if (bn?.Length > 0) //batch mode { - ProcessBatch(reBatch, reg, d, f, csv, csvf, dt); + ProcessBatch(reBatch, reg, d, f, csv, csvf, json, jsonf, dt); } } catch (Exception ex) @@ -1637,43 +1650,77 @@ private static void DoWork(string d, string f, string kn, string vn, string bn, Log.Information("Total search time: {TotalSeconds:N3} seconds",totalSeconds); if (_batchCsvOutList.Count > 0) { - if (Directory.Exists(csv) == false) + if (csv.IsNullOrEmpty() == false) { - Log.Information( - "Path to {Csv} doesn't exist. Creating...",csv); - Directory.CreateDirectory(csv); - } + if (Directory.Exists(csv) == false) + { + Log.Information( + "Path to {Csv} doesn't exist. Creating...", csv); + Directory.CreateDirectory(csv); + } - var outName = - $"{RunTimestamp}_RECmd_Batch_{Path.GetFileNameWithoutExtension(bn)}_Output.csv"; + var outName = + $"{RunTimestamp}_RECmd_Batch_{Path.GetFileNameWithoutExtension(bn)}_Output.csv"; - if (csvf.IsNullOrEmpty() == false) - { - outName = Path.GetFileName(csvf); - } + if (csvf.IsNullOrEmpty() == false) + { + outName = Path.GetFileName(csvf); + } - var outFile = Path.Combine(csv, outName); + var outFile = Path.Combine(csv, outName); - Console.WriteLine(); - Log.Information("Saving batch mode CSV file to {OutFile}",outFile); + Console.WriteLine(); + Log.Information("Saving batch mode CSV file to {OutFile}", outFile); - var swCsv = new StreamWriter(outFile, false, Encoding.UTF8); - var csvWriter = new CsvWriter(swCsv, CultureInfo.InvariantCulture); + var swCsv = new StreamWriter(outFile, false, Encoding.UTF8); + var csvWriter = new CsvWriter(swCsv, CultureInfo.InvariantCulture); - var foo = csvWriter.Context.AutoMap(); + var foo = csvWriter.Context.AutoMap(); - foo.Map(t => t.LastWriteTimestamp).Convert(t => - $"{t.Value.LastWriteTimestamp?.ToString(dt)}"); + foo.Map(t => t.LastWriteTimestamp).Convert(t => + $"{t.Value.LastWriteTimestamp?.ToString(dt)}"); - csvWriter.Context.RegisterClassMap(foo); + csvWriter.Context.RegisterClassMap(foo); - csvWriter.WriteHeader(); - csvWriter.NextRecord(); + csvWriter.WriteHeader(); + csvWriter.NextRecord(); - csvWriter.WriteRecords(_batchCsvOutList); + csvWriter.WriteRecords(_batchCsvOutList); + + swCsv.Flush(); + swCsv.Close(); + } - swCsv.Flush(); - swCsv.Close(); + if (json.IsNullOrEmpty() == false) + { + if (Directory.Exists(json) == false) + { + Log.Information( + "Path to {Json} doesn't exist. Creating...",json); + Directory.CreateDirectory(json); + } + + var outName = + $"{RunTimestamp}_RECmd_Batch_{Path.GetFileNameWithoutExtension(bn)}_Output.json"; + + if (jsonf.IsNullOrEmpty() == false) + { + outName = Path.GetFileName(jsonf); + } + + var outFile = Path.Combine(json, outName); + + Console.WriteLine(); + Log.Information("Saving batch mode JSON file to {OutFile}",outFile); + + using (var writer = new StreamWriter(outFile, false, Encoding.UTF8)) + { + foreach (var item in _batchCsvOutList) + { + writer.WriteLine(item.ToJson()); + } + } + } } } @@ -1841,7 +1888,7 @@ private static void UpdateFromRepo() Directory.Delete(Path.Combine(BaseDirectory, "RECmd-master"), true); } - private static void ProcessBatchKey(RegistryKey key, Key batchKey, string hivePath, string d, string f, string csv, string csvf, string dt) + private static void ProcessBatchKey(RegistryKey key, Key batchKey, string hivePath, string d, string f, string csv, string csvf, string json, string jsonf, string dt) { var regKey = key; @@ -1859,7 +1906,7 @@ private static void ProcessBatchKey(RegistryKey key, Key batchKey, string hivePa if (regVal != null) { Log.Verbose("Found value {ValueName} in key {KeyPath}!",batchKey.ValueName,regKey.KeyPath); - BatchDumpKey(key, batchKey, hivePath, d, f, csv, csvf, dt); + BatchDumpKey(key, batchKey, hivePath, d, f, csv, csvf, json, jsonf, dt); } } @@ -1867,7 +1914,7 @@ private static void ProcessBatchKey(RegistryKey key, Key batchKey, string hivePa { //do not need to find a value, Log.Verbose("Found key {KeyPath}!",key.KeyPath); - BatchDumpKey(key, batchKey, hivePath, d, f, csv, csvf, dt); + BatchDumpKey(key, batchKey, hivePath, d, f, csv, csvf, json, jsonf, dt); } @@ -1878,11 +1925,11 @@ private static void ProcessBatchKey(RegistryKey key, Key batchKey, string hivePa foreach (var regKeySubKey in regKey.SubKeys) { - ProcessBatchKey(regKeySubKey, batchKey, hivePath, d, f, csv, csvf, dt); + ProcessBatchKey(regKeySubKey, batchKey, hivePath, d, f, csv, csvf, json, jsonf, dt); } } - private static void ProcessBatch(ReBatch reBatch, RegistryHive regHive, string d, string f, string csv, string csvf, string dt) + private static void ProcessBatch(ReBatch reBatch, RegistryHive regHive, string d, string f, string csv, string csvf, string json, string jsonf, string dt) { foreach (var key in reBatch.Keys) { @@ -1906,7 +1953,7 @@ private static void ProcessBatch(ReBatch reBatch, RegistryHive regHive, string d continue; } - ProcessBatchKey(regKey, key, regHive.HivePath, d, f, csv, csvf, dt); + ProcessBatchKey(regKey, key, regHive.HivePath, d, f, csv, csvf, json, jsonf, dt); } else if (key.KeyPath.Contains("*")) { @@ -1952,7 +1999,7 @@ private static void ProcessBatch(ReBatch reBatch, RegistryHive regHive, string d //BatchDumpKey(regKey, key, regHive.HivePath); //switch to this for better recursive support vs BatchDumpKey - ProcessBatchKey(regKey, key, regHive.HivePath, d, f, csv, csvf, dt); + ProcessBatchKey(regKey, key, regHive.HivePath, d, f, csv, csvf, json, jsonf, dt); } } else @@ -1965,7 +2012,7 @@ private static void ProcessBatch(ReBatch reBatch, RegistryHive regHive, string d continue; } - ProcessBatchKey(regKey, key, regHive.HivePath, d, f, csv, csvf, dt); + ProcessBatchKey(regKey, key, regHive.HivePath, d, f, csv, csvf, json, jsonf, dt); } } } @@ -2044,7 +2091,7 @@ private static List GetPluginsToActivate(RegistryKey regKey return pluginsToActivate; } - private static void BatchDumpKey(RegistryKey regKey, Key key, string hivePath, string d, string f, string csv, string csvf, string dt) + private static void BatchDumpKey(RegistryKey regKey, Key key, string hivePath, string d, string f, string csv, string csvf, string json, string jsonf, string dt) { Log.Verbose("Batch dumping {KeyPath} in {HivePath}",regKey.KeyPath,hivePath); @@ -2066,7 +2113,7 @@ private static void BatchDumpKey(RegistryKey regKey, Key key, string hivePath, s pig.ProcessValues(regKey); - var pluginDetailsFile = DumpPluginValues(pig, hivePath, d, f, csv, csvf); + var pluginDetailsFile = DumpPluginValues(pig, hivePath, d, f, csv, csvf, json, jsonf); var path = hivePath; @@ -2144,7 +2191,7 @@ private static void BatchDumpKey(RegistryKey regKey, Key key, string hivePath, s } } - private static string DumpPluginValues(IRegistryPluginGrid plugin, string hivePath, string d, string f, string csv, string csvf) + private static string DumpPluginValues(IRegistryPluginGrid plugin, string hivePath, string d, string f, string csv, string csvf, string json, string jsonf) { var pluginType = plugin.GetType(); @@ -2169,28 +2216,88 @@ private static string DumpPluginValues(IRegistryPluginGrid plugin, string hivePa var outbase = $"{RunTimestamp}_{pluginType.Name}_{hiveName1}.csv"; - if (Directory.Exists(csv) == false) + if (csv.IsNullOrEmpty() == false && Directory.Exists(csv) == false) { Log.Information( "Path to {Csv} doesn't exist. Creating...",csv); Directory.CreateDirectory(csv); } + if (json.IsNullOrEmpty() == false && Directory.Exists(json) == false) + { + Log.Information( + "Path to {json} doesn't exist. Creating...",json); + Directory.CreateDirectory(json); + } + if (csvf.IsNullOrEmpty() == false) { outbase = $"{Path.GetFileNameWithoutExtension(csvf)}_{pluginType.Name}{Path.GetExtension(csvf)}"; } - var outFile = Path.Combine(csv, RunTimestamp, outbase); + string outputDir = null; + if (json.IsNullOrEmpty() == false) + { + outputDir = json; + } + else if (csv.IsNullOrEmpty() == false) + { + outputDir = csv; + } + else + { + return null; + } - if (Directory.Exists(Path.GetDirectoryName(outFile)) == false) + var outFile = Path.Combine(outputDir, RunTimestamp, outbase); + var outDir = Path.GetDirectoryName(outFile); + if (!string.IsNullOrEmpty(outDir) && !Directory.Exists(outDir)) { - Directory.CreateDirectory(Path.GetDirectoryName(outFile)); + Directory.CreateDirectory(outDir); } var exists = File.Exists(outFile); + if (json.IsNullOrEmpty() == false) + { + outbase = $"{RunTimestamp}_{pluginType.Name}_{hiveName1}.json"; + if (jsonf.IsNullOrEmpty() == false) + { + outbase = + $"{Path.GetFileNameWithoutExtension(jsonf)}_{pluginType.Name}{Path.GetExtension(jsonf)}"; + } + + outFile = Path.Combine(outputDir, RunTimestamp, outbase); + + using (var writer = new StreamWriter(outFile, true, Encoding.UTF8)) + { + if (plugin.Values.Count > 0) + { + var propsToExclude = plugin.Values[0].GetType().GetProperties() + .Where(p => p.Name.StartsWith("BatchValueData")) + .Select(p => p.Name) + .ToList(); + + foreach (var item in plugin.Values) + { + var dict = new Dictionary(); + foreach (var prop in item.GetType().GetProperties()) + { + if (propsToExclude.Contains(prop.Name) == false) + { + dict.Add(prop.Name, prop.GetValue(item)); + } + } + writer.WriteLine(dict.ToJson()); + } + } + } + + return outFile; + } + + using var sw = new StreamWriter(outFile, true, Encoding.UTF8); var csvWriter = new CsvWriter(sw, CultureInfo.InvariantCulture);