public override MappedStream OpenContent(SparseStream parent, Ownership ownsParent) { FileAccess access = FileAccess.Read; FileShare share = FileShare.Read; if (_descriptor.Access == ExtentAccess.ReadWrite && _access != FileAccess.Read) { access = FileAccess.ReadWrite; share = FileShare.None; } if (_descriptor.Type != ExtentType.Sparse && _descriptor.Type != ExtentType.VmfsSparse && _descriptor.Type != ExtentType.Zero) { if (ownsParent == Ownership.Dispose && parent != null) { parent.Dispose(); } } else if (parent == null) { parent = new ZeroStream(_descriptor.SizeInSectors * Sizes.Sector); } if (_monolithicStream != null) { // Early-out for monolithic VMDKs return(new HostedSparseExtentStream( _monolithicStream, Ownership.None, _diskOffset, parent, ownsParent)); } switch (_descriptor.Type) { case ExtentType.Flat: case ExtentType.Vmfs: return(MappedStream.FromStream( _fileLocator.Open(_descriptor.FileName, FileMode.Open, access, share), Ownership.Dispose)); case ExtentType.Zero: return(new ZeroStream(_descriptor.SizeInSectors * Sizes.Sector)); case ExtentType.Sparse: return(new HostedSparseExtentStream( _fileLocator.Open(_descriptor.FileName, FileMode.Open, access, share), Ownership.Dispose, _diskOffset, parent, ownsParent)); case ExtentType.VmfsSparse: return(new ServerSparseExtentStream( _fileLocator.Open(_descriptor.FileName, FileMode.Open, access, share), Ownership.Dispose, _diskOffset, parent, ownsParent)); default: throw new NotSupportedException(); } }
private static void Main(string[] args) { ExceptionlessClient.Default.Startup("Kruacm8p1B6RFAw2WMnKcEqkQcnWRkF3RmPSOzlW"); SetupNLog(); SetupPatterns(); _logger = LogManager.GetCurrentClassLogger(); _fluentCommandLineParser = new FluentCommandLineParser <ApplicationArguments> { IsCaseSensitive = false }; _fluentCommandLineParser.Setup(arg => arg.File) .As('f') .WithDescription("File to search. Either this or -d is required"); _fluentCommandLineParser.Setup(arg => arg.Directory) .As('d') .WithDescription("Directory to recursively process. Either this or -f is required"); _fluentCommandLineParser.Setup(arg => arg.SaveTo) .As('o') .WithDescription("File to save results to"); _fluentCommandLineParser.Setup(arg => arg.GetAscii) .As('a') .SetDefault(true) .WithDescription("If set, look for ASCII strings. Default is true. Use -a false to disable"); _fluentCommandLineParser.Setup(arg => arg.GetUnicode) .As('u') .SetDefault(true) .WithDescription("If set, look for Unicode strings. Default is true. Use -u false to disable"); _fluentCommandLineParser.Setup(arg => arg.MinimumLength) .As('m').SetDefault(3).WithDescription("Minimum string length. Default is 3"); _fluentCommandLineParser.Setup(arg => arg.BlockSizeMb) .As('b').SetDefault(512).WithDescription("Chunk size in MB. Valid range is 1 to 1024. Default is 512"); _fluentCommandLineParser.Setup(arg => arg.Quiet) .As('q').SetDefault(false).WithDescription("Quiet mode (Do not show header or total number of hits)"); _fluentCommandLineParser.Setup(arg => arg.QuietQuiet) .As('s') .SetDefault(false) .WithDescription( "Really Quiet mode (Do not display hits to console. Speeds up processing when using -o)"); _fluentCommandLineParser.Setup(arg => arg.MaximumLength) .As('x').SetDefault(-1).WithDescription("Maximum string length. Default is unlimited\r\n"); _fluentCommandLineParser.Setup(arg => arg.GetPatterns) .As('p').SetDefault(false).WithDescription("Display list of built in regular expressions"); _fluentCommandLineParser.Setup(arg => arg.LookForString) .As("ls") .SetDefault(string.Empty) .WithDescription("String to look for. When set, only matching strings are returned"); _fluentCommandLineParser.Setup(arg => arg.LookForRegex) .As("lr") .SetDefault(string.Empty) .WithDescription("Regex to look for. When set, only strings matching the regex are returned"); _fluentCommandLineParser.Setup(arg => arg.StringFile) .As("fs") .SetDefault(string.Empty) .WithDescription("File containing strings to look for. When set, only matching strings are returned"); _fluentCommandLineParser.Setup(arg => arg.RegexFile) .As("fr") .SetDefault(string.Empty) .WithDescription( "File containing regex patterns to look for. When set, only strings matching regex patterns are returned\r\n"); _fluentCommandLineParser.Setup(arg => arg.AsciiRange) .As("ar") .SetDefault("[\x20-\x7E]") .WithDescription( @"Range of characters to search for in 'Code page' strings. Specify as a range of characters in hex format and enclose in quotes. Default is [\x20 -\x7E]"); _fluentCommandLineParser.Setup(arg => arg.UnicodeRange) .As("ur") .SetDefault("[\u0020-\u007E]") .WithDescription( "Range of characters to search for in Unicode strings. Specify as a range of characters in hex format and enclose in quotes. Default is [\\u0020-\\u007E]\r\n"); _fluentCommandLineParser.Setup(arg => arg.CodePage) .As("cp") .SetDefault(1252) .WithDescription( "Code page to use. Default is 1252. Use the Identifier value for code pages at https://goo.gl/ig6DxW"); _fluentCommandLineParser.Setup(arg => arg.FileMask) .As("mask") .SetDefault(string.Empty) .WithDescription( "When using -d, file mask to search for. * and ? are supported. This option has no effect when using -f"); _fluentCommandLineParser.Setup(arg => arg.RegexOnly) .As("ro") .SetDefault(false) .WithDescription( "When true, list the string matched by regex pattern vs string the pattern was found in (This may result in duplicate strings in output. ~ denotes approx. offset)"); _fluentCommandLineParser.Setup(arg => arg.ShowOffset) .As("off") .SetDefault(false) .WithDescription( $"Show offset to hit after string, followed by the encoding (A={_fluentCommandLineParser.Object.CodePage}, U=Unicode)\r\n"); _fluentCommandLineParser.Setup(arg => arg.SortAlpha) .As("sa").SetDefault(false).WithDescription("Sort results alphabetically"); _fluentCommandLineParser.Setup(arg => arg.SortLength) .As("sl").SetDefault(false).WithDescription("Sort results by length"); var header = $"bstrings version {Assembly.GetExecutingAssembly().GetName().Version}" + "\r\n\r\nAuthor: Eric Zimmerman ([email protected])" + "\r\nhttps://github.com/EricZimmerman/bstrings"; var footer = @"Examples: bstrings.exe -f ""C:\Temp\UsrClass 1.dat"" --ls URL" + "\r\n\t " + @" bstrings.exe -f ""C:\Temp\someFile.txt"" --lr guid" + "\r\n\t " + @" bstrings.exe -f ""C:\Temp\aBigFile.bin"" --fs c:\temp\searchStrings.txt --fr c:\temp\searchRegex.txt -s" + "\r\n\t " + @" bstrings.exe -d ""C:\Temp"" --mask ""*.dll""" + "\r\n\t " + @" bstrings.exe -d ""C:\Temp"" --ar ""[\x20-\x37]""" + "\r\n\t " + @" bstrings.exe -d ""C:\Temp"" --cp 10007" + "\r\n\t " + @" bstrings.exe -d ""C:\Temp"" --ls test" + "\r\n\t " + @" bstrings.exe -f ""C:\Temp\someOtherFile.txt"" --lr cc -sa" + "\r\n\t " + @" bstrings.exe -f ""C:\Temp\someOtherFile.txt"" --lr cc -sa -m 15 -x 22" + "\r\n\t " + @" bstrings.exe -f ""C:\Temp\UsrClass 1.dat"" --ls mui -sl" + "\r\n\t "; _fluentCommandLineParser.SetupHelp("?", "help") .WithHeader(header) .Callback(text => _logger.Info(text + "\r\n" + footer)); var result = _fluentCommandLineParser.Parse(args); if (result.HelpCalled) { return; } if (_fluentCommandLineParser.Object.GetPatterns) { _logger.Warn("Name \t\tDescription"); foreach (var regExPattern in RegExPatterns.OrderBy(t => t.Key)) { var desc = RegExDesc[regExPattern.Key]; _logger.Info($"{regExPattern.Key}\t{desc}"); } _logger.Info(""); _logger.Info("To use a built in pattern, supply the Name to the --lr switch\r\n"); return; } if (result.HasErrors) { _logger.Error(""); _logger.Error(result.ErrorText); _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options); return; } if (_fluentCommandLineParser.Object.File.IsNullOrEmpty() && _fluentCommandLineParser.Object.Directory.IsNullOrEmpty()) { _fluentCommandLineParser.HelpOption.ShowHelp(_fluentCommandLineParser.Options); _logger.Warn("Either -f or -d is required. Exiting"); return; } if (_fluentCommandLineParser.Object.File.IsNullOrEmpty() == false && !File.Exists(_fluentCommandLineParser.Object.File) && _fluentCommandLineParser.Object.FileMask.Length == 0) { _logger.Warn($"File '{_fluentCommandLineParser.Object.File}' not found. Exiting"); return; } if (_fluentCommandLineParser.Object.Directory.IsNullOrEmpty() == false && !Directory.Exists(_fluentCommandLineParser.Object.Directory) && _fluentCommandLineParser.Object.FileMask.Length == 0) { _logger.Warn($"Directory '{_fluentCommandLineParser.Object.Directory}' not found. Exiting"); return; } if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info(header); _logger.Info(""); } var files = new List <string>(); if (_fluentCommandLineParser.Object.File.IsNullOrEmpty() == false) { files.Add(Path.GetFullPath(_fluentCommandLineParser.Object.File)); } else { try { if (_fluentCommandLineParser.Object.FileMask.Length > 0) { files.AddRange(Directory.EnumerateFiles(Path.GetFullPath(_fluentCommandLineParser.Object.Directory), _fluentCommandLineParser.Object.FileMask, SearchOption.AllDirectories)); } else { files.AddRange(Directory.EnumerateFiles(Path.GetFullPath(_fluentCommandLineParser.Object.Directory), "*", SearchOption.AllDirectories)); } } catch (Exception ex) { _logger.Error( $"Error getting files in '{_fluentCommandLineParser.Object.Directory}'. Error message: {ex.Message}"); return; } } if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info($"Command line: {string.Join(" ", args)}"); _logger.Info(""); } StreamWriter sw = null; var globalCounter = 0; var globalHits = 0; double globalTimespan = 0; var withBoundaryHits = false; if (_fluentCommandLineParser.Object.SaveTo.IsNullOrEmpty() == false && _fluentCommandLineParser.Object.SaveTo.Length > 0) { _fluentCommandLineParser.Object.SaveTo = _fluentCommandLineParser.Object.SaveTo.TrimEnd('\\'); var dir = Path.GetDirectoryName(_fluentCommandLineParser.Object.SaveTo); if (dir != null && Directory.Exists(dir) == false) { try { Directory.CreateDirectory(dir); } catch (Exception) { _logger.Warn( $"Invalid path: '{_fluentCommandLineParser.Object.SaveTo}'. Results will not be saved to a file."); _logger.Info(""); _fluentCommandLineParser.Object.SaveTo = string.Empty; } } else { if (dir == null) { _logger.Warn($"Invalid path: '{_fluentCommandLineParser.Object.SaveTo}"); _fluentCommandLineParser.Object.SaveTo = string.Empty; } } if (_fluentCommandLineParser.Object.SaveTo.Length > 0 && !_fluentCommandLineParser.Object.Quiet) { _logger.Info($"Saving hits to '{_fluentCommandLineParser.Object.SaveTo}'"); _logger.Info(""); } if (_fluentCommandLineParser.Object.SaveTo.Length > 0) { // try // { // File.Create(_fluentCommandLineParser.Object.SaveTo); // } // catch (Exception e) // { // _logger.Fatal($"Unable to create output file '{_fluentCommandLineParser.Object.SaveTo}'! Check permissions and try again! Error: {e.Message}"); // return; // } sw = new StreamWriter(_fluentCommandLineParser.Object.SaveTo, true); } } foreach (var file in files) { if (File.Exists(file) == false) { _logger.Warn($"'{file}' does not exist! Skipping"); } _sw = new Stopwatch(); _sw.Start(); var counter = 0; var hits = new HashSet <string>(); var regPattern = _fluentCommandLineParser.Object.LookForRegex; if (RegExPatterns.ContainsKey(_fluentCommandLineParser.Object.LookForRegex)) { regPattern = RegExPatterns[_fluentCommandLineParser.Object.LookForRegex]; } if (regPattern.Length > 0 && !_fluentCommandLineParser.Object.Quiet) { _logger.Info($"Searching via RegEx pattern: {regPattern}"); _logger.Info(""); } var minLength = 3; if (_fluentCommandLineParser.Object.MinimumLength > 0) { minLength = _fluentCommandLineParser.Object.MinimumLength; } var maxLength = -1; if (_fluentCommandLineParser.Object.MaximumLength > minLength) { maxLength = _fluentCommandLineParser.Object.MaximumLength; } var chunkSizeMb = _fluentCommandLineParser.Object.BlockSizeMb < 1 || _fluentCommandLineParser.Object.BlockSizeMb > 1024 ? 512 : _fluentCommandLineParser.Object.BlockSizeMb; var chunkSizeBytes = chunkSizeMb * 1024 * 1024; var fileSizeBytes = new FileInfo(file).Length; var bytesRemaining = fileSizeBytes; long offset = 0; var chunkIndex = 1; var totalChunks = fileSizeBytes / chunkSizeBytes + 1; var hsuffix = totalChunks == 1 ? "" : "s"; if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info( $"Searching {totalChunks:N0} chunk{hsuffix} ({chunkSizeMb} MB each) across {GetSizeReadable(fileSizeBytes)} in '{file}'"); _logger.Info(""); } try { MappedStream ms = null; try { var fs = File.Open(File.GetFileSystemEntryInfo(file).LongFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, PathFormat.LongFullPath); ms = MappedStream.FromStream(fs, Ownership.None); } catch (Exception) { _logger.Warn($"Unable to open file directly. This usually means the file is in use. Switching to raw access\r\n"); } if (ms == null) { //raw mode var ss = OpenFile(file); ms = MappedStream.FromStream(ss, Ownership.None); } using (ms) { while (bytesRemaining > 0) { if (bytesRemaining <= chunkSizeBytes) { chunkSizeBytes = (int)bytesRemaining; } var chunk = new byte[chunkSizeBytes]; ms.Read(chunk, 0, chunkSizeBytes); if (_fluentCommandLineParser.Object.GetUnicode) { var uh = GetUnicodeHits(chunk, minLength, maxLength, offset, _fluentCommandLineParser.Object.ShowOffset); foreach (var h in uh) { hits.Add(h); } } if (_fluentCommandLineParser.Object.GetAscii) { var ah = GetAsciiHits(chunk, minLength, maxLength, offset, _fluentCommandLineParser.Object.ShowOffset); foreach (var h in ah) { hits.Add(h); } } offset += chunkSizeBytes; bytesRemaining -= chunkSizeBytes; if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info( $"Chunk {chunkIndex:N0} of {totalChunks:N0} finished. Total strings so far: {hits.Count:N0} Elapsed time: {_sw.Elapsed.TotalSeconds:N3} seconds. Average strings/sec: {hits.Count/_sw.Elapsed.TotalSeconds:N0}"); } chunkIndex += 1; } //do chunk boundary checks to make sure we get everything and not split things if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info( "Primary search complete. Looking for strings across chunk boundaries..."); } bytesRemaining = fileSizeBytes; chunkSizeBytes = chunkSizeMb * 1024 * 1024; offset = chunkSizeBytes - _fluentCommandLineParser.Object.MinimumLength * 10 * 2; //move starting point backwards for our starting point chunkIndex = 0; var boundaryChunkSize = _fluentCommandLineParser.Object.MinimumLength * 10 * 2 * 2; //grab the same # of bytes on both sides of the boundary while (bytesRemaining > 0) { if (offset + boundaryChunkSize > fileSizeBytes) { break; } var chunk = new byte[boundaryChunkSize]; ms.Read(chunk, 0, boundaryChunkSize); if (_fluentCommandLineParser.Object.GetUnicode) { var uh = GetUnicodeHits(chunk, minLength, maxLength, offset, _fluentCommandLineParser.Object.ShowOffset); foreach (var h in uh) { hits.Add(" " + h); } if (withBoundaryHits == false && uh.Count > 0) { withBoundaryHits = uh.Count > 0; } } if (_fluentCommandLineParser.Object.GetAscii) { var ah = GetAsciiHits(chunk, minLength, maxLength, offset, _fluentCommandLineParser.Object.ShowOffset); foreach (var h in ah) { hits.Add(" " + h); } if (withBoundaryHits == false && ah.Count > 0) { withBoundaryHits = true; } } offset += chunkSizeBytes; bytesRemaining -= chunkSizeBytes; chunkIndex += 1; } } } catch (Exception ex) { _logger.Info(""); _logger.Error($"Error: {ex.Message}"); } _sw.Stop(); if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info("Search complete."); _logger.Info(""); } if (_fluentCommandLineParser.Object.SortAlpha) { _logger.Info("Sorting alphabetically..."); _logger.Info(""); var tempList = hits.ToList(); tempList.Sort(); hits = new HashSet <string>(tempList); } else if (_fluentCommandLineParser.Object.SortLength) { _logger.Info("Sorting by length..."); _logger.Info(""); var tempList = SortByLength(hits.ToList()).ToList(); hits = new HashSet <string>(tempList); } var fileStrings = new HashSet <string>(); var regexStrings = new HashSet <string>(); //set up highlighting if (_fluentCommandLineParser.Object.LookForString.Length > 0) { fileStrings.Add(_fluentCommandLineParser.Object.LookForString); } if (_fluentCommandLineParser.Object.LookForRegex.Length > 0) { regexStrings.Add(regPattern); } if (_fluentCommandLineParser.Object.StringFile.IsNullOrEmpty() == false || _fluentCommandLineParser.Object.RegexFile.IsNullOrEmpty() == false) { if (_fluentCommandLineParser.Object.StringFile.Length > 0) { if (File.Exists(_fluentCommandLineParser.Object.StringFile)) { fileStrings.UnionWith(new HashSet <string>(File.ReadAllLines(_fluentCommandLineParser.Object.StringFile))); } else { _logger.Error($"Strings file '{_fluentCommandLineParser.Object.StringFile}' not found."); } } if (_fluentCommandLineParser.Object.RegexFile.Length > 0) { if (File.Exists(_fluentCommandLineParser.Object.RegexFile)) { regexStrings.UnionWith(new HashSet <string>(File.ReadAllLines(_fluentCommandLineParser.Object.RegexFile))); } else { _logger.Error($"Regex file '{_fluentCommandLineParser.Object.RegexFile}' not found."); } } } AddHighlightingRules(fileStrings.ToList()); if (_fluentCommandLineParser.Object.RegexOnly == false) { AddHighlightingRules(regexStrings.ToList(), true); } if (!_fluentCommandLineParser.Object.Quiet) { _logger.Info("Processing strings..."); _logger.Info(""); } foreach (var hit in hits) { if (hit.Length == 0) { continue; } if (fileStrings.Count > 0 || regexStrings.Count > 0) { foreach (var fileString in fileStrings) { if (fileString.Trim().Length == 0) { continue; } if (hit.IndexOf(fileString, StringComparison.InvariantCultureIgnoreCase) < 0) { continue; } counter += 1; if (_fluentCommandLineParser.Object.QuietQuiet == false) { _logger.Info(hit); } sw?.WriteLine(hit); } var hitoffset = ""; if (_fluentCommandLineParser.Object.ShowOffset) { hitoffset = $"~{hit.Split('\t').Last()}"; } foreach (var regString in regexStrings) { if (regString.Trim().Length == 0) { continue; } try { var reg1 = new Regex(regString, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); if (reg1.IsMatch(hit) == false) { continue; } counter += 1; if (_fluentCommandLineParser.Object.RegexOnly) { foreach (var match in reg1.Matches(hit)) { if (_fluentCommandLineParser.Object.QuietQuiet == false) { _logger.Info($"{match}\t{hitoffset}"); } sw?.WriteLine($"{match}\t{hitoffset}"); } } else { if (_fluentCommandLineParser.Object.QuietQuiet == false) { _logger.Info(hit); } sw?.WriteLine(hit); } } catch (Exception ex) { _logger.Error($"Error setting up regular expression '{regString}': {ex.Message}"); } } } else { //dump all strings counter += 1; if (_fluentCommandLineParser.Object.QuietQuiet == false) { _logger.Info(hit); } sw?.WriteLine(hit); } } if (_fluentCommandLineParser.Object.Quiet) { continue; } var suffix = counter == 1 ? "" : "s"; _logger.Info(""); if (withBoundaryHits) { _logger.Info("** Strings prefixed with 2 spaces are hits found across chunk boundaries **"); _logger.Info(""); } _logger.Info( $"Found {counter:N0} string{suffix} in {_sw.Elapsed.TotalSeconds:N3} seconds. Average strings/sec: {hits.Count/_sw.Elapsed.TotalSeconds:N0}"); globalCounter += counter; globalHits += hits.Count; globalTimespan += _sw.Elapsed.TotalSeconds; if (files.Count > 1) { _logger.Info( "-------------------------------------------------------------------------------------\r\n"); } } if (sw != null) { sw.Flush(); sw.Close(); } if (!_fluentCommandLineParser.Object.Quiet && files.Count > 1) { var suffix = globalCounter == 1 ? "" : "s"; _logger.Info(""); _logger.Info( $"Found {globalCounter:N0} string{suffix} in {globalTimespan:N3} seconds. Average strings/sec: {globalHits/globalTimespan:N0}"); } }