public void CanLoadPdbGeneratedByBuild() { var folder = TestData.Get(@"TestData\MultiFileCompressed"); using (var fs = new DisposableFileSystem()) { var intermediateFolder = fs.GetFolder(); var result = WixRunner.Execute(new[] { "build", Path.Combine(folder, "Package.wxs"), Path.Combine(folder, "PackageComponents.wxs"), "-d", "MediaTemplateCompressionLevel", "-loc", Path.Combine(folder, "Package.en-us.wxl"), "-bindpath", Path.Combine(folder, "data"), "-intermediateFolder", intermediateFolder, "-o", Path.Combine(intermediateFolder, @"bin\test.msi") }, out var messages); Assert.Equal(0, result); Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\cab1.cab"))); var pdbPath = Path.Combine(intermediateFolder, @"bin\test.wixpdb"); Assert.True(File.Exists(pdbPath)); var pdb = Pdb.Load(pdbPath, suppressVersionCheck: true); Assert.NotNull(pdb); Assert.NotNull(pdb.Output); } }
private Pdb LoadPdb() { // We should never be required to load a PDB for a managed assembly that does // not incorporate native code, as no managed-relevant rule currently crawls // PDBs for its analysis. Debug.Assert(!this.PE.IsManaged || this.PE.IsMixedMode); Pdb pdb = null; try { pdb = new Pdb( this.PE.FileName, this.symbolPath, this.localSymbolDirectories, this.tracePdbLoad); } catch (PdbException ex) { this.PdbParseException = ex; } if (pdb != null && pdb.IsStripped) { this.StrippedPdb = pdb; pdb = null; this.PdbParseException = new PdbException(BinaryParsersResources.PdbStripped) { LoadTrace = this.StrippedPdb.LoadTrace }; } return(pdb); }
private Pdb LoadPdb() { Pdb pdb = null; try { pdb = new Pdb( this.PE.FileName, this.symbolPath, this.localSymbolDirectories, this.tracePdbLoad); } catch (PdbException ex) { this.PdbParseException = ex; } if (pdb != null && pdb.IsStripped) { this.StrippedPdb = pdb; pdb = null; this.PdbParseException = new PdbException(BinaryParsersResources.PdbStripped) { LoadTrace = this.StrippedPdb.LoadTrace }; } return(pdb); }
private void AnalyzeManagedAssemblyAndPdb(BinaryAnalyzerContext context) { PEBinary target = context.PEBinary(); Pdb di = target.Pdb; if (target.PE.ManagedPdbSourceFileChecksumAlgorithm(di.FileType) != ChecksumAlgorithmType.Sha256) { // '{0}' is a managed binary compiled with an insecure (SHA-1) source code hashing algorithm. // SHA-1 is subject to collision attacks and its use can compromise supply chain integrity. // Pass '-checksumalgorithm:SHA256' on the csc.exe command-line or populate the project // <ChecksumAlgorithm> property with 'SHA256' to enable secure source code hashing. context.Logger.Log(this, RuleUtilities.BuildResult(ResultKind.Fail, context, null, nameof(RuleResources.BA2004_Error_Managed), context.TargetUri.GetFileName())); return; } // '{0}' is a {1} binary which was compiled with a secure (SHA-256) // source code hashing algorithm. context.Logger.Log(this, RuleUtilities.BuildResult(ResultKind.Pass, context, null, nameof(RuleResources.BA2004_Pass), context.TargetUri.GetFileName(), "managed")); }
public void CanBuildInstanceTransform() { var folder = TestData.Get(@"TestData\InstanceTransform"); using (var fs = new DisposableFileSystem()) { var intermediateFolder = fs.GetFolder(); var result = WixRunner.Execute(new[] { "build", Path.Combine(folder, "Package.wxs"), Path.Combine(folder, "PackageComponents.wxs"), "-loc", Path.Combine(folder, "Package.en-us.wxl"), "-bindpath", Path.Combine(folder, "data"), "-intermediateFolder", intermediateFolder, "-o", Path.Combine(intermediateFolder, @"bin\test.msi") }, out var messages); Assert.Equal(0, result); var pdb = Pdb.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"), false); Assert.NotEmpty(pdb.Output.SubStorages); } }
/// <summary> /// Extracts files from an MSI database and rewrites the paths embedded in the source .wixpdb to the output .wixpdb. /// </summary> private void MeltProduct() { // print friendly message saying what file is being decompiled Console.WriteLine("{0} / {1}", Path.GetFileName(this.inputFile), Path.GetFileName(this.inputPdbFile)); // extract files from the .msi (unless suppressed) and get the path map of File ids to target paths string outputDirectory = this.exportBasePath ?? Environment.GetEnvironmentVariable("WIX_TEMP"); IDictionary <string, string> paths = null; using (InstallPackage package = new InstallPackage(this.inputFile, DatabaseOpenMode.ReadOnly, null, outputDirectory)) { if (!this.suppressExtraction) { package.ExtractFiles(); } paths = package.Files.SourcePaths; } Pdb inputPdb = Pdb.Load(this.inputPdbFile, true, true); Table wixFileTable = inputPdb.Output.Tables["WixFile"]; if (null != wixFileTable) { foreach (Row row in wixFileTable.Rows) { WixFileRow fileRow = row as WixFileRow; if (null != fileRow) { string newPath; if (paths.TryGetValue(fileRow.File, out newPath)) { fileRow.Source = Path.Combine(outputDirectory, newPath); } } } } string tempPath = Path.Combine(Environment.GetEnvironmentVariable("WIX_TEMP") ?? Path.GetTempPath(), Path.GetRandomFileName()); try { inputPdb.Save(this.outputFile, null, null, tempPath); } finally { if (this.tidy) { if (!AppCommon.DeleteDirectory(tempPath, this.messageHandler)) { Console.WriteLine(MeltStrings.WAR_FailedToDeleteTempDir, tempPath); } } else { Console.WriteLine(MeltStrings.INF_TempDirLocatedAt, tempPath); } } }
/// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { if (Assembly != null) { Assembly.Dispose(); } if (Pdb != null) { Pdb.Dispose(); } }
public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context) { PEBinary target = context.PEBinary(); Pdb pdb = target.Pdb; if (PrintHeader) { Console.WriteLine("Target,Compiler Name,Compiler BackEnd Version,Compiler FrontEnd Version,Language,Module Name,Module Library,Hash,Error"); PrintHeader = false; } if (pdb == null) { Console.Write(context.TargetUri.LocalPath + ","); Console.WriteLine($",,,,,,{context?.Hashes?.Sha256},{target.PdbParseException.Message}"); return; } var records = new Dictionary <string, ObjectModuleDetails>(); foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails omDetails = om.GetObjectModuleDetails(); string record = omDetails.CompilerName?.Replace(",", "_").Trim() + "," + omDetails.CompilerBackEndVersion + "," + omDetails.CompilerFrontEndVersion + "," + omDetails.Language; if (!records.TryGetValue(record, out ObjectModuleDetails value)) { records[record] = omDetails; } } foreach (KeyValuePair <string, ObjectModuleDetails> kv in records) { string compilerData = kv.Key; ObjectModuleDetails omDetails = kv.Value; Console.Write(context.TargetUri.LocalPath + ","); Console.Write(compilerData + ","); Console.Write(omDetails.Name?.Replace(",", "_") + ","); Console.Write(omDetails.Library?.Replace(",", ";") + ","); Console.Write($"{context?.Hashes?.Sha256},"); Console.WriteLine(); } }
public void DisposePortableExecutableData() { if (_pdb != null) { _pdb.Dispose(); _pdb = null; } if (_pe != null) { _pe.Dispose(); _pe = null; } }
/// <summary> /// Extract binary data from tables with a Name and Data column in them. /// </summary> /// <param name="inputPdb">A reference to a <see cref="Pdb"/> as output. Paths (Data properties) will be modified in this object.</param> /// <param name="package">The installer database to rip from.</param> /// <param name="exportPath">The full path where files will be exported to.</param> /// <param name="tableName">The name of the table to export.</param> private static void MeltBinaryTable(Pdb inputPdb, InstallPackage package, string exportPath, string tableName) { if (string.IsNullOrEmpty(tableName)) { throw new ArgumentNullException("tableName"); } if (string.IsNullOrEmpty(exportPath)) { throw new ArgumentNullException("exportPath"); } if (null == package) { throw new ArgumentNullException("package"); } if (null == inputPdb) { throw new ArgumentNullException("inputPdb"); } Table pdbTable = inputPdb.Output.Tables[tableName]; if (null == pdbTable) { Console.WriteLine("Table {0} does not exist.", tableName); return; } try { Directory.CreateDirectory(exportPath); Melt.ExtractFilesInBinaryTable(package, null, tableName, exportPath); IDictionary <string, string> paths = package.GetFilePaths(exportPath); if (null != paths) { foreach (Row row in pdbTable.Rows) { string filename = (string)row.Fields[0].Data; row.Fields[1].Data = paths[filename]; } } } catch (Exception ex) { Console.WriteLine("An error occured extracting the {0} binary table from the install package.", tableName); Console.WriteLine(ex.Message); } }
public sealed override void Analyze(BinaryAnalyzerContext context) { // Uses PDB Parsing. BinaryParsers.PlatformSpecificHelpers.ThrowIfNotOnWindows(); PEBinary target = context.PEBinary(); if (target.Pdb == null) { Errors.LogExceptionLoadingPdb(context, target.PdbParseException); return; } Pdb di = target.Pdb; AnalyzePortableExecutableAndPdb(context); }
/// <summary> /// Returns a hash code for this instance. /// </summary> /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns> /// <inheritdoc /> public override int GetHashCode() { unchecked { var hashCode = FileName != null?FileName.GetHashCode() : 0; hashCode = (hashCode * 397) ^ (int)FileSize; hashCode = (hashCode * 397) ^ ImageBase.GetHashCode(); hashCode = (hashCode * 397) ^ IsManaged.GetHashCode(); hashCode = (hashCode * 397) ^ IsRuntime.GetHashCode(); hashCode = (hashCode * 397) ^ (Pdb != null ? Pdb.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (PeFile != null ? PeFile.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (int)TimeStamp; hashCode = (hashCode * 397) ^ Version.GetHashCode(); return(hashCode); } }
public int Run() { if (_args.Length == 0) { MainHelp(); return 0; } _action = AppActions.LookUp(_args[0]); if (_action == null) { WriteError("unknown action: " + _args[0]); return 0; } if (_args.Length == 1) { WriteError("need file path"); return 0; } try { _pdb = Pdb.Load(_args[1]); } catch (PdbNotFoundException) { WriteError("file not found: " + _args[1]); return 1; } catch (PdbFormatException) { WriteError("wrong pdb format: " + _args[1]); return 2; } try { _action.Execute(this); } catch (NotImplementedException ex) { WriteError("not impl: " + ex.Message + " @ " + _args[1]); return 127; } return 0; }
/// <summary> /// Extracts files from an MSI database and rewrites the paths embedded in the source .wixpdb to the output .wixpdb. /// </summary> private void MeltProduct() { // print friendly message saying what file is being decompiled Console.WriteLine("{0} / {1}", Path.GetFileName(this.inputFile), Path.GetFileName(this.inputPdbFile)); Pdb inputPdb = Pdb.Load(this.inputPdbFile, true); // extract files from the .msi (unless suppressed) and get the path map of File ids to target paths string outputDirectory = this.exportBasePath ?? Environment.GetEnvironmentVariable("WIX_TEMP"); IDictionary <string, string> paths = null; using (InstallPackage package = new InstallPackage(this.inputFile, DatabaseOpenMode.ReadOnly, null, outputDirectory)) { // ignore failures as this is a new validation in v3.x ValidateMsiMatchesPdb(package, inputPdb); if (!this.suppressExtraction) { package.ExtractFiles(); } paths = package.Files.SourcePaths; } Table wixFileTable = inputPdb.Output.Tables["WixFile"]; if (null != wixFileTable) { foreach (Row row in wixFileTable.Rows) { WixFileRow fileRow = row as WixFileRow; if (null != fileRow) { string newPath; if (paths.TryGetValue(fileRow.File, out newPath)) { fileRow.Source = Path.Combine(outputDirectory, newPath); } } } } inputPdb.Save(this.outputFile); }
static void ExtractTrm(string trmPath, string basePath = null) { if (basePath == null) { basePath = Path.ChangeExtension(trmPath, null); } else { basePath = Path.Combine(basePath, Path.GetFileNameWithoutExtension(trmPath)); } using DatReader dat = new DatReader(Utils.CheckDecompress(File.OpenRead(trmPath))); // First entry is texture DAT using DatReader txmDat = new DatReader(new MemoryStream(dat.GetData(0))); using ObjConverter converter = new ObjConverter(txmDat); string mtlPath = basePath + ".mtl"; string mtlName = Path.GetFileName(mtlPath); // Subsequent entries are train car DATs for (int i = 1; i < dat.EntriesCount; ++i) { using DatReader innerDat = new DatReader(new MemoryStream(dat.GetData(i))); // And within each train car DAT are PDBs for (int j = 0; j < innerDat.EntriesCount; ++j) { using MemoryStream ms = new MemoryStream(innerDat.GetData(j)); BinaryReader br = new BinaryReader(ms); Pdb pdb = new Pdb(); pdb.Read(br); using StreamWriter sw = File.CreateText($"{basePath}.{i}_{j}.obj"); sw.WriteLine($"mtllib {mtlName}"); sw.WriteLine(); converter.ConvertObj(pdb, sw); } } using (StreamWriter sw = File.CreateText(mtlPath)) { converter.ExportTextures(sw, basePath + "."); } }
/// <summary> /// Checks to make sure that the debug symbols match up with the MSI. /// This is to help in ensuring that error 1642 does not inexplicably appear. /// </summary> /// <remarks> /// This is meant to assist with Bug # 4792 /// http://wixtoolset.org/issues/4792/ /// </remarks> /// <param name="package"> /// The MSI currently being melted. /// </param> /// <param name="inputPdb"> /// The debug symbols package being compared against the <paramref name="package"/>. /// </param> /// <returns></returns> private static bool ValidateMsiMatchesPdb(InstallPackage package, Pdb inputPdb) { string msiPackageCode = (string)package.Property["PackageCode"]; foreach (Row pdbPropertyRow in inputPdb.Output.Tables["Property"].Rows) { if ("PackageCode" == (string)pdbPropertyRow.Fields[0].Data) { string pdbPackageCode = (string)pdbPropertyRow.Fields[1].Data; if (msiPackageCode != pdbPackageCode) { Console.WriteLine(MeltStrings.WAR_MismatchedPackageCode, msiPackageCode, pdbPackageCode); return(false); } break; } } return(true); }
public void ConvertObj(Pdb pdb, StreamWriter sw) { int startVert = 1; if (pdb.Specular != null) { sw.WriteLine("o specular"); startVert = WriteObj(pdb.Specular, sw, startVert); } if (pdb.Diffuse != null) { sw.WriteLine("o diffuse"); startVert = WriteObj(pdb.Diffuse, sw, startVert); } if (pdb.Metallic != null) { sw.WriteLine("o metallic"); startVert = WriteObj(pdb.Metallic, sw, startVert); } }
static void ExtractMapAnim(string mapAnimPath, string basePath = null) { if (basePath == null) { basePath = Path.ChangeExtension(mapAnimPath, null); } else { basePath = Path.Combine(basePath, Path.GetFileNameWithoutExtension(mapAnimPath)); } using DatReader dat = new DatReader(Utils.CheckDecompress(File.OpenRead(mapAnimPath))); // Second entry is texture DAT using DatReader txmDat = new DatReader(new MemoryStream(dat.GetData(1))); using ObjConverter converter = new ObjConverter(txmDat); string mtlPath = basePath + ".mtl"; string mtlName = Path.GetFileName(mtlPath); // First entry is a collection of weird PDBs using DatReader pdbDat = new DatReader(new MemoryStream(dat.GetData(0))); for (int i = 0; i < pdbDat.EntriesCount; ++i) { using MemoryStream ms = new MemoryStream(pdbDat.GetData(i)); // This is a DAT, but we're going to pretend it's a normal PDB BinaryReader br = new BinaryReader(ms); Pdb pdb = new Pdb(); pdb.Read(br); using StreamWriter sw = File.CreateText($"{basePath}.{i}.obj"); sw.WriteLine($"mtllib {mtlName}"); sw.WriteLine(); converter.ConvertObj(pdb, sw); } using (StreamWriter sw = File.CreateText(mtlPath)) { converter.ExportTextures(sw, basePath + "."); } }
public PEBinary(Uri uri) : base(uri) { PE = new PE(TargetUri.LocalPath); IsManagedAssembly = PE.IsManaged; LoadException = PE.LoadException; Valid = PE.IsPEFile; try { Pdb = new Pdb(PE.FileName, Pdb.SymbolPath); } catch (PdbParseException ex) { PdbParseException = ex; } if (Pdb != null && Pdb.IsStripped) { StrippedPdb = Pdb; Pdb = null; PdbParseException = new PdbParseException(BinaryParsersResources.PdbStripped); } }
static void ExtractPdb(string pdbPath, string txmPath, string basePath = null, bool forceDirect = false) { if (basePath == null) { basePath = Path.ChangeExtension(pdbPath, null); } else { basePath = Path.Combine(basePath, Path.GetFileNameWithoutExtension(pdbPath)); } DatReader dat = new DatReader(Utils.CheckDecompress(File.OpenRead(pdbPath))); using DatReader txmDat = new DatReader(Utils.CheckDecompress(File.OpenRead(txmPath))); using ObjConverter converter = new ObjConverter(txmDat); string mtlPath = basePath + ".mtl"; string mtlName = Path.GetFileName(mtlPath); for (int i = 0; i < dat.EntriesCount; ++i) { using MemoryStream ms = new MemoryStream(dat.GetData(i)); BinaryReader br = new BinaryReader(ms); Pdb pdb = new Pdb(); pdb.Read(br); using StreamWriter sw = File.CreateText($"{basePath}.{i}.obj"); sw.WriteLine($"mtllib {mtlName}"); sw.WriteLine(); converter.ConvertObj(pdb, sw); } using (StreamWriter sw = File.CreateText(mtlPath)) { converter.ExportTextures(sw, basePath + ".", forceDirect); } }
private void DumpFile(string target, bool verbose) { PE pe; var sb = new StringBuilder(); try { pe = new PE(target); } catch (UnauthorizedAccessException) { Console.WriteLine(Path.GetFileName(target) + ": Unauthorized access exception"); return; } sb.AppendLine(Path.GetFileName(pe.FileName) + ":"); if (verbose) { sb.AppendLine(Indent + "Path: " + pe.FileName); } sb.Append(Indent + "Attr: "); if (!pe.IsPEFile) { sb.AppendLine("Not a portable executable"); sb.AppendLine(); return; } string language = pe.IsManaged ? "Pure Managed" : "Native"; if (pe.IsManaged && !pe.IsILOnly) { language = "Mixed Managed"; } ; sb.Append(language); string machine = pe.Machine.ToString(); sb.Append(Delimiter + machine); string subsystem = pe.Subsystem.ToString(); sb.Append(Delimiter + subsystem); if (pe.IsKernelMode) { sb.Append(Delimiter + "Kernel Mode"); } if (pe.IsResourceOnly) { sb.Append(Delimiter + "Resource Only"); } sb.Append(Delimiter + "Link " + pe.LinkerVersion.ToString()); sb.AppendLine(); // Close comma-separated attributes line sb.Append(Indent + "Pdb : "); Pdb pdb = null; try { pdb = new Pdb(pe.FileName); } catch (PdbParseException pdbParseException) { sb.AppendLine(pdbParseException.ExceptionCode.ToString()); sb.AppendLine(); return; } if (verbose) { sb.AppendLine(pdb.PdbLocation); } else { sb.AppendLine(Path.GetFileName(pdb.PdbLocation)); } sb.AppendLine(Indent + "SHA1: " + pe.SHA1Hash); Console.Out.WriteLineAsync(sb.ToString()); }
public override void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; if (context.Pdb == null) { Errors.LogExceptionLoadingPdb(context, context.PdbParseException); return; } Pdb di = context.Pdb; TruncatedCompilandRecordList warningTooLowModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList disabledWarningModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList unknownLanguageModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList allWarningLevelLowModules = new TruncatedCompilandRecordList(); string exampleTooLowWarningCommandLine = null; int overallMinimumWarningLevel = Int32.MaxValue; string exampleDisabledWarningCommandLine = null; List <int> overallDisabledWarnings = new List <int>(); foreach (DisposableEnumerableView <Symbol> omView in di.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails omDetails = om.GetObjectModuleDetails(); if (omDetails.Language == Language.Unknown) { // See if this module contributed to an executable section. If not, we can ignore the module. if (di.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId)) { unknownLanguageModules.Add(om.CreateCompilandRecord()); } continue; } if ((omDetails.Language != Language.C) && (omDetails.Language != Language.Cxx)) { continue; } if (omDetails.Compiler != "Microsoft (R) Optimizing Compiler") { continue; } if (!om.CreateChildIterator(SymTagEnum.SymTagFunction).Any()) { // uninteresting... continue; } int warningLevel = omDetails.WarningLevel; List <int> requiredDisabledWarnings = omDetails.ExplicitlyDisabledWarnings .Where(context.Policy.GetProperty(RequiredCompilerWarnings).Contains).ToList(); if (warningLevel >= 3 && requiredDisabledWarnings.Count == 0) { // We duplicate this condition to bail out early and avoid writing the // module description or newline into sbBadWarningModules if everything // in the module is OK. continue; } List <string> suffix = new List <string>(2); overallMinimumWarningLevel = Math.Min(overallMinimumWarningLevel, warningLevel); if (warningLevel < 3) { exampleTooLowWarningCommandLine = exampleTooLowWarningCommandLine ?? omDetails.CommandLine; string msg = "[warning level: " + warningLevel.ToString(CultureInfo.InvariantCulture) + "]"; warningTooLowModules.Add(om.CreateCompilandRecordWithSuffix(msg)); suffix.Add(msg); } if (requiredDisabledWarnings.Count != 0) { MergeInto(overallDisabledWarnings, requiredDisabledWarnings); exampleDisabledWarningCommandLine = exampleDisabledWarningCommandLine ?? omDetails.CommandLine; string msg = "[Explicitly disabled warnings: " + CreateTextWarningList(requiredDisabledWarnings) + "]"; disabledWarningModules.Add(om.CreateCompilandRecordWithSuffix(msg)); suffix.Add(msg); } allWarningLevelLowModules.Add(om.CreateCompilandRecordWithSuffix(String.Join(" ", suffix))); } if (unknownLanguageModules.Empty && exampleTooLowWarningCommandLine == null && exampleDisabledWarningCommandLine == null) { // '{0}' was compiled at a secure warning level ({1}) and does not // include any modules that disable specific warnings which are // required by policy. As a result, there is a greater likelihood // that memory corruption, information disclosure, double-free and // other security-related vulnerabilities do not exist in code. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2007_Pass), overallMinimumWarningLevel.ToString())); return; } if (!unknownLanguageModules.Empty) { // '{0}' contains code from an unknown language, preventing a // comprehensive analysis of the compiler warning settings. // The language could not be identified for the following modules: {1} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2007_Error_UnknownModuleLanguage), unknownLanguageModules.CreateSortedObjectList())); } if (exampleTooLowWarningCommandLine != null) { // '{0}' was compiled at too low a warning level. Warning level 3 enables // important static analysis in the compiler to flag bugs that can lead // to memory corruption, information disclosure, or double-free // vulnerabilities.To resolve this issue, compile at warning level 3 or // higher by supplying / W3, / W4, or / Wall to the compiler, and resolve // the warnings emitted. // An example compiler command line triggering this check: {1} // Modules triggering this check: {2} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2007_Error_InsufficientWarningLevel), overallMinimumWarningLevel.ToString(), exampleTooLowWarningCommandLine, warningTooLowModules.CreateTruncatedObjectList())); } if (exampleDisabledWarningCommandLine != null) { // '{0}' disables compiler warning(s) which are required by policy. A // compiler warning is typically required if it has a high likelihood of // flagging memory corruption, information disclosure, or double-free // vulnerabilities. To resolve this issue, enable the indicated warning(s) // by removing /Wxxxx switches (where xxxx is a warning id indicated here) // from your command line, and resolve any warnings subsequently raised // during compilation. // An example compiler command line triggering this check was: {1} // Modules triggering this check were: {2} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2007_Error_WarningsDisabled), exampleDisabledWarningCommandLine, disabledWarningModules.CreateTruncatedObjectList())); } }
public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context) { PEBinary target = context.PEBinary(); Pdb pdb = target.Pdb; Dictionary <string, TruncatedCompilandRecordList> vulnerabilityToModules = new Dictionary <string, TruncatedCompilandRecordList>(); TruncatedCompilandRecordList moduleList; foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails details = om.GetObjectModuleDetails(); if (details.Language != Language.C && details.Language != Language.Cxx) { continue; } if (!details.HasDebugInfo) { continue; } foreach (DisposableEnumerableView <SourceFile> sfView in pdb.CreateSourceFileIterator(om)) { SourceFile sf = sfView.Value; string fileName = Path.GetFileName(sf.FileName); if (!_files.Contains(fileName) || sf.HashType == HashType.None) { continue; } string hash = fileName + "#" + BitConverter.ToString(sf.Hash); VulnerableDependencyDescriptor descriptor; if (_filesToVulnerabilitiesMap.TryGetValue(hash, out descriptor)) { if (!vulnerabilityToModules.TryGetValue(descriptor.Id, out moduleList)) { moduleList = vulnerabilityToModules[descriptor.Id] = new TruncatedCompilandRecordList(); } moduleList.Add(om.CreateCompilandRecordWithSuffix(hash)); } } } if (vulnerabilityToModules.Count != 0) { foreach (string id in vulnerabilityToModules.Keys) { moduleList = vulnerabilityToModules[id]; VulnerableDependencyDescriptor descriptor = (VulnerableDependencyDescriptor)context.Policy.GetProperty(VulnerableDependencies)[id]; // '{0}' was built with a version of {1} which is subject to the following issues: {2}. // To resolve this, {3}. The source files that triggered this were: {4} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2002_Error), context.TargetUri.GetFileName(), descriptor.Name, descriptor.VulnerabilityDescription, descriptor.Resolution, moduleList.CreateSortedObjectList())); } return; } // '{0}' does not incorporate any known vulnerable dependencies, as configured by current policy. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2002_Pass), context.TargetUri.GetFileName())); }
public override void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb pdb = context.Pdb; if (pdb == null) { Errors.LogExceptionLoadingPdb(context, context.PdbParseException); return; } TruncatedCompilandRecordList noGsModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList unknownLanguageModules = new TruncatedCompilandRecordList(); foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails details = om.GetObjectModuleDetails(); if (details.Language == Language.Unknown) { // See if this module contributed to an executable section. // If not, we can ignore the module. if (pdb.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId)) { unknownLanguageModules.Add(om.CreateCompilandRecord()); } continue; } // Detection applies to C/C++ produced by MS compiler only if ((details.Language != Language.C) && (details.Language != Language.Cxx) || details.Compiler != "Microsoft (R) Optimizing Compiler") { continue; } if (!details.HasSecurityChecks && om.CreateChildIterator(SymTagEnum.SymTagFunction).Any()) { noGsModules.Add(om.CreateCompilandRecord()); } } if (unknownLanguageModules.Empty && noGsModules.Empty) { // '{0}' is a C or C++ binary built with the stack protector buffer security // feature enabled for all modules, making it more difficult for an attacker to // exploit stack buffer overflow memory corruption vulnerabilities. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2011_Pass))); return; } if (!unknownLanguageModules.Empty) { // '{0}' contains code from unknown language, preventing a comprehensive analysis of the // stack protector buffer security features. The language could not be identified for // the following modules: {1}. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2011_Error_UnknownModuleLanguage), unknownLanguageModules.CreateSortedObjectList())); } if (!noGsModules.Empty) { // '{0}' is a C or C++ binary built with the stack protector buffer security feature // disabled in one or more modules. The stack protector (/GS) is a security feature // of the compiler which makes it more difficult to exploit stack buffer overflow // memory corruption vulnerabilities. To resolve this issue, ensure that your code // is compiled with the stack protector enabled by supplying /GS on the Visual C++ // compiler command line. The affected modules were: {1} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2011_Error), noGsModules.ToString())); } }
/// <summary> /// Inspect the final output after binding. /// </summary> /// <param name="filePath">The file path to the final bound output.</param> /// <param name="pdb">The <see cref="Pdb"/> that contains source line numbers /// for the database and all rows.</param> public virtual void InspectDatabase(string filePath, Pdb pdb) { }
public override void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; if (context.Pdb == null) { Errors.LogExceptionLoadingPdb(context, context.PdbParseException); return; } Pdb di = context.Pdb; bool noCode = !di.CreateGlobalFunctionIterator().Any() && !di.ContainsExecutableSectionContribs(); if (noCode) { // '{0}' is a C or C++ binary that is not required to initialize the stack protection, as it does not contain executable code. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2013_Pass_NoCode), context.TargetUri.GetFileName())); return; } bool bHasGSCheck = di.CreateGlobalFunctionIterator( StackProtectionUtilities.GSCheckFunctionName, NameSearchOptions.nsfCaseSensitive).Any(); bool bHasGSInit = StackProtectionUtilities.GSInitializationFunctionNames.Any( functionName => di.CreateGlobalFunctionIterator(functionName, NameSearchOptions.nsfCaseSensitive).Any()); if (!bHasGSCheck && !bHasGSInit) { // '{0}' is a C or C++ binary that does enable the stack protection buffer // security feature. It is therefore not required to initialize the stack protector. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.NotApplicable, context, null, nameof(RuleResources.BA2013_NotApplicable_FeatureNotEnabled), context.TargetUri.GetFileName())); return; } if (!bHasGSInit) { // '{0}' is a C or C++ binary that does not initialize the stack protector. // The stack protector(/ GS) is a security feature of the compiler which // makes it more difficult to exploit stack buffer overflow memory // corruption vulnerabilities. The stack protector requires access to // entropy in order to be effective, which means a binary must initialize // a random number generator at startup, by calling __security_init_cookie() // as close to the binary's entry point as possible. Failing to do so will // result in spurious buffer overflow detections on the part of the stack // protector. To resolve this issue, use the default entry point provided // by the C runtime, which will make this call for you, or call // __security_init_cookie() manually in your custom entry point. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2013_Error), context.TargetUri.GetFileName())); return; } // '{0}' is a C or C++ binary built with the buffer security feature // that properly initializes the stack protecter. This has the //effect of increasing the effectiveness of the feature and reducing // spurious detections. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2013_Pass), context.TargetUri.GetFileName())); }
/// <summary> /// Main running method for the application. /// </summary> /// <param name="args">Commandline arguments to the application.</param> /// <returns>Returns the application error code.</returns> private int Run(string[] args) { try { // parse the command line this.ParseCommandLine(args); // exit if there was an error parsing the command line (otherwise the logo appears after error messages) if (this.messageHandler.EncounteredError) { return(this.messageHandler.LastErrorNumber); } if (0 == this.inputFiles.Count) { this.showHelp = true; } if (this.showLogo) { AppCommon.DisplayToolHeader(); } if (this.showHelp) { Console.WriteLine(SmokeStrings.HelpMessage); AppCommon.DisplayToolFooter(); return(this.messageHandler.LastErrorNumber); } foreach (string parameter in this.invalidArgs) { this.messageHandler.Display(this, WixWarnings.UnsupportedCommandLineArgument(parameter)); } this.invalidArgs = null; validator.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); // load any extensions bool validatorExtensionLoaded = false; foreach (string extension in this.extensionList) { WixExtension wixExtension = WixExtension.Load(extension); ValidatorExtension validatorExtension = wixExtension.ValidatorExtension; if (null != validatorExtension) { if (validatorExtensionLoaded) { throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, SmokeStrings.EXP_CannotLoadLinkerExtension, validatorExtension.GetType().ToString(), validator.Extension.ToString()), "ext"); } validator.Extension = validatorExtension; validatorExtensionLoaded = true; } } // set the message handlers validator.Extension.Message += new MessageEventHandler(this.messageHandler.Display); // disable ICE33 and ICE66 by default this.suppressICEs.Add("ICE33"); this.suppressICEs.Add("ICE66"); // set the ICEs string[] iceArray = new string[this.ices.Count]; this.ices.CopyTo(iceArray, 0); validator.ICEs = iceArray; // set the suppressed ICEs string[] suppressICEArray = new string[this.suppressICEs.Count]; this.suppressICEs.CopyTo(suppressICEArray, 0); validator.SuppressedICEs = suppressICEArray; // Load the pdb and assign the Output to the validator if (null != pdbPath) { string pdbFullPath = Path.GetFullPath(pdbPath); Pdb pdb = Pdb.Load(pdbFullPath, false, false); this.validator.Output = pdb.Output; } foreach (string inputFile in this.inputFiles) { // set the default cube file Assembly assembly = Assembly.GetExecutingAssembly(); string appDirectory = Path.GetDirectoryName(assembly.Location); if (this.addDefault) { switch (Path.GetExtension(inputFile).ToLower(CultureInfo.InvariantCulture)) { case msm: validator.AddCubeFile(Path.Combine(appDirectory, "mergemod.cub")); break; case msi: validator.AddCubeFile(Path.Combine(appDirectory, "darice.cub")); break; default: throw new WixException(WixErrors.UnexpectedFileExtension(inputFile, ".msi, .msm")); } } // print friendly message saying what file is being validated Console.WriteLine(Path.GetFileName(inputFile)); Stopwatch stopwatch = Stopwatch.StartNew(); try { validator.Validate(Path.GetFullPath(inputFile)); } catch (UnauthorizedAccessException) { this.messageHandler.Display(this, WixErrors.UnauthorizedAccess(Path.GetFullPath(inputFile))); } finally { stopwatch.Stop(); this.messageHandler.Display(this, WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds)); if (this.tidy) { if (!validator.DeleteTempFiles()) { Console.WriteLine(SmokeStrings.WAR_FailedToDeleteTempDir, validator.TempFilesLocation); } } else { Console.WriteLine(SmokeStrings.INF_TempDirLocatedAt, validator.TempFilesLocation); } } } } catch (WixException we) { this.messageHandler.Display(this, we.Error); } catch (Exception e) { this.messageHandler.Display(this, WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); if (e is NullReferenceException || e is SEHException) { throw; } } return(this.messageHandler.LastErrorNumber); }
public override void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb pdb = context.Pdb; if (pdb == null) { Errors.LogExceptionLoadingPdb(context, context.PdbParseException); return; } List <string> names = new List <string>(); foreach (DisposableEnumerableView <Symbol> functionView in pdb.CreateGlobalFunctionIterator()) { Symbol function = functionView.Value; if (function.IsManaged) { continue; } if (!function.IsSafeBuffers) { continue; } string functionName = function.GetUndecoratedName(); if (functionName == "__security_init_cookie" || context.Policy.GetProperty(ApprovedFunctionsThatDisableStackProtection).Contains(functionName)) { continue; } names.Add(functionName); } if (names.Count != 0) { string functionNames = string.Join(";", names); // '{0}' is a C or C++ binary built with function(s) ({1}) that disable the stack // protector. The stack protector (/GS) is a security feature of the compiler // which makes it more difficult to exploit stack buffer overflow memory // corruption vulnerabilities. Disabling the stack protector, even on a // function -by-function basis, is disallowed by SDL policy. To resolve this // issue, remove occurrences of __declspec(safebuffers) from your code. If the // additional code inserted by the stack protector has been shown in profiling // to cause a significant performance problem for your application, attempt to // move stack buffer modifications out of the hot path of execution to allow the // compiler to avoid inserting stack protector checks in these locations rather // than disabling the stack protector altogether. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2014_Error), context.TargetUri.GetFileName(), functionNames)); return; } // '{0}' is a C or C++ binary built with the stack protector buffer // security feature enabled which does not disable protection for // any individual functions (via __declspec(safebuffers), making it // more difficult for an attacker to exploit stack buffer overflow // memory corruption vulnerabilities. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2014_Pass), context.TargetUri.GetFileName())); }
/// <summary> /// Checks to make sure that the debug symbols match up with the MSI. /// This is to help in ensuring that error 1642 does not inexplicably appear. /// </summary> /// <remarks> /// This is meant to assist with Bug # 4792 /// http://wixtoolset.org/issues/4792/ /// </remarks> /// <param name="package"> /// The MSI currently being melted. /// </param> /// <param name="inputPdb"> /// The debug symbols package being compared against the <paramref name="package"/>. /// </param> /// <returns></returns> private static bool ValidateMsiMatchesPdb(InstallPackage package, Pdb inputPdb) { string msiPackageCode = (string)package.Property["PackageCode"]; foreach (Row pdbPropertyRow in inputPdb.Output.Tables["Property"].Rows) { if ("PackageCode" == (string)pdbPropertyRow.Fields[0].Data) { string pdbPackageCode = (string)pdbPropertyRow.Fields[1].Data; if (msiPackageCode != pdbPackageCode) { Console.WriteLine(MeltStrings.WAR_MismatchedPackageCode, msiPackageCode, pdbPackageCode); return false; } break; } } return true; }
public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context) { PEBinary target = context.PEBinary(); Machine reflectionMachineType = target.PE.Machine; // The current Machine enum does not have support for Arm64, so translate to our Machine enum ExtendedMachine machineType = (ExtendedMachine)reflectionMachineType; if (!machineType.CanBeMitigated()) { // QUESTION: // Machine HW is unsupported for mitigations... // should this be in the CanAnalyze() method or here and issue a warning? return; } Pdb pdb = target.Pdb; TruncatedCompilandRecordList masmModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList mitigationNotEnabledModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList mitigationDisabledInDebugBuild = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList mitigationExplicitlyDisabledModules = new TruncatedCompilandRecordList(); StringToVersionMap allowedLibraries = context.Policy.GetProperty(AllowedLibraries); foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails omDetails = om.GetObjectModuleDetails(); // See if the item is in our skip list. if (!string.IsNullOrEmpty(om.Lib)) { string libFileName = string.Concat(System.IO.Path.GetFileName(om.Lib), ",", omDetails.Language.ToString()).ToLowerInvariant(); Version minAllowedVersion; if (allowedLibraries.TryGetValue(libFileName, out minAllowedVersion) && omDetails.CompilerVersion >= minAllowedVersion) { continue; } } Version actualVersion; Language omLanguage = omDetails.Language; // We already opted-out of IL Only binaries, so only check for native languages // or those that can appear in mixed binaries. switch (omLanguage) { case Language.C: case Language.Cxx: { if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftNativeCompiler) { // TODO: https://github.com/Microsoft/binskim/issues/114 continue; } else { actualVersion = omDetails.CompilerVersion; } break; } case Language.MASM: { masmModules.Add(om.CreateCompilandRecord()); continue; } case Language.LINK: { // Linker is not involved in the mitigations, so no need to check version or switches at this time. continue; } case Language.CVTRES: { // Resource compiler is not involved in the mitigations, so no need to check version or switches at this time. continue; } case Language.HLSL: { // HLSL compiler is not involved in the mitigations, so no need to check version or switches at this time. continue; } // Mixed binaries (/clr) can contain non C++ compilands if they are linked in via netmodules // .NET IL should be mitigated by the runtime if any mitigations are necessary // At this point simply accept them as safe until this is disproven. case Language.CSharp: case Language.MSIL: case Language.ILASM: { continue; } case Language.Unknown: { // The linker may emit debug information for modules from static libraries that do not contribute to actual code. // do not contribute to actual code. Currently these come back as Language.Unknown :( // TODO: https://github.com/Microsoft/binskim/issues/116 continue; } default: { // TODO: https://github.com/Microsoft/binskim/issues/117 // Review unknown languages for this and all checks } continue; } // Get the appropriate compiler version against which to check this compiland. // check that we are greater than or equal to the first fully supported release: 15.6 first Version omVersion = omDetails.CompilerVersion; CompilerMitigations availableMitigations = GetAvailableMitigations(context, machineType, omVersion); if (availableMitigations == CompilerMitigations.None) { // Built with a compiler version {0} that does not support any Spectre // mitigations. We do not report here. BA2006 will fire instead. continue; } string[] mitigationSwitches = new string[] { "/Qspectre", "/guardspecload" }; SwitchState effectiveState; // Go process the command line to check for switches effectiveState = omDetails.GetSwitchState(mitigationSwitches, null, SwitchState.SwitchDisabled, OrderOfPrecedence.LastWins); if (effectiveState == SwitchState.SwitchDisabled) { SwitchState QSpectreState = SwitchState.SwitchNotFound; SwitchState d2guardspecloadState = SwitchState.SwitchNotFound; if (availableMitigations.HasFlag(CompilerMitigations.QSpectreAvailable)) { QSpectreState = omDetails.GetSwitchState(mitigationSwitches[0] /*"/Qspectre"*/, OrderOfPrecedence.LastWins); } if (availableMitigations.HasFlag(CompilerMitigations.D2GuardSpecLoadAvailable)) { // /d2xxxx options show up in the PDB without the d2 string // So search for just /guardspecload d2guardspecloadState = omDetails.GetSwitchState(mitigationSwitches[1] /*"/guardspecload"*/, OrderOfPrecedence.LastWins); } // TODO: https://github.com/Microsoft/binskim/issues/119 // We should flag cases where /d2guardspecload is enabled but the // toolset supports /qSpectre (which should be preferred). if (QSpectreState == SwitchState.SwitchNotFound && d2guardspecloadState == SwitchState.SwitchNotFound) { // Built with tools that support the Spectre mitigations but these have not been enabled. mitigationNotEnabledModules.Add(om.CreateCompilandRecord()); } else { // Built with the Spectre mitigations explicitly disabled. mitigationExplicitlyDisabledModules.Add(om.CreateCompilandRecord()); } continue; } if (!availableMitigations.HasFlag(CompilerMitigations.NonoptimizedCodeMitigated)) { string[] OdSwitches = { "/Od" }; // These switches override /Od - there is no one place to find this information on msdn at this time. string[] OptimizeSwitches = { "/O1", "/O2", "/Ox", "/Og" }; bool debugEnabled = false; if (omDetails.GetSwitchState(OdSwitches, OptimizeSwitches, SwitchState.SwitchEnabled, OrderOfPrecedence.LastWins) == SwitchState.SwitchEnabled) { debugEnabled = true; } if (debugEnabled) { // Built with /Od which disables Spectre mitigations. mitigationDisabledInDebugBuild.Add(om.CreateCompilandRecord()); continue; } } } string line; var sb = new StringBuilder(); if (!mitigationExplicitlyDisabledModules.Empty) { // The following modules were compiled with Spectre // mitigations explicitly disabled: {0} line = string.Format( RuleResources.BA2024_Error_SpectreMitigationExplicitlyDisabled, mitigationExplicitlyDisabledModules.CreateSortedObjectList()); sb.AppendLine(line); } if (!mitigationNotEnabledModules.Empty) { // The following modules were compiled with a toolset that supports // /Qspectre but the switch was not enabled on the command-line: {0} line = string.Format( RuleResources.BA2024_Error_SpectreMitigationNotEnabled, mitigationNotEnabledModules.CreateSortedObjectList()); sb.AppendLine(line); } if (!mitigationDisabledInDebugBuild.Empty) { // The following modules were compiled with optimizations disabled(/ Od), // a condition that disables Spectre mitigations: {0} line = string.Format( RuleResources.BA2024_Error_OptimizationsDisabled, mitigationDisabledInDebugBuild.CreateSortedObjectList()); sb.AppendLine(line); } if ((context.Policy.GetProperty(Reporting) & ReportingOptions.WarnIfMasmModulesPresent) == ReportingOptions.WarnIfMasmModulesPresent && !masmModules.Empty) { line = string.Format( RuleResources.BA2024_Error_MasmModulesDetected, masmModules.CreateSortedObjectList()); sb.AppendLine(line); } if (sb.Length > 0) { // '{0}' was compiled with one or more modules that do not properly enable code // generation mitigations for speculative execution side-channel attack (Spectre) // vulnerabilities. Spectre attacks can compromise hardware-based isolation, // allowing non-privileged users to retrieve potentially sensitive data from the // CPU cache. To resolve the issue, provide the /Qspectre switch on the compiler // command-line (or /d2guardspecload in cases where your compiler supports this // switch and it is not possible to update to a toolset that supports /Qspectre). // The following modules are out of policy: {1} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2024_Error), context.TargetUri.GetFileName(), sb.ToString())); return; } // All linked modules ‘{0}’ were compiled with mitigations enabled that help prevent Spectre (speculative execution side-channel attack) vulnerabilities. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2024_Pass), context.TargetUri.GetFileName())); }
public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context) { PEBinary target = context.PEBinary(); Pdb pdb = target.Pdb; Version minCompilerVersion; minCompilerVersion = (target.PE.IsXBox) ? context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_COMPILER_VER] : context.Policy.GetProperty(MinimumToolVersions)[MIN_COMPILER_VER]; TruncatedCompilandRecordList badModuleList = new TruncatedCompilandRecordList(); StringToVersionMap allowedLibraries = context.Policy.GetProperty(AllowedLibraries); foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails omDetails = om.GetObjectModuleDetails(); if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftNativeCompiler) { continue; } // See if the item is in our skip list if (!string.IsNullOrEmpty(om.Lib)) { string libFileName = string.Concat(System.IO.Path.GetFileName(om.Lib), ",", omDetails.Language.ToString()).ToLowerInvariant(); Version minAllowedVersion; if (allowedLibraries.TryGetValue(libFileName, out minAllowedVersion) && omDetails.CompilerVersion >= minAllowedVersion) { continue; } } Version actualVersion; Version minimumVersion; Language omLanguage = omDetails.Language; switch (omLanguage) { case Language.C: case Language.Cxx: actualVersion = Minimum(omDetails.CompilerVersion, omDetails.CompilerFrontEndVersion); minimumVersion = minCompilerVersion; break; default: continue; } bool foundIssue = actualVersion < minimumVersion; AdvancedMitigations advancedMitigations = context.Policy.GetProperty(AdvancedMitigationsEnforced); if (!foundIssue && (advancedMitigations & AdvancedMitigations.Spectre) == AdvancedMitigations.Spectre) { ExtendedMachine machineType = (ExtendedMachine)target.PE.Machine; // Current toolchain is within the version range to validate. // Now we'll retrieve relevant compiler mitigation details to // ensure this object module's build and revision meet // expectations. CompilerMitigations newMitigationData = EnableSpectreMitigations.GetAvailableMitigations(context, machineType, actualVersion); // Current compiler version does not support Spectre mitigations. foundIssue = !newMitigationData.HasFlag(CompilerMitigations.D2GuardSpecLoadAvailable) && !newMitigationData.HasFlag(CompilerMitigations.QSpectreAvailable); if (foundIssue) { // Get the closest compiler version that has mitigations--i.e. if the user is using a 19.0 (VS2015) compiler, we should be recommending an upgrade to the // 19.0 version that has the mitigations, not an upgrade to a 19.10+ (VS2017) compiler. // Limitation--if there are multiple 'upgrade to' versions to recommend, this just going to give users the last one we see in the error. minCompilerVersion = EnableSpectreMitigations.GetClosestCompilerVersionWithSpectreMitigations(context, machineType, actualVersion); // Indicates Spectre mitigations are not supported on this platform. We won't flag this case. if (minCompilerVersion == null) { foundIssue = false; } } } if (foundIssue) { // built with {0} compiler version {1} (Front end version: {2}) badModuleList.Add( om.CreateCompilandRecordWithSuffix( String.Format(CultureInfo.InvariantCulture, RuleResources.BA2006_Error_BadModule, omLanguage, omDetails.CompilerVersion, omDetails.CompilerFrontEndVersion))); } } if (!badModuleList.Empty) { // '{0}' was compiled with one or more modules which were not built using // minimum required tool versions (compiler version {1}). More recent toolchains // contain mitigations that make it more difficult for an attacker to exploit // vulnerabilities in programs they produce. To resolve this issue, compile // and /or link your binary with more recent tools. If you are servicing a // product where the tool chain cannot be modified (e.g. producing a hotfix // for an already shipped version) ignore this warning. Modules built outside // of policy: {2} context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2006_Error), context.TargetUri.GetFileName(), minCompilerVersion.ToString(), badModuleList.CreateSortedObjectList())); return; } // All linked modules of '{0}' generated by the Microsoft front-end // satisfy configured policy (compiler minimum version {1}). context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2006_Pass), context.TargetUri.GetFileName(), minCompilerVersion.ToString())); }
/// <summary> /// Extract binary data from tables with a Name and Data column in them. /// </summary> /// <param name="inputPdb">A reference to a <see cref="Pdb"/> as output. Paths (Data properties) will be modified in this object.</param> /// <param name="package">The installer database to rip from.</param> /// <param name="exportPath">The full path where files will be exported to.</param> /// <param name="tableName">The name of the table to export.</param> private static void MeltBinaryTable(Pdb inputPdb, InstallPackage package, string exportPath, string tableName) { if (string.IsNullOrEmpty(tableName)) { throw new ArgumentNullException("tableName"); } if (string.IsNullOrEmpty(exportPath)) { throw new ArgumentNullException("exportPath"); } if (null == package) { throw new ArgumentNullException("package"); } if (null == inputPdb) { throw new ArgumentNullException("inputPdb"); } Table pdbTable = inputPdb.Output.Tables[tableName]; if (null == pdbTable) { Console.WriteLine("Table {0} does not exist.", tableName); return; } try { Directory.CreateDirectory(exportPath); Melt.ExtractFilesInBinaryTable(package, null, tableName, exportPath); IDictionary<string, string> paths = package.GetFilePaths(exportPath); if (null != paths) { foreach (Row row in pdbTable.Rows) { string filename = (string)row.Fields[0].Data; row.Fields[1].Data = paths[filename]; } } } catch (Exception ex) { Console.WriteLine("An error occured extracting the {0} binary table from the install package.", tableName); Console.WriteLine(ex.Message); } }
/// <summary> /// Main running method for the application. /// </summary> /// <param name="args">Commandline arguments to the application.</param> /// <returns>Returns the application error code.</returns> private int Run(string[] args) { Microsoft.Tools.WindowsInstallerXml.Binder binder = null; Differ differ = null; Unbinder unbinder = null; TempFileCollection tempFileCollection = null; try { // parse the command line this.ParseCommandLine(args); // validate the inputs if (this.xmlInputs && this.adminImage) { this.messageHandler.Display(this, WixErrors.IllegalCommandlineArgumentCombination("a", "xi")); this.showHelp = true; } string[] allValidExtensions = new string[] { wixMstExtension, wixOutExtension, wixPdbExtension, msiExtension }; string[] expectedSingleInputExtensions = new string[] { wixMstExtension, wixOutExtension }; string[] expectedDoubleInputXmlExtensions = new string[] { wixOutExtension, wixPdbExtension }; string[] expectedDoubleInputMsiExtensions = new string[] { msiExtension }; // Validate that all inputs have the correct extension and we dont have too many inputs. if (1 == this.inputFiles.Count) { string inputFile = this.inputFiles[0]; bool hasValidExtension = false; foreach (string extension in expectedSingleInputExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; break; } } if (!hasValidExtension) { bool missingInput = false; // Check if its using an extension that could be valid in other scenarios. foreach (string validExtension in allValidExtensions) { if (String.Equals(Path.GetExtension(inputFile), validExtension, StringComparison.OrdinalIgnoreCase)) { this.messageHandler.Display(this, WixErrors.WrongFileExtensionForNumberOfInputs(Path.GetExtension(inputFile), inputFile)); missingInput = true; break; } } if (!missingInput) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(inputFile, String.Join(", ", expectedSingleInputExtensions))); } } } else if (2 == this.inputFiles.Count) { foreach (string inputFile in inputFiles) { bool hasValidExtension = false; string[] expectedExtensions = allValidExtensions; if (this.xmlInputs) { foreach (string extension in expectedDoubleInputXmlExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; expectedExtensions = expectedDoubleInputXmlExtensions; break; } } } else { foreach (string extension in expectedDoubleInputMsiExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; expectedExtensions = expectedDoubleInputMsiExtensions; break; } } } if (!hasValidExtension) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(inputFile, String.Join(", ", expectedExtensions))); } } } else { this.showHelp = true; } // exit if there was an error parsing the command line or with a file extension (otherwise the logo appears after error messages) if (this.messageHandler.EncounteredError) { return(this.messageHandler.LastErrorNumber); } if (null == this.outputFile) { this.showHelp = true; } if (this.showLogo) { AppCommon.DisplayToolHeader(); } if (this.showHelp) { Console.WriteLine(TorchStrings.HelpMessage); AppCommon.DisplayToolFooter(); return(this.messageHandler.LastErrorNumber); } foreach (string parameter in this.invalidArgs) { this.messageHandler.Display(this, WixWarnings.UnsupportedCommandLineArgument(parameter)); } this.invalidArgs = null; binder = new Microsoft.Tools.WindowsInstallerXml.Binder(); differ = new Differ(); unbinder = new Unbinder(); // load any extensions foreach (string extension in this.extensionList) { WixExtension wixExtension = WixExtension.Load(extension); unbinder.AddExtension(wixExtension); binder.AddExtension(wixExtension); differ.AddExtension(wixExtension); } binder.Message += new MessageEventHandler(this.messageHandler.Display); differ.Message += new MessageEventHandler(this.messageHandler.Display); unbinder.Message += new MessageEventHandler(this.messageHandler.Display); binder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); unbinder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); tempFileCollection = new TempFileCollection(Environment.GetEnvironmentVariable("WIX_TEMP")); binder.WixVariableResolver = new WixVariableResolver(); differ.PreserveUnchangedRows = this.preserveUnchangedRows; differ.ShowPedanticMessages = this.showPedanticMessages; unbinder.SuppressExtractCabinets = true; unbinder.IsAdminImage = this.adminImage; if (null == this.exportBasePath) { this.exportBasePath = tempFileCollection.BasePath; } // load and process the inputs Output transform; if (1 == this.inputFiles.Count) { transform = Output.Load(this.inputFiles[0], false, false); if (OutputType.Transform != transform.Type) { this.messageHandler.Display(this, WixErrors.InvalidWixTransform(this.inputFiles[0])); return(this.messageHandler.LastErrorNumber); } } else // 2 inputs { Output targetOutput; Output updatedOutput; if (this.xmlInputs) { // load the target database if (String.Equals(Path.GetExtension(inputFiles[0]), wixPdbExtension, StringComparison.OrdinalIgnoreCase)) { Pdb targetPdb = Pdb.Load(this.inputFiles[0], false, false); targetOutput = targetPdb.Output; } else { targetOutput = Output.Load(this.inputFiles[0], false, false); } // load the updated database if (String.Equals(Path.GetExtension(inputFiles[1]), wixPdbExtension, StringComparison.OrdinalIgnoreCase)) { Pdb updatedPdb = Pdb.Load(this.inputFiles[1], false, false); updatedOutput = updatedPdb.Output; } else { updatedOutput = Output.Load(this.inputFiles[1], false, false); } this.xmlOutput = true; } else { // load the target database targetOutput = unbinder.Unbind(this.inputFiles[0], OutputType.Product, Path.Combine(this.exportBasePath, "targetBinaries")); // load the updated database updatedOutput = unbinder.Unbind(this.inputFiles[1], OutputType.Product, Path.Combine(this.exportBasePath, "updatedBinaries")); } // diff the target and updated databases transform = differ.Diff(targetOutput, updatedOutput, this.validationFlags); if (null == transform.Tables || 0 >= transform.Tables.Count) { throw new WixException(WixErrors.NoDifferencesInTransform(transform.SourceLineNumbers)); } } // output the transform if (null != transform) { // If either the user selected xml output or gave xml input, save as xml output. // With xml inputs, many funtions of the binder have not been performed on the inputs (ie. file sequencing). This results in bad IDT files which cannot be put in a transform. if (this.xmlOutput) { transform.Save(this.outputFile, null, null, tempFileCollection.BasePath); } else { binder.Bind(transform, this.outputFile); } } } catch (WixException we) { if (we is WixInvalidIdtException) { // make sure the IDT files stay around this.tidy = false; } this.messageHandler.Display(this, we.Error); } catch (Exception e) { // make sure the files stay around for debugging this.tidy = false; this.messageHandler.Display(this, WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); if (e is NullReferenceException || e is SEHException) { throw; } } finally { if (null != binder) { if (this.tidy) { if (!binder.DeleteTempFiles()) { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, binder.TempFilesLocation); } } else { Console.WriteLine(TorchStrings.INF_BinderTempDirLocatedAt, binder.TempFilesLocation); } } if (null != unbinder) { if (this.tidy) { if (!unbinder.DeleteTempFiles()) { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, binder.TempFilesLocation); } } else { Console.WriteLine(TorchStrings.INF_UnbinderTempDirLocatedAt, binder.TempFilesLocation); } } if (null != tempFileCollection) { if (this.tidy) { try { Directory.Delete(tempFileCollection.BasePath, true); } catch (DirectoryNotFoundException) { // if the path doesn't exist, then there is nothing for us to worry about } catch { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, tempFileCollection.BasePath); } } else { Console.WriteLine(TorchStrings.INF_TorchTempDirLocatedAt, tempFileCollection.BasePath); } } } return(this.messageHandler.LastErrorNumber); }
/// <summary> /// The library "hash". This is an XOR of the 1st 16 bytes of all source file hashes. /// </summary> public byte[] GetObjectFileHash(Pdb session) { bool hashPresent = false; byte[] res = new byte[16]; // go through all source files in the lib and XOR their hashes (if present) // NB: there are a couple of interesting cases worth considering: // 1. .h files may be used to build more than one object files in the lib // and when XOR'ed they cancel each other out; // 2. Some source files may not have hashes, in which case our results won't be totally reliable. foreach (DisposableEnumerableView<SourceFile> sfView in session.CreateSourceFileIterator(this)) { SourceFile sf = sfView.Value; if (sf.HashType != HashType.None) { byte[] hash = sf.Hash; if ((hash == null) || (hash.Length < 16)) { throw new PdbParseException("Unexpected hash length for file " + sf); } for (int j = 0; j < 16; j++) { res[j] ^= hash[j]; } hashPresent = true; } } if (!hashPresent) { return null; } return res; }