public static void LaunchFileEnumerator(FileEnumeratorParameters parameters) { ThrowIfParametersInvalid(parameters); var fileEnumerationDelegate = new Func <FileEnumeratorParameters, List <FailSuccessCount> >((args) => ThreadLauncher(args)); if (!parameters.DisableWorkerThread) { Task <List <FailSuccessCount> > enumerationTask = Task.Run(() => { List <FailSuccessCount> results = new List <FailSuccessCount>(); results.AddRange(fileEnumerationDelegate.Invoke(parameters)); return(results); }, parameters.CancelToken); Task reportResultsTask = enumerationTask.ContinueWith((antecedent) => parameters.ReportResultsFunction(antecedent.Result)); } else { List <FailSuccessCount> results = fileEnumerationDelegate.Invoke(parameters); parameters.ReportResultsFunction(results); } }
private static List <FailSuccessCount> ThreadLauncher(FileEnumeratorParameters parameters) { fileEnumCount = new FailSuccessCount("OS files enumerated"); databaseInsertCount = new FailSuccessCount("OS database rows updated"); directoryEnumCount = new FailSuccessCount("directories enumerated"); try { char driveLetter = parameters.SelectedFolder[0]; Queue <string> folders = new Queue <string>(new string[] { parameters.SelectedFolder }); while (folders.Count > 0) { parameters.CancelToken.ThrowIfCancellationRequested(); string currentDirectory = folders.Dequeue(); // Get all _FILES_ inside folder IEnumerable <FileProperties> properties = EnumerateFileProperties(parameters, driveLetter, currentDirectory); foreach (FileProperties prop in properties) { parameters.CancelToken.ThrowIfCancellationRequested(); // INSERT file properties into _DATABASE_ bool insertResult = FilePropertiesAccessLayer.InsertFileProperties(prop); if (insertResult) { databaseInsertCount.IncrementSucceededCount(); } else { databaseInsertCount.IncrementFailedCount(); parameters.CancelToken.ThrowIfCancellationRequested(); continue; } parameters.CancelToken.ThrowIfCancellationRequested(); } // Get all _FOLDERS_ at this depth inside this folder IEnumerable <NtfsDirectory> nestedDirectories = MftHelper.GetDirectories(driveLetter, currentDirectory); foreach (NtfsDirectory directory in nestedDirectories) { parameters.CancelToken.ThrowIfCancellationRequested(); string dirPath = Path.Combine(currentDirectory, directory.Name); folders.Enqueue(dirPath); directoryEnumCount.IncrementSucceededCount(); parameters.CancelToken.ThrowIfCancellationRequested(); } } } catch (OperationCanceledException) { } return(new List <FailSuccessCount> { fileEnumCount, databaseInsertCount, directoryEnumCount }); }
public BackgroundEnumerationThread(FileEnumeratorParameters parameters) { configuration = parameters; cancelToken = configuration.CancelToken; cancelToken.Register(() => CancelBackgroundWorker()); reportProgressFuntionDelegate = configuration.ReportOutputFunction; completedFuntionDelegate = configuration.ReportResultsFunction; worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.WorkerSupportsCancellation = true; worker.RunWorkerCompleted += OnRunWorkerCompleted; worker.ProgressChanged += Worker_ProgressChanged; worker.DoWork += OnDoWork; }
public static void LaunchFileEnumerator(FileEnumeratorParameters parameters) { var fileEnumerationDelegate = new Func <FileEnumeratorParameters, FileEnumeratorReport>((args) => Worker(args)); if (!parameters.DisableWorkerThread) { Task <FileEnumeratorReport> enumerationTask = Task.Run(() => { FileEnumeratorReport results = fileEnumerationDelegate.Invoke(parameters); return(results); }, parameters.CancelToken); Task reportResultsTask = enumerationTask.ContinueWith((antecedent) => parameters.ReportResultsFunction(antecedent.Result)); } else { FileEnumeratorReport results = fileEnumerationDelegate.Invoke(parameters); parameters.ReportResultsFunction(results); } }
private void btnSearch_Click(object sender, EventArgs e) { if (ProcessingToggle.IsActive) { btnSearch.Enabled = false; ProcessingToggle.SetState(false); } else { btnSearch.Text = "Cancel"; ProcessingToggle.SetState(true); bool calculateEntropy = checkboxCalculateEntropy.Checked; bool onlineCertValidation = checkboxOnlineCertValidation.Checked; string selectedFolder = tbPath.Text; string searchPatterns = tbSearchPatterns.Text; string yaraRulesPath = ""; if (checkBoxYaraRules.Checked) { yaraRulesPath = tbYaraRuleFile.Text; } FileEnumeratorParameters parameters = new FileEnumeratorParameters(cancelToken, Settings.FileEnumeration_DisableWorkerThread, selectedFolder, searchPatterns, calculateEntropy, onlineCertValidation, yaraRulesPath, ReportOutput, LogOutput, ReportNumbers, LogExceptionMessage); tbOutput.AppendText(Environment.NewLine); ReportOutput($"Beginning Enumeration of folder: \"{selectedFolder}\""); enumerationStart = DateTime.Now; FileEnumerator.LaunchFileEnumerator(parameters); } }
public static IEnumerable <FileProperties> EnumerateFileProperties(FileEnumeratorParameters parameters, char driveLetter, string currentDirectory) { foreach (NtfsFile file in MftHelper.EnumerateFiles(driveLetter, currentDirectory)) { parameters.CancelToken.ThrowIfCancellationRequested(); // File _PATTERN MATCHING_ if (FileMatchesPattern(file, parameters.SearchPatterns)) { string message = $"MFT File: {Path.Combine(currentDirectory, file.Name)}"; if (parameters.LogOutputFunction != null) { parameters.LogOutputFunction.Invoke(message); } if (parameters.ReportOutputFunction != null) { parameters.ReportOutputFunction.Invoke(message); } fileEnumCount.IncrementSucceededCount(); parameters.CancelToken.ThrowIfCancellationRequested(); FileProperties prop = new FileProperties(); prop.PopulateFileProperties(parameters, driveLetter, file); parameters.CancelToken.ThrowIfCancellationRequested(); yield return(prop); } else { fileEnumCount.IncrementFailedCount(); parameters.CancelToken.ThrowIfCancellationRequested(); } } yield break; }
private static void ThrowIfParametersInvalid(FileEnumeratorParameters parameters) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (parameters.SearchPatterns == null) { throw new ArgumentNullException(nameof(parameters.SearchPatterns)); } if (parameters.ReportExceptionFunction == null) { throw new ArgumentNullException(nameof(parameters.ReportExceptionFunction)); } if (parameters.ReportOutputFunction == null) { throw new ArgumentNullException(nameof(parameters.ReportOutputFunction)); } if (parameters.LogOutputFunction == null) { throw new ArgumentNullException(nameof(parameters.LogOutputFunction)); } if (parameters.ReportResultsFunction == null) { throw new ArgumentNullException(nameof(parameters.ReportResultsFunction)); } if (!Directory.Exists(parameters.SelectedFolder)) { throw new DirectoryNotFoundException(parameters.SelectedFolder); } if (parameters.CancelToken == null) { throw new ArgumentNullException(nameof(parameters.CancelToken), "If you do not want to pass a CancellationToken, then pass 'CancellationToken.None'"); } parameters.CancelToken.ThrowIfCancellationRequested(); }
private void BeginScanning() { if (ProcessingToggle.CurrentState == ToggleState.Active) { btnSearch.Enabled = false; ProcessingToggle.SetState(ToggleState.Inactive); } else if (ProcessingToggle.CurrentState == ToggleState.Ready) { btnSearch.Enabled = false; ProcessingToggle.SetState(ToggleState.Active); bool calculateEntropy = checkboxCalculateEntropy.Checked; string selectedFolder = tbPath.Text; string searchPatterns = tbSearchPatterns.Text; List <YaraFilter> yaraParameters = new List <YaraFilter>(); if (checkBoxYaraRules.Checked) { yaraParameters = currentYaraFilters.ToList(); } IDataPersistenceLayer dataPersistenceLayer = null; if (radioPersistenceCSV.Checked) { dataPersistenceLayer = new CsvDataPersistenceLayer(tbPersistenceParameter.Text); } else if (radioPersistenceSqlite.Checked) { dataPersistenceLayer = new SqliteDataPersistenceLayer(tbPersistenceParameter.Text); } else if (radioPersistenceSqlServer.Checked) { dataPersistenceLayer = new SqlDataPersistenceLayer(tbPersistenceParameter.Text); } FileEnumeratorParameters parameters = new FileEnumeratorParameters(cancelToken, Settings.FileEnumeration_DisableWorkerThread, selectedFolder, searchPatterns, calculateEntropy, yaraParameters, dataPersistenceLayer, Log.ToUI, Log.ToFile, ReportNumbers, Log.ExceptionMessage); enumerationStart = DateTime.Now; bool didThrow = false; try { parameters.ThrowIfAnyParametersInvalid(); } catch (Exception ex) { didThrow = true; MessageBox.Show( ex.ToString().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(), MsgBox_TitleBarText, MessageBoxButtons.OK, MessageBoxIcon.Error ); } if (didThrow) { ProcessingToggle.SetState(ToggleState.Ready); } else { Log.ToUI(Environment.NewLine); Log.ToAll($"Beginning Enumeration of folder: \"{parameters.SelectedFolder}\""); Log.ToAll("Parsing MFT. (This may take a few minutes)"); FileEnumerator.LaunchFileEnumerator(parameters); } } }
private static void Main(string[] args) { if (args.Length == 0) { DisplayUsageSyntax(); return; } // Will hold flag & parameter to flag, such as: "-p", "C:\Windows\" List <Tuple <string, string> > flags = GetFlags(args); if (!flags.Any()) { DisplayUsageSyntax(); return; } string searchPath = ""; string searchMask = "*.*"; bool isEntropyEnabled = false; bool isYaraEnabled = false; bool isSqlServerEnabled = false; bool isSqliteEnabled = false; bool isCsvEnabled = false; string sqliteDbFile = ""; string csvFile = ""; string sqlConnectionString = (Settings.Database_ConnectionString == "SetMe") ? "" : Settings.Database_ConnectionString; string yaraFiltersFile = ""; List <YaraFilter> yaraFilters = new List <YaraFilter>(); foreach (Tuple <string, string> flagTuple in flags) { string flag = flagTuple.Item1; string parameter = flagTuple.Item2; switch (flag) { case "e": isEntropyEnabled = true; break; case "p": searchPath = parameter; break; case "m": searchMask = parameter; break; case "y": isYaraEnabled = true; yaraFiltersFile = parameter; break; case "s": isSqlServerEnabled = true; break; case "l": isSqliteEnabled = true; sqliteDbFile = string.IsNullOrWhiteSpace(parameter) ? sqlConnectionString : parameter; break; case "c": isCsvEnabled = true; csvFile = parameter; break; } } ReportOutput(); ReportOutput("Running with these parameters:"); ReportOutput($" Search [P]ath: \"{searchPath}\""); ReportOutput($" Search [M]ask: {searchMask}"); ReportOutput($" Calulate [E]ntropy: {isEntropyEnabled}"); if (isYaraEnabled) { ReportOutput($" [Y]ara filters file: \"{yaraFiltersFile}\""); } if (isSqlServerEnabled) { ReportOutput($" [S]QL connection: \"{sqlConnectionString}\""); } else if (isSqliteEnabled) { ReportOutput($" Sq[l]ite DB: \"{sqliteDbFile}\""); } else if (isCsvEnabled) { ReportOutput($" [C]SV file: \"{csvFile}\""); } ReportOutput(); if (string.IsNullOrWhiteSpace(searchPath)) { ReportOutput("No search path provided!"); ReportOutput("You must supply the -p flag with a path, e.g.:"); ReportOutput("-p:\"C:\\Program Files\\BanzaiBuddy\""); ReportOutput(); ReportOutput("Aborting..."); return; } if (isYaraEnabled) { if (!File.Exists(yaraFiltersFile)) { ReportOutput($"The yara filters file path suppled does not exist: \"{yaraFiltersFile}\"."); ReportOutput(); ReportOutput("Aborting..."); return; } try { string loadJson = File.ReadAllText(yaraFiltersFile); yaraFilters = JsonConvert.DeserializeObject <List <YaraFilter> >(loadJson); } catch { ReportOutput("The yara filters file must be a JSON file."); ReportOutput(); ReportOutput("Aborting..."); return; } } IDataPersistenceLayer dataPersistenceLayer; if (isSqliteEnabled) { dataPersistenceLayer = new SqliteDataPersistenceLayer(sqliteDbFile); } else if (isCsvEnabled) { dataPersistenceLayer = new CsvDataPersistenceLayer(csvFile); } else if (isSqlServerEnabled) { dataPersistenceLayer = new SqlDataPersistenceLayer(sqlConnectionString); } else { ReportOutput("No output parameter provided!"); if (!string.IsNullOrWhiteSpace(sqlConnectionString)) { ReportOutput("(SQL server connection string supplied in config file, asuming SQL server output...)"); dataPersistenceLayer = new SqlDataPersistenceLayer(sqlConnectionString); } else { ReportOutput("You must supply an output parameter, e.g.:"); ReportOutput("-c:C:\\out.csv"); ReportOutput($"OR provide a SQL server connection string in the config file: {_thisExecutableFilename}"); ReportOutput("(Because it defaults to a SQL server connection. However, the connection string was missing.)"); ReportOutput(); ReportOutput("Aborting..."); return; } } FileEnumeratorParameters parameters = new FileEnumeratorParameters( CancellationToken.None, true, // Do not change this. If set to false, it will run on a thread, return immediately and exit, killing the thread. searchPath, searchMask, isEntropyEnabled, yaraFilters, dataPersistenceLayer, ReportOutput, Log.LogOutputAction, ReportResults, Log.ExceptionMessage ); parameters.ThrowIfAnyParametersInvalid(); ReportOutput("Beginning scan..."); FileEnumerator.LaunchFileEnumerator(parameters); }
private static FileEnumeratorReport Worker(FileEnumeratorParameters parameters) { TimingMetrics timingMetrics = new TimingMetrics(); FailSuccessCount fileEnumCount = new FailSuccessCount("OS files enumerated"); FailSuccessCount databaseInsertCount = new FailSuccessCount("OS database rows updated"); try { parameters.CancelToken.ThrowIfCancellationRequested(); StringBuilder currentPath = new StringBuilder(parameters.SelectedFolder); string lastParent = currentPath.ToString(); string temp = currentPath.ToString(); if (temp.Contains(':') && (temp.Length == 2 || temp.Length == 3)) // Is a root directory, i.e. "C:" or "C:\" { lastParent = "."; } string drive = parameters.SelectedFolder[0].ToString().ToUpper(); timingMetrics.Start(TimingMetric.ParsingMFT); List <DriveInfo> ntfsDrives = DriveInfo.GetDrives().Where(d => d.IsReady && d.DriveFormat == "NTFS").ToList(); DriveInfo driveToAnalyze = ntfsDrives.Where(dr => dr.Name.ToUpper().Contains(drive)).Single(); NtfsReader ntfsReader = new NtfsReader(driveToAnalyze, RetrieveMode.All); IEnumerable <INode> mftNodes = ntfsReader.GetNodes(driveToAnalyze.Name) .Where(node => (node.Attributes & (NtfsNodeAttributes.Device | NtfsNodeAttributes.Directory | NtfsNodeAttributes.ReparsePoint | NtfsNodeAttributes.SparseFile )) == 0) // This means that we DONT want any matches of the above NtfsNodeAttributes type .Where(node => FileMatchesPattern(node.FullName, parameters.SearchPatterns)); //.OrderByDescending(n => n.Size); if (parameters.SelectedFolder.ToCharArray().Length > 3) { string selectedFolderUppercase = parameters.SelectedFolder.ToUpperInvariant().TrimEnd(new char[] { '\\' }); mftNodes = mftNodes.Where(node => node.FullName.ToUpperInvariant().Contains(selectedFolderUppercase)); } timingMetrics.Stop(TimingMetric.ParsingMFT); IDataPersistenceLayer dataPersistenceLayer = parameters.DataPersistenceLayer; foreach (INode node in mftNodes) { string message = $"MFT#: {node.MFTRecordNumber.ToString().PadRight(7)} Seq.#: {node.SequenceNumber.ToString().PadRight(4)} Path: {node.FullName}"; parameters.ReportAndLogOutputFunction.Invoke(message); fileEnumCount.IncrementSucceededCount(); FileProperties prop = new FileProperties(); prop.PopulateFileProperties(parameters, parameters.SelectedFolder[0], node, timingMetrics); // INSERT file properties into _DATABASE_ timingMetrics.Start(TimingMetric.PersistingFileProperties); bool insertResult = dataPersistenceLayer.PersistFileProperties(prop); if (insertResult) { databaseInsertCount.IncrementSucceededCount(); } else { databaseInsertCount.IncrementFailedCount(); } timingMetrics.Stop(TimingMetric.PersistingFileProperties); parameters.CancelToken.ThrowIfCancellationRequested(); } dataPersistenceLayer.Dispose(); FileProperties.CleanUp(); } catch (OperationCanceledException) { } return(new FileEnumeratorReport(new List <FailSuccessCount> { fileEnumCount, databaseInsertCount }, timingMetrics)); }
private static List <FailSuccessCount> Worker(FileEnumeratorParameters parameters) { fileEnumCount = new FailSuccessCount("OS files enumerated"); databaseInsertCount = new FailSuccessCount("OS database rows updated"); directoryEnumCount = new FailSuccessCount("directories enumerated"); try { parameters.CancelToken.ThrowIfCancellationRequested(); StringBuilder currentPath = new StringBuilder(parameters.SelectedFolder); string lastParent = currentPath.ToString(); string temp = currentPath.ToString(); if (temp.Contains(':') && (temp.Length == 2 || temp.Length == 3)) // Is a root directory, i.e. "C:" or "C:\" { lastParent = "."; } string drive = parameters.SelectedFolder[0].ToString(); List <DriveInfo> ntfsDrives = DriveInfo.GetDrives().Where(d => d.DriveFormat == "NTFS").ToList(); DriveInfo driveToAnalyze = ntfsDrives.Where(dr => dr.Name.ToUpper().Contains(drive.ToUpper())).Single(); IEnumerable <INode> mftNodes = MftHelper.EnumerateMft(driveToAnalyze); if (parameters.SelectedFolder.ToCharArray().Length > 3) { string selectedFolderUppercase = parameters.SelectedFolder.ToUpperInvariant().TrimEnd(new char[] { '\\' }); mftNodes = mftNodes.Where(node => node.FullName.ToUpperInvariant().Contains(selectedFolderUppercase)); } foreach (INode node in mftNodes) { // File _PATTERN MATCHING_ if (FileMatchesPattern(node.FullName, parameters.SearchPatterns)) { string message = $"MFT#: {node.MFTRecordNumber.ToString().PadRight(7)} Seq.#: {node.SequenceNumber.ToString().PadRight(4)} Path: {node.FullName}"; if (parameters.LogOutputFunction != null) { parameters.LogOutputFunction.Invoke(message); } if (parameters.ReportOutputFunction != null) { parameters.ReportOutputFunction.Invoke(message); } fileEnumCount.IncrementSucceededCount(); parameters.CancelToken.ThrowIfCancellationRequested(); FileProperties prop = new FileProperties(); prop.PopulateFileProperties(parameters, parameters.SelectedFolder[0], node); parameters.CancelToken.ThrowIfCancellationRequested(); // INSERT file properties into _DATABASE_ bool insertResult = FilePropertiesAccessLayer.InsertFileProperties(prop); if (insertResult) { databaseInsertCount.IncrementSucceededCount(); } else { databaseInsertCount.IncrementFailedCount(); } } else { if (parameters.LogOutputFunction != null) { parameters.LogOutputFunction.Invoke($"FileMatchingPattern returned false: \"{node.FullName}\""); } fileEnumCount.IncrementFailedCount(); } parameters.CancelToken.ThrowIfCancellationRequested(); } } catch (OperationCanceledException) { } return(new List <FailSuccessCount> { fileEnumCount, databaseInsertCount, directoryEnumCount }); }
private static void Main(string[] args) { string connectionString = Settings.Database_ConnectionString; if (string.IsNullOrWhiteSpace(connectionString) || connectionString == "SetMe") { ReportOutput("ERROR: Connection string not set! Please set the SQL connection string in .config file."); ReportOutput("Aborting..."); return; } else { FilePropertiesAccessLayer.SetConnectionString(connectionString); } if (args.Length == 0) { DisplayUsageSyntax(); return; } // Will hold flag & parameter to flag, such as: "-p", "C:\Windows\" List <Tuple <string, string> > flags = GetFlags(args); if (!flags.Any()) { return; } string searchPath = ""; string searchMask = "*.*"; bool calcEntropy = false; bool onlineValidation = false; string yaraRulesFile = ""; foreach (Tuple <string, string> flagTuple in flags) { string flag = flagTuple.Item1; string parameter = flagTuple.Item2; switch (flag) { case "e": calcEntropy = true; break; case "v": onlineValidation = true; break; case "p": searchPath = parameter; break; case "m": searchMask = parameter; break; case "y": yaraRulesFile = parameter; break; } } ReportOutput($"Search [P]ath: \"{searchPath}\""); ReportOutput($"Search [M]ask: {searchMask}"); ReportOutput($"Calulate [E]ntropy: {calcEntropy}"); ReportOutput($"Online [V]alidation: {onlineValidation}"); ReportOutput($"[Y]ara Rules File: \"{yaraRulesFile}\""); ReportOutput(""); FileEnumeratorParameters parameters = new FileEnumeratorParameters(CancellationToken.None, Settings.FileEnumeration_DisableWorkerThread, searchPath, searchMask, calcEntropy, onlineValidation, yaraRulesFile, ReportOutput, LogOutput, ReportResults, ReportException); ReportOutput("Beginning enumeration..."); FileEnumerator.LaunchFileEnumerator(parameters); }