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"));
        }
Beispiel #2
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target   = context.PEBinary();
            PEHeader peHeader = target.PE.PEHeaders.PEHeader;

            if ((peHeader.DllCharacteristics & DllCharacteristics.NxCompatible /*IMAGE_DLLCHARACTERISTICS_NX_COMPAT*/) == 0)
            {
                // '{0}' is not marked NX compatible. The NXCompat bit, also known as "Data Execution Prevention"
                // (DEP) or "Execute Disable" (XD), is a processor feature that allows a program to mark a piece
                // of memory as non - executable. This helps mitigate memory corruption vulnerabilities by
                // preventing an attacker from supplying direct shellcode in their exploit, because the exploit
                // comes in the form of input data to the exploited program on a data segment, rather than on an
                // executable code segment. To resolve this issue, ensure that your tool chain is configured to mark
                //your binaries as NX compatible, e.g. by passing / NXCOMPAT to the C / C++ linker.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2016_Error),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            // '{0}' is marked as NX compatible.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2016_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        public sealed override AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            AnalysisApplicability result = base.CanAnalyze(context, out reasonForNotAnalyzing);

            if (result != AnalysisApplicability.ApplicableToSpecifiedTarget)
            {
                return(result);
            }

            PE portableExecutable = context.PEBinary().PE;

            result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

            reasonForNotAnalyzing = MetadataConditions.ImageIsWixBinary;
            if (portableExecutable.IsWixBinary)
            {
                return(result);
            }

            reasonForNotAnalyzing = MetadataConditions.ImageIsILLibraryAssembly;
            if (portableExecutable.IsILLibrary)
            {
                return(result);
            }

            reasonForNotAnalyzing = MetadataConditions.ImageIsDotNetCoreBootstrapExe;
            if (portableExecutable.IsDotNetCoreBootstrapExe)
            {
                return(result);
            }

            reasonForNotAnalyzing = null;
            return(AnalysisApplicability.ApplicableToSpecifiedTarget);
        }
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            PE       pe     = target.PE;

            // Analysis may return one or more failures, each of which uses a format
            // string stored in the MessageResourceNames array above to produce a result.
            // By convention, we recapitulate that format string when we return the
            // associated result, to document the specific failure or pass condition.

            if (!this.IsSecure(target))
            {
                // '{0}' is not secure for some reaons.
                // To resolve this issue, pass /beEvenMoreSecure on both the compiler
                // and linker command lines. Binaries also require the
                // /beSecure option in order to enable the enhanced setting.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BAXXXX_Error),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            // '{0}' enables /beEvenMoreSecure on both the compiler and linker
            // command-lines, preventing a broad range of conditions that
            // bad actors can use to engage in their malignant, unfortunately
            // often-profitable foolishness.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BAXXXX_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        private void AnalyzeManagedPE(BinaryAnalyzerContext context)
        {
            Version minCscVersion =
                context.Policy.GetProperty(MinimumToolVersions)[nameof(Language.CSharp)];

            PE pe = context.PEBinary().PE;

            if (pe.LinkerVersion < minCscVersion)
            {
                // '{0}' is a managed assembly that was compiled with an outdated toolchain
                // ({1}) that does not support security features (such as SHA256 PDB
                // checksums and reproducible builds) that must be enabled by policy. To
                // resolve this issue, compile with more recent tools ({2} or later).
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2006_Error_OutdatedCsc),
                                                             context.TargetUri.GetFileName(),
                                                             pe.LinkerVersion.ToString(),
                                                             minCscVersion.ToString()));

                return;
            }

            // '{0}' is a managed assembly that was compiled with toolchain ({1}) that supports all security features that must be enabled by policy.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2006_Pass_Csc),
                                                         context.TargetUri.GetFileName(),
                                                         pe.LinkerVersion.ToString()));
        }
Beispiel #6
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target   = context.PEBinary();
            PEHeader peHeader = target.PE.PEHeaders.PEHeader;

            ulong imageBase = peHeader.ImageBase;

            if (imageBase <= 0xFFFFFFFF)
            {
                // '{0}' is a 64-bit image with a preferred base address below the 4GB boundary.
                // Having a preferred base address below this boundary triggers a compatibility
                // mode in Address Space Layout Randomization (ASLR) on recent versions of
                // Windows that reduces the number of locations to which ASLR may relocate the
                // binary. This reduces the effectiveness of ASLR at mitigating memory corruption
                // vulnerabilities. To resolve this issue, either use the default preferred base
                // address by removing any uses of /baseaddress from compiler command lines, or
                // /BASE from linker command lines (recommended), or configure your program to
                // start at a base address above 4GB when compiled for 64 bit platforms (by
                // changing the constant passed to /baseaddress / /BASE). Note that if you choose
                // to continue using a custom preferred base address, you will need to make this
                // modification only for 64-bit builds, as base addresses above 4GB are not valid
                // for 32-bit binaries.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2001_Error),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            // '{0}' is marked as NX compatible.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2001_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        public override void Analyze(BinaryAnalyzerContext context)
        {
            // Uses Windows Certificate Interop
            BinaryParsers.PlatformSpecificHelpers.ThrowIfNotOnWindows();
            PEBinary target = context.PEBinary();

            Native.WINTRUST_DATA winTrustData;
            string algorithmName, filePath;

            filePath = target.PE.FileName;

            winTrustData = new Native.WINTRUST_DATA();

            if (InvokeVerifyAction(context, filePath, out winTrustData, out algorithmName))
            {
                // '{0}' appears to be signed securely by a trusted publisher with no
                // verification or time stamp errors. Revocation checking was performed
                // on the entire certificate chain, excluding the root certificate.
                // The following digitial signature algorithms were detected: {1}
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                             nameof(RuleResources.BA2022_Pass),
                                                             context.TargetUri.GetFileName(),
                                                             algorithmName));
            }
        }
        public sealed override void Analyze(BinaryAnalyzerContext context)
        {
            // Uses PDB Parsing.
            BinaryParsers.PlatformSpecificHelpers.ThrowIfNotOnWindows();
            PEBinary target = context.PEBinary();

            if (!string.IsNullOrEmpty(target.Pdb?.LoadTrace))
            {
                LogPdbLoadTrace(
                    context,
                    pdbLoadSucceeded: true,
                    target.Pdb.LoadTrace);

                // Set the trace to null so that we only emit it once.
                target.Pdb.LoadTrace = null;
            }

            if (target.Pdb == null)
            {
                LogExceptionLoadingPdb(context, target.PdbParseException);
                return;
            }

            this.AnalyzePortableExecutableAndPdb(context);
        }
        private bool Validate32BitImage(BinaryAnalyzerContext context)
        {
            PEBinary    target       = context.PEBinary();
            PEHeader    peHeader     = target.PE.PEHeaders.PEHeader;
            SafePointer sp           = new SafePointer(target.PE.ImageBytes, peHeader.LoadConfigTableDirectory.RelativeVirtualAddress);
            SafePointer loadConfigVA = target.PE.RVA2VA(sp);
            ImageLoadConfigDirectory32 loadConfig = new ImageLoadConfigDirectory32(peHeader, loadConfigVA);

            UInt32 cookieVA    = (UInt32)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.SecurityCookie);
            UInt32 baseAddress = (UInt32)peHeader.ImageBase;

            // we need to find the offset in the file based on the cookie's VA
            UInt32        sectionSize, sectionVA = 0;
            SectionHeader ish = new SectionHeader();
            bool          foundCookieSection = false;

            foreach (SectionHeader t in target.PE.PEHeaders.SectionHeaders)
            {
                sectionVA   = (UInt32)t.VirtualAddress + baseAddress;
                sectionSize = (UInt32)t.VirtualSize;
                if ((cookieVA >= sectionVA) &&
                    (cookieVA < sectionVA + sectionSize))
                {
                    ish = t;
                    foundCookieSection = true;
                    break;
                }
            }

            if (!foundCookieSection)
            {
                LogCouldNotLocateCookie(context);
                return(false);
            }

            UInt64      fileCookieOffset = (cookieVA - baseAddress) - (sectionVA - baseAddress) + (UInt32)ish.PointerToRawData;
            SafePointer fileCookiePtr    = loadConfigVA;

            fileCookiePtr.Address = (int)fileCookieOffset;

            SafePointer boundsCheck = fileCookiePtr + 4;

            if (!CookieOffsetValid(context, boundsCheck))
            {
                return(false);
            }

            UInt32 cookie = BitConverter.ToUInt32(fileCookiePtr.GetBytes(4), 0);

            if (!StackProtectionUtilities.DefaultCookiesX86.Contains(cookie) && target.PE.Machine == Machine.I386)
            {
                LogFailure(context, cookie.ToString("x"));
                return(false);
            }

            return(true);
        }
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();

            string         executableImportSection = null;
            PEHeader       peHeader    = target.PE.PEHeaders.PEHeader;
            DirectoryEntry importTable = peHeader.ImportTableDirectory;

            if (importTable.RelativeVirtualAddress != 0 && target.PE.PEHeaders.SectionHeaders != null)
            {
                int importSize = peHeader.ImportTableDirectory.Size;
                foreach (SectionHeader sectionHeader in target.PE.PEHeaders.SectionHeaders)
                {
                    SectionCharacteristics memExecute = SectionCharacteristics.MemExecute;
                    if ((sectionHeader.SectionCharacteristics & memExecute) == 0)
                    {
                        continue;
                    }

                    int size    = sectionHeader.SizeOfRawData;
                    int address = sectionHeader.VirtualAddress;

                    if ((address <= importTable.RelativeVirtualAddress) &&
                        (address + size >= importTable.RelativeVirtualAddress + importTable.Size))
                    {
                        // Our import section is in a writable section - bad
                        executableImportSection = sectionHeader.Name;
                        break;
                    }
                }
            }

            if (executableImportSection != null)
            {
                // '{0}' has the imports section marked executable. Because the loader will always mark
                // the imports section as writable, it is important to mark this section as non-executable,
                // so that an attacker cannot place shellcode here. To resolve this issue, ensure that your
                //program does not mark the imports section as executable. Look for uses of /SECTION or
                // /MERGE on the linker command line, or #pragma segment in source code, which change the
                // imports section to be executable, or which merge the ".rdata" segment into an executable
                // section.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2010_Error),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            // '{0}' does not have an imports section that is marked as executable.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2010_Pass),
                                                         context.TargetUri.GetFileName()));
        }
 private void LogInvalidCookieOffset(BinaryAnalyzerContext context)
 {
     // The security cookie offset for '{0}' exceeds the size of the image.
     // The file may be corrupted or processed by an executable packer.
     // feature therefore could not be verified. The file was possibly packed by: {1}
     context.Logger.Log(this,
                        RuleUtilities.BuildResult(ResultLevel.Warning, context, null,
                                                  nameof(RuleResources.BA2012_Warning_InvalidSecurityCookieOffset),
                                                  context.TargetUri.GetFileName(),
                                                  context.PEBinary().PE.Packer.ToString()));
 }
 private void LogInvalidCookieOffsetForKnownPackedFile(BinaryAnalyzerContext context)
 {
     // '{0}' appears to be a packed C or C++ binary that reports a security cookie
     // offset that exceeds the size of the packed file. Use of the stack protector (/GS)
     // feature therefore could not be verified. The file was possibly packed by: {1}
     context.Logger.Log(this,
                        RuleUtilities.BuildResult(ResultLevel.Warning, context, null,
                                                  nameof(RuleResources.BA2012_Warning_InvalidSecurityCookieOffset),
                                                  context.TargetUri.GetFileName(),
                                                  context.PEBinary().PE.Packer.ToString()));
 }
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PE pe = context.PEBinary().PE;

            if (pe.IsManaged && !pe.IsMixedMode)
            {
                AnalyzeManagedAssemblyAndPdb(context);
                return;
            }

            AnalyzeNativeBinaryAndPdb(context);
        }
Beispiel #14
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();

            var sectionHeaders = target.PE.PEHeaders.SectionHeaders;

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

            if (sectionHeaders != null)
            {
                foreach (SectionHeader sectionHeader in sectionHeaders)
                {
                    SectionCharacteristics wsFlags = SectionCharacteristics.MemWrite | SectionCharacteristics.MemShared;

                    if ((sectionHeader.SectionCharacteristics & wsFlags) == wsFlags) // IMAGE_SCN_MEM_WRITE & IMAGE_SCN_MEM_SHARED
                    {
                        badSections.Add(sectionHeader.Name);
                    }
                }
            }

            if (badSections.Count == 0)
            {
                // Image '{0}' contains no data or code sections marked as both shared and writable.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                             nameof(RuleResources.BA2019_Pass),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            string badSectionsText = String.Join(";", badSections);

            // '{0}' contains PE section(s) ({1}) that are both writable and executable.
            // Writable and executable memory segments make it easier for an attacker
            // to exploit memory corruption vulnerabilities, because it may provide an
            // attacker executable location(s) to inject shellcode. To resolve this
            // issue, configure your tools to not emit memory sections that are writable
            // and executable. For example, look for uses of /SECTION on the linker
            // command line for C and C++ programs, or #pragma section in C and C++
            // source code, which mark a section with both attributes. Enabling
            // incremental linking via the /INCREMENTAL argument (the default for
            // Microsoft Visual Studio debug build) can also result in a writable and
            // executable section named 'textbss'. For this case, disable incremental
            // linking (or analyze an alternate build configuration that disables this
            // feature) to resolve the problem.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                         nameof(RuleResources.BA2019_Error),
                                                         context.TargetUri.GetFileName(),
                                                         badSectionsText));
        }
 public override AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
 {
     if (context.IsPE())
     {
         PEBinary target = context.PEBinary();
         return(this.CanAnalyzePE(target, context.Policy, out reasonForNotAnalyzing));
     }
     else
     {
         reasonForNotAnalyzing = MetadataConditions.ImageIsNotPE;
         return(AnalysisApplicability.NotApplicableToSpecifiedTarget);
     }
 }
        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;
            }

            AnalyzePortableExecutableAndPdb(context);
        }
        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();
            }
        }
Beispiel #18
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            // Throw a platform unsupported exception--FileVersionInfo does not behave "correctly" on Linux.
            BinaryParsers.PlatformSpecificHelpers.ThrowIfNotOnWindows();

            PEBinary target = context.PEBinary();

            string fileName = Path.GetFileName(target.PE.FileName);

            Version minimumVersion;

            if (context.Policy.GetProperty(VulnerableBinaries).TryGetValue(fileName, out minimumVersion))
            {
                FileVersionInfo fvi              = FileVersionInfo.GetVersionInfo(Path.GetFullPath(target.PE.FileName));
                string          rawVersion       = fvi.FileVersion ?? string.Empty;
                Match           sanitizedVersion = s_versionRegex.Match(rawVersion);
                if (!sanitizedVersion.Success)
                {
                    // Version information for '{0}' could not be parsed. The binary therefore could not be verified not to be an obsolete binary that is known to be vulnerable to one or more security problems.
                    context.Logger.Log(this,
                                       RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                                 nameof(RuleResources.BA2005_Error_CouldNotParseVersion),
                                                                 context.TargetUri.GetFileName()));
                    return;
                }

                var actualVersion = new Version(sanitizedVersion.Value);
                if (actualVersion < minimumVersion)
                {
                    // '{0}' appears to be an obsolete library (version {1}) for which there are one
                    // or more known security vulnerabilities. To resolve this issue, obtain a version
                    //of {0} that is version {2} or greater. If this binary is not in fact {0},
                    // ignore this warning.
                    context.Logger.Log(this,
                                       RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                                 nameof(RuleResources.BA2005_Error),
                                                                 context.TargetUri.GetFileName(),
                                                                 sanitizedVersion.Value,
                                                                 minimumVersion.ToString()));
                    return;
                }
            }

            // '{0}' is not known to be an obsolete binary that is
            //vulnerable to one or more security problems.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2005_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target   = context.PEBinary();
            PEHeader peHeader = target.PE.PEHeaders.PEHeader;

            if (peHeader.LoadConfigTableDirectory.RelativeVirtualAddress == 0)
            {
                // LOAD_CONFIG block absent. This can occur in 2 cases:
                // 1. The user has C or C++ code linked with a linker older than Dev11 (VS2010)
                // 2. The code is not C or C++ code at all.
                //
                // In the first case we expect CompilerVersionCheck to fire on this code. In the
                // second case we don't want to warn because the code is likely safe;
                // e.g. .NET ngen'd images fall into this bucket.

                //'{0}' is  C or C++binary that does not contain a load config table, which
                // indicates either that it was compiled and linked with a version of the
                // compiler that precedes stack protection features or is a binary (such as
                // an ngen'ed assembly) that is not subject to relevant security issues.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                             nameof(RuleResources.BA2012_Pass_NoLoadConfig),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (target.PE.Is64Bit)
            {
                if (!Validate64BitImage(context))
                {
                    return;
                }
            }
            else if (!Validate32BitImage(context))
            {
                return;
            }

            // '{0}' is a C or C++ binary built with the buffer security feature
            // that properly preserves the stack protecter cookie. This has the
            // effect of enabling a significant increase in entropy provided by
            // the operating system over that produced by the C runtime start-up
            // code.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2012_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        private bool CookieOffsetValid(BinaryAnalyzerContext context, SafePointer boundsCheck)
        {
            if (boundsCheck.IsValid)
            {
                return(true);
            }

            PEBinary target = context.PEBinary();

            if (target.PE.IsPacked)
            {
                LogInvalidCookieOffsetForKnownPackedFile(context);
            }
            else
            {
                LogInvalidCookieOffset(context);
            }
            return(false);
        }
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            PE       pe     = target.PE;

            if (pe.IsKernelMode &&
                (pe.FileVersion.FileMajorPart < 10 || pe.FileVersion.FileBuildPart < 15000) &&
                pe.FileVersion.FileBuildPart < 15000)
            {
                // '{0}' is a kernel mode portable executable compiled for a
                // version of Windows that does not support the control flow
                // guard feature for kernel mode binaries.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultKind.NotApplicable, context, null,
                                                             nameof(RuleResources.BA2008_NotApplicable_UnsupportedKernelModeVersion),
                                                             context.TargetUri.GetFileName()));
            }

            if (!this.EnablesControlFlowGuard(target))
            {
                // '{0}' does not enable the control flow guard (CFG) mitigation.
                // To resolve this issue, pass /guard:cf on both the compiler
                // and linker command lines. Binaries also require the
                // /DYNAMICBASE linker option in order to enable CFG.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2008_Error),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            // '{0}' enables the control flow guard mitigation.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2008_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        public sealed override void Analyze(BinaryAnalyzerContext context)
        {
            // Uses PDB Parsing.
            BinaryParsers.PlatformSpecificHelpers.ThrowIfNotOnWindows();
            PEBinary target = context.PEBinary();

            string pdbLoadTrace = target.PdbLoadTrace;

            if (!string.IsNullOrEmpty(pdbLoadTrace))
            {
                LogPdbLoadTrace(
                    context,
                    pdbLoadSucceeded: target.Pdb != null,
                    pdbLoadTrace);
            }

            if (target.Pdb == null)
            {
                LogExceptionLoadingPdb(context, target.PdbParseException);
                return;
            }

            this.AnalyzePortableExecutableAndPdb(context);
        }
Beispiel #23
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary target   = context.PEBinary();
            PEHeader peHeader = target.PE.PEHeaders.PEHeader;

            /* IMAGE_DLLCHARACTERISTICS_NO_SEH */
            if ((peHeader.DllCharacteristics & DllCharacteristics.NoSeh) == DllCharacteristics.NoSeh)
            {
                // '{0}' is an x86 binary that does not use SEH, making it an invalid
                // target for exploits that attempt to replace SEH jump targets with
                // attacker-controlled shellcode.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                             nameof(RuleResources.BA2018_Pass_NoSEH),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            // This will not raise false positives for non-C and C++ code, because the above
            // check for IMAGE_DLLCHARACTERISTICS_NO_SEH excludes things that don't actually
            // handle SEH exceptions like .NET ngen'd code.
            if (peHeader.LoadConfigTableDirectory.RelativeVirtualAddress == 0)
            {
                // '{0}' is an x86 binary which does not contain a load configuration table,
                // indicating that it does not enable the SafeSEH mitigation. SafeSEH makes
                // it more difficult to exploit memory corruption vulnerabilities that can
                // overwrite SEH control blocks on the stack, by verifying that the location
                // to which a thrown SEH exception would jump is indeed defined as an
                // exception handler in the source program (and not shellcode). To resolve
                // this issue, supply the /SafeSEH flag on the linker command line. Note
                // that you will need to configure your build system to supply this flag for
                // x86 builds only, as the /SafeSEH flag is invalid when linking for ARM and x64.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2018_Error),
                                                             context.TargetUri.GetFileName(),
                                                             RuleResources.BA2018_Error_NoLoadConfigurationTable));
                return;
            }

            var         sp           = new SafePointer(target.PE.ImageBytes, peHeader.LoadConfigTableDirectory.RelativeVirtualAddress);
            SafePointer loadConfigVA = target.PE.RVA2VA(sp);
            var         loadConfig   = new ImageLoadConfigDirectory32(peHeader, loadConfigVA);

            int seHandlerSize = (int)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.Size);

            if (seHandlerSize < 72)
            {
                // contains an unexpectedly small load configuration table {size 0}
                string seHandlerSizeText = string.Format(RuleResources.BA2018_Error_LoadConfigurationIsTooSmall, seHandlerSize.ToString());

                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2018_Error),
                                                             context.TargetUri.GetFileName(),
                                                             RuleResources.BA2018_Error_LoadConfigurationIsTooSmall,
                                                             seHandlerSizeText));
                return;
            }

            uint seHandlerTable = (uint)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.SEHandlerTable);
            uint seHandlerCount = (uint)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.SEHandlerCount);

            if (seHandlerTable == 0 || seHandlerCount == 0)
            {
                string failureKind = null;
                if (seHandlerTable == 0)
                {
                    // has an empty SE handler table in the load configuration table
                    failureKind = RuleResources.BA2018_Error_EmptySEHandlerTable;
                }
                else if (seHandlerCount == 0)
                {
                    // has zero SE handlers in the load configuration table
                    failureKind = RuleResources.BA2018_Error_NoSEHandlers;
                }

                // '{0}' is an x86 binary which {1}, indicating that it does not enable the SafeSEH
                // mitigation. SafeSEH makes it more difficult to exploit memory corruption
                // vulnerabilities that can overwrite SEH control blocks on the stack, by verifying
                // that the location to which a thrown SEH exception would jump is indeed defined
                // as an exception handler in the source program (and not shellcode). To resolve
                // this issue, supply the /SafeSEH flag on the linker command line. Note that you
                // will need to configure your build system to supply this flag for x86 builds only,
                // as the /SafeSEH flag is invalid when linking for ARM and x64.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2018_Error),
                                                             context.TargetUri.GetFileName(),
                                                             failureKind));
                return;
            }

            // ''{0}' is an x86 binary that enables SafeSEH, a mitigation that verifies SEH exception
            // jump targets are defined as exception handlers in the program (and not shellcode).
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2018_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()));
        }
Beispiel #25
0
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      pdb    = target.Pdb;

            TruncatedCompilandRecordList noGsModules            = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList unknownLanguageModules = new TruncatedCompilandRecordList();

            foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator())
            {
                Symbol om = omView.Value;
                ObjectModuleDetails omDetails = om.GetObjectModuleDetails();

                // Detection applies to C/C++ produced by MS compiler only
                if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftNativeCompiler)
                {
                    continue;
                }

                if (omDetails.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;
                }

                if (!omDetails.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),
                                                             context.TargetUri.GetFileName()));
                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),
                                                             context.TargetUri.GetFileName(),
                                                             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),
                                                             context.TargetUri.GetFileName(),
                                                             noGsModules.ToString()));
            }
        }
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary           target             = context.PEBinary();
            PEHeader           peHeader           = target.PE.PEHeaders.PEHeader;
            DllCharacteristics dllCharacteristics = peHeader.DllCharacteristics;

            CoffHeader      coffHeader      = target.PE.PEHeaders.CoffHeader;
            Characteristics characteristics = coffHeader.Characteristics;

            bool highEntropyVA = ((int)dllCharacteristics & 0x0020 /*IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA*/) == 0x0020;

            //  /LARGEADDRESSAWARE is necessary for HIGH_ENTROPY_VA to have effect
            bool largeAddressAware = (characteristics & Characteristics.LargeAddressAware /*IMAGE_FILE_LARGE_ADDRESS_AWARE*/) == Characteristics.LargeAddressAware;

            if (!highEntropyVA && !largeAddressAware)
            {
                // '{0}' does not declare itself as high entropy ASLR compatible. High entropy allows
                // Address Space Layout Randomization to be more effective in mitigating memory
                // corruption vulnerabilities. To resolve this issue, configure your tool chain to
                // mark the program high entropy compatible; e.g. by supplying /HIGHENTROPYVA as well
                // as /LARGEADDRESSAWARE to the C or C++ linker command line.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2015_Error_NeitherHighEntropyVANorLargeAddressAware),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (!highEntropyVA)
            {
                // '{0}' does not declare itself as high entropy ASLR compatible. High entropy allows
                // Address Space Layout Randomization to be more effective in mitigating memory
                // corruption vulnerabilities. To resolve this issue, configure your tool chain to
                // mark the program high entropy compatible; e.g. by supplying /HIGHENTROPYVA to the
                // C or C++ linker command line. (This image was determined to have been properly
                // compiled as /LARGEADDRESSAWARE.)
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2015_Error_NoHighEntropyVA),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (!largeAddressAware)
            {
                // '{0}' does not declare itself as high entropy ASLR compatible. High entropy allows
                // Address Space Layout Randomization to be more effective in mitigating memory
                // corruption vulnerabilities. To resolve this issue, configure your tool chain to
                // mark the program high entropy compatible by supplying /LARGEADDRESSAWARE to the C
                // or C++ linker command line. (This image was determined to have been properly
                // compiled as /HIGHENTROPYVA.)
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2015_Error_NoLargeAddressAware),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            //'{0}' is high entropy ASLR compatible.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2015_Pass),
                                                         context.TargetUri.GetFileName()));
        }
Beispiel #27
0
        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      di     = target.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 not 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()));
        }
        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)
        {
            PEBinary target         = context.PEBinary();
            PEHeader optionalHeader = target.PE.PEHeaders.PEHeader;

            if ((optionalHeader.DllCharacteristics & DllCharacteristics.DynamicBase) != DllCharacteristics.DynamicBase)
            {
                // '{0}' is not marked as DYNAMICBASE. This means that the binary is not eligible for relocation
                // by Address Space Layout Randomization (ASLR). ASLR is an important mitigation that makes it
                // more difficult for an attacker to exploit memory corruption vulnerabilities. To resolve this
                // issue, configure your tool chain to build with this feature enabled. For C and C++ binaries,
                // add /DYNAMICBASE to your linker command line. For .NET applications, use a compiler shipping
                // with Visual Studio 2008 or later.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2009_Error_NotDynamicBase),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            CoffHeader coffHeader = target.PE.PEHeaders.CoffHeader;

            if ((coffHeader.Characteristics & Characteristics.RelocsStripped) == Characteristics.RelocsStripped)
            {
                // '{0}' is marked as DYNAMICBASE but relocation data has been stripped
                // from the image, preventing address space layout randomization.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2009_Error_RelocsStripped),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (target.PE.Subsystem == Subsystem.WindowsCEGui)
            {
                Debug.Assert(target.PE.OSVersion >= OSVersions.WindowsCE7);

                bool relocSectionFound = false;


                // For WinCE 7+ ASLR is a machine-wide setting and binaries must
                // have relocation info present in order to be dynamically rebased.
                foreach (SectionHeader sectionHeader in target.PE.PEHeaders.SectionHeaders)
                {
                    if (sectionHeader.Name.Equals(".reloc", StringComparison.OrdinalIgnoreCase) &&
                        sectionHeader.SizeOfRawData > 0)
                    {
                        relocSectionFound = true;
                        break;
                    }
                }

                if (!relocSectionFound)
                {
                    // EnableAddressSpaceLayoutRandomization_WinCENoRelocationSection_Error	'{0}'
                    // is a Windows CE image but does not contain any relocation data, preventing
                    // address space layout randomization.
                    context.Logger.Log(this,
                                       RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                                 nameof(RuleResources.BA2009_Error_WinCENoRelocationSection),
                                                                 context.TargetUri.GetFileName()));
                    return;
                }
            }

            //'{0}' is properly compiled to enable address space layout randomization.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2009_Pass),
                                                         context.TargetUri.GetFileName()));
        }