public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            UInt64 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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.LoadImageAboveFourGigabyteAddress_Fail));
                return;
            }

            // '{0}' is marked as NX compatible.
            context.Logger.Log(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.LoadImageAboveFourGigabyteAddress_Pass));
        }
Ejemplo n.º 2
0
 public void Analyze(BinaryAnalyzerContext context)
 {
     if (_exceptionCondition == ExceptionCondition.InvokingAnalyze)
     {
         throw new InvalidOperationException(nameof(ExceptionCondition.InvokingAnalyze));
     }
 }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            DllCharacteristics dllCharacteristics = peHeader.DllCharacteristics;

            CoffHeader coffHeader = context.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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableHighEntropyVirtualAddresses_NeitherHighEntropyVANorLargeAddressAware_FAIL));
                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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableHighEntropyVirtualAddresses_NoHighEntropyVA_FAIL));
                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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableHighEntropyVirtualAddresses_NoLargeAddressAware_FAIL));
                return;
            }

            //'{0}' is high entropy ASLR compatible.
            context.Logger.Log(MessageKind.Pass, context,
                 RuleUtilities.BuildMessage(context,
                    RulesResources.EnableHighEntropyVirtualAddresses_Pass));
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            Pdb pdb = context.Pdb;

            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.DoNotDisableStackProtectionForFunctions_Pass, 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(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotDisableStackProtectionForFunctions_Pass));
        }
Ejemplo n.º 5
0
 public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonIfNotApplicable)
 {
     reasonIfNotApplicable = null;
     if (_exceptionCondition == ExceptionCondition.InvokingCanAnalyze)
     {
         throw new InvalidOperationException(nameof(ExceptionCondition.InvokingCanAnalyze));
     }
     return AnalysisApplicability.ApplicableToSpecifiedTarget;
 }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            // TODO: do we really require this check? What is the proposed fix to this issue?
            if (peHeader.SectionAlignment < PAGE_SIZE)
            {
                // '{0}' has a section alignment ({1}) that is less than page size ({2}).
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.DoNotMarkWritableSectionsAsExecutable_Fail,
                        context.PE.FileName,
                        "0x" + peHeader.SectionAlignment.ToString("x"),
                        "0x" + PAGE_SIZE.ToString("x")));
                return;
            }

            var sectionHeaders = context.PE.PEHeaders.SectionHeaders;

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

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

                    if ((sectionHeader.SectionCharacteristics & wxFlags) == wxFlags)
                    {
                        badSections.Add(sectionHeader.Name);
                    }
                }
            }

            if (badSections.Count == 0)
            {
                // '{0}' contains no data or code sections marked as both shared and executable.
                context.Logger.Log(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.DoNotMarkWritableSectionsAsExecutable_Pass));
                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 give an attacker
            // executable location(s) to inject shellcode. To resolve this
            // issue, configure your toolchain 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.
            context.Logger.Log(MessageKind.Fail, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotMarkWritableSectionsAsExecutable_Fail, badSectionsText));
        }
        public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            PE portableExecutable = context.PE;
            AnalysisApplicability result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

            reasonForNotAnalyzing = MetadataConditions.ImageIsXBoxBinary;
            if (portableExecutable.IsXBox) { return result; }

            return AnalysisApplicability.ApplicableToSpecifiedTarget;
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            string executableImportSection = null;
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            DirectoryEntry importTable = peHeader.ImportTableDirectory;

            if (importTable.RelativeVirtualAddress != 0 && context.PE.PEHeaders.SectionHeaders != null)
            {
                int importSize = peHeader.ImportTableDirectory.Size;
                foreach (SectionHeader sectionHeader in context.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(MessageKind.Fail, context,
                        RuleUtilities.BuildMessage(context,
                            RulesResources.DoNotMarkImportsSectionAsExecutable_Fail));
                return;
            }

            // '{0}' does not have an imports section that is marked as executable.
            context.Logger.Log(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotMarkImportsSectionAsExecutable_Pass));
        }
        internal static AnalysisApplicability CommonCanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            PE portableExecutable = context.PE;
            AnalysisApplicability result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

            reasonForNotAnalyzing = MetadataConditions.ImageIsResourceOnlyBinary;
            if (portableExecutable.IsResourceOnly) { return result; }

            reasonForNotAnalyzing = MetadataConditions.ImageIsILOnlyManagedAssembly;
            if (portableExecutable.IsILOnly) { return result; }

            reasonForNotAnalyzing = MetadataConditions.ImageIsXBoxBinary;
            if (portableExecutable.IsXBox) { return result; }

            return AnalysisApplicability.ApplicableToSpecifiedTarget;
        }
        public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            PE portableExecutable = context.PE;
            AnalysisApplicability result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

            reasonForNotAnalyzing = MetadataConditions.ImageIsNot64BitBinary;
            if (context.PE.PEHeaders.PEHeader.Magic != PEMagic.PE32Plus) { return result; }

            reasonForNotAnalyzing = MetadataConditions.ImageIsKernelModeBinary;
            if (portableExecutable.IsKernelMode) { return result; }

            reasonForNotAnalyzing = MetadataConditions.ImageIsResourceOnlyBinary;
            if (portableExecutable.IsResourceOnly) { return result; }

            return AnalysisApplicability.ApplicableToSpecifiedTarget;
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.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(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.DoNotModifyStackProtectionCookie_NoLoadConfig_Pass));
                return;
            }

            if (context.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(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotModifyStackProtectionCookie_Pass));
        }
Ejemplo n.º 12
0
        public static string BuildMessage(BinaryAnalyzerContext context, string messageFormatString, params string[] arguments)
        {
            // By convention, the first argument is always the target name,
            // which we retrieve from the context
            Debug.Assert(File.Exists(context.PE.FileName));
            string targetName = Path.GetFileName(context.PE.FileName);

            string[] fullArguments = new string[arguments != null ? arguments.Length + 1 : 1];
            fullArguments[0] = targetName;

            if (fullArguments.Length > 1)
            {
                arguments.CopyTo(fullArguments, 1);
            }

            return String.Format(CultureInfo.InvariantCulture,
                messageFormatString, fullArguments);
        }
Ejemplo n.º 13
0
        public static string BuildMessage(BinaryAnalyzerContext context, string messageFormatString, params string[] arguments)
        {
            // By convention, the first argument is always the target name,
            // which we retrieve from the context
            Debug.Assert(File.Exists(context.PE.FileName));
            string targetName = Path.GetFileName(context.PE.FileName);

            string[] fullArguments = new string[arguments != null ? arguments.Length + 1 : 1];
            fullArguments[0] = targetName;

            if (fullArguments.Length > 1)
            {
                arguments.CopyTo(fullArguments, 1);
            }

            return(String.Format(CultureInfo.InvariantCulture,
                                 messageFormatString, fullArguments));
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            var sectionHeaders = context.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(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.DoNotMarkWritableSectionsAsShared_Pass));
                return;
            }

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

            // {0} contains one or more code or data sections ({1}) which are marked as both
            // shared and writable. Because these sections are shared across processes, this
            // condition might permit a process with low privilege to mutate memory in a higher
            // privilege process. If you do not actually require that the section be both
            // writable and shared, remove one or both of these attributes (by modifying your
            // .DEF file, the appropriate linker /section switch arguments, etc.). If you are
            // required to share common data across processes (for IPC or other purposes) use
            // CreateFileMapping with proper security attributes or an actual IPC mechanism
            // instead (COM, named pipes, LPC, etc.).
            context.Logger.Log(MessageKind.Fail, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotMarkWritableSectionsAsShared_Fail, badSectionsText));
        }
Ejemplo n.º 15
0
        public static string BuildCouldNotLoadPdbMessage(BinaryAnalyzerContext context)
        {
            Debug.Assert(context.Pdb == null);
            Debug.Assert(context.PdbParseException != null);

            string ruleName         = context.Rule.Name;
            string targetPath       = Path.GetFileName(context.PE.FileName);
            string exceptionMessage = context.PdbParseException.Message;

            // Image '{0}' was not evaluated for check '{1}' as
            // an exception occurred loading its pdb: '{3}'
            return(String.Format(
                       CultureInfo.InvariantCulture,
                       SdkResources.TargetNotAnalyzed_MissingPdb,
                       targetPath,
                       ruleName,
                       SdkResources.MetadataCondition_CouldNotLoadPdb,
                       exceptionMessage));
        }
Ejemplo n.º 16
0
        public static string BuildCouldNotLoadPdbMessage(BinaryAnalyzerContext context)
        {
            Debug.Assert(context.Pdb == null);
            Debug.Assert(context.PdbParseException != null);

            string ruleName = context.Rule.Name;
            string targetPath = Path.GetFileName(context.PE.FileName);
            string exceptionMessage = context.PdbParseException.Message;

            // Image '{0}' was not evaluated for check '{1}' as
            // an exception occurred loading its pdb: '{3}'
            return String.Format(
                CultureInfo.InvariantCulture,
                SdkResources.TargetNotAnalyzed_MissingPdb,
                targetPath,
                ruleName,
                SdkResources.MetadataCondition_CouldNotLoadPdb,
                exceptionMessage);
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            string fileName = Path.GetFileName(context.PE.FileName);

            Version minimumVersion;
            if (context.Policy.GetProperty(VulnerableBinaries).TryGetValue(fileName, out minimumVersion))
            {
                FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(Path.GetFullPath(context.PE.FileName));
                string rawVersion = fvi.FileVersion;
                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(MessageKind.Fail, context,
                        RuleUtilities.BuildMessage(context,
                            RulesResources.DoNotShipVulnerableBinaries_CouldNotParseVersion_Fail));
                    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 newer than version {2}. If this binary is not in fact {0},
                    // ignore this warning.

                    context.Logger.Log(MessageKind.Fail, context,
                        RuleUtilities.BuildMessage(context,
                            RulesResources.DoNotShipVulnerableBinaries_CouldNotParseVersion_Fail,
                            minimumVersion.ToString()));
                    return;
                }
            }

            // '{0}' is not known to be an obsolete binary that is
            //vulnerable to one or more security problems.
            context.Logger.Log(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotShipVulnerableBinaries_Pass));
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.MarkImageAsNXCompatible_Fail));
                return;
            }

            // '{0}' is marked as NX compatible.
            context.Logger.Log(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.MarkImageAsNXCompatible_Pass));
        }
Ejemplo n.º 19
0
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            Pdb pdb = context.Pdb;
            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            Version minLinkVersion;
            Version minCompilerVersion;

            if (context.PE.IsXBox)
            {
                minCompilerVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_COMPILER_VER];
                minLinkVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_LINKER_VER];
            }
            else
            {
                minCompilerVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_COMPILER_VER];
                minLinkVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_LINKER_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();

                // 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;

                    case Language.MASM:
                        // TODO verify this
                        actualVersion = omDetails.CompilerVersion;
                        minimumVersion = minLinkVersion;
                        break;

                    case Language.LINK:
                        continue;

                    default:
                        continue;
                }

                if (actualVersion < minimumVersion)
                {
                    // built with {0} compiler version {1} (Front end version: {2})
                    badModuleList.Add(
                        om.CreateCompilandRecordWithSuffix(
                            String.Format(CultureInfo.InvariantCulture,
                            RulesResources.BuildWithSecureTools_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}, linker version {2}). More recent
                // tool chains 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: {3}
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.BuildWithSecureTools_Fail,
                        minCompilerVersion.ToString(),
                        minLinkVersion.ToString(),
                        badModuleList.CreateSortedObjectList()));
                return;
            }

            // '{0}' was built with a tool chain that satisfies configured policy
            // (compiler minimum version {1}, linker minimum version {2}).
            context.Logger.Log(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.BuildWithSecureTools_Pass,
                    minCompilerVersion.ToString(), minLinkVersion.ToString()));
        }
        public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            AnalysisApplicability applicability = StackProtectionUtilities.CommonCanAnalyze(context, out reasonForNotAnalyzing);

            // Checks for missing policy should always be evaluated as the last action, so that
            // we do not raise an error in cases where the analysis would not otherise be applied.
            if (applicability == AnalysisApplicability.ApplicableToSpecifiedTarget)
            {
                reasonForNotAnalyzing = RulesResources.DoNotShipVulnerabilities_MissingPolicy_InternalError;
                if (context.Policy == null) { return AnalysisApplicability.NotApplicableToAnyTargetWithoutPolicy; }
            }
            return applicability;
        }
 public void Initialize(BinaryAnalyzerContext context)
 {
     return;
 }
Ejemplo n.º 22
0
        private void Verify(
            IBinarySkimmer skimmer,
            IEnumerable<string> additionalTestFiles,
            bool useDefaultPolicy,
            bool expectToPass)
        {
            var targets = new List<string>();
            string ruleName = skimmer.GetType().Name;
            string testFilesDirectory = ruleName;
            testFilesDirectory = Path.Combine(Environment.CurrentDirectory, "FunctionalTestsData", testFilesDirectory);
            testFilesDirectory = Path.Combine(testFilesDirectory, expectToPass ? "Pass" : "Fail");

            Assert.True(Directory.Exists(testFilesDirectory));

            foreach (string target in Directory.GetFiles(testFilesDirectory, "*", SearchOption.AllDirectories))
            {
                if (AnalyzeCommand.ValidAnalysisFileExtensions.Contains(Path.GetExtension(target)))
                {
                    targets.Add(target);
                }
            }

            if (additionalTestFiles != null)
            {
                foreach (string additionalTestFile in additionalTestFiles)
                {
                    targets.Add(additionalTestFile);
                }
            }

            var context = new BinaryAnalyzerContext();
            var logger = new TestMessageLogger();
            context.Logger = logger;
            PropertyBag policy = null;

            if (useDefaultPolicy)
            {
                policy = new PropertyBag();
            }
            context.Policy = policy;

            skimmer.Initialize(context);

            foreach (string target in targets)
            {
                PE pe = new PE(target);
                if (!pe.IsPEFile) { continue; }

                context = AnalyzeCommand.CreateContext(logger, policy, target);

                context.Rule = skimmer;

                string reasonForNotAnalyzing;
                if (skimmer.CanAnalyze(context, out reasonForNotAnalyzing) != AnalysisApplicability.ApplicableToSpecifiedTarget)
                {
                    continue;
                }

                skimmer.Analyze(context);
            }

            HashSet<string> expected = expectToPass ? logger.PassTargets : logger.FailTargets;
            HashSet<string> other = expectToPass ? logger.FailTargets : logger.PassTargets;
            string expectedText = expectToPass ? "success" : "failure";
            string actualText = expectToPass ? "failed" : "succeeded";
            var sb = new StringBuilder();

            foreach (string target in targets)
            {
                if (expected.Contains(target))
                {
                    expected.Remove(target);
                    continue;
                }
                bool missingEntirely = !other.Contains(target);

                if (missingEntirely)
                {
                    sb.AppendLine("Expected '" + ruleName + "' " + expectedText + " but saw no result at all for file: " + Path.GetFileName(target));
                }
                else
                {
                    other.Remove(target);
                    sb.AppendLine("Expected '" + ruleName + "' " + expectedText + " but check " + actualText + " for: " + Path.GetFileName(target));
                }
            }

            if (sb.Length > 0)
            {
                _testOutputHelper.WriteLine(sb.ToString());
            }

            Assert.Equal(0, sb.Length);
            Assert.Equal(0, expected.Count);
            Assert.Equal(0, other.Count);
        }
Ejemplo n.º 23
0
        public void Log(MessageKind messageKind, BinaryAnalyzerContext context, string message)
        {
            switch (messageKind)
            {
                case MessageKind.AnalyzingTarget:
                    {
                        break;
                    }

                case MessageKind.Pass:
                    {
                        if (Verbose)
                        {
                            WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.NoError);
                        }
                        break;
                    }

                case MessageKind.Fail:
                    {
                        WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.Error);
                        break;
                    }

                case MessageKind.NotApplicable:
                    {
                        if (Verbose)
                        {
                            WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.NotApplicable);
                        }
                        break;
                    }

                case MessageKind.Note:
                    {
                        if (Verbose)
                        {
                            WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.Note);
                        }
                        break;
                    }

                case MessageKind.Pending:
                    {
                        WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.Pending);
                        break;
                    }

                case MessageKind.InternalError:
                    {
                        WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.InternalError);
                        break;
                    }

                case MessageKind.ConfigurationError:
                    {
                        WriteJsonIssue(context.PE.FileName, context.Rule.Id, message, IssueKind.ConfigurationError);
                        break;
                    }

                default:
                    {
                        throw new InvalidOperationException();
                    }
            }
        }
Ejemplo n.º 24
0
        private void VerifyNotApplicable(
            IBinarySkimmer skimmer,
            HashSet<string> notApplicableConditions,
            AnalysisApplicability expectedApplicability = AnalysisApplicability.NotApplicableToSpecifiedTarget)
        {
            string ruleName = skimmer.GetType().Name;
            string testFilesDirectory = ruleName;
            testFilesDirectory = Path.Combine(Environment.CurrentDirectory, "FunctionalTestsData", testFilesDirectory);
            testFilesDirectory = Path.Combine(testFilesDirectory, "NotApplicable");

            var context = new BinaryAnalyzerContext();

            HashSet<string> targets = GetTestFilesMatchingConditions(notApplicableConditions);

            if (Directory.Exists(testFilesDirectory))
            {
                foreach (string target in Directory.GetFiles(testFilesDirectory, "*", SearchOption.AllDirectories))
                {
                    if (AnalyzeCommand.ValidAnalysisFileExtensions.Contains(Path.GetExtension(target)))
                    {
                        targets.Add(target);
                    }
                }
            }

            var logger = new TestMessageLogger();
            context.Logger = logger;

            var sb = new StringBuilder();

            foreach (string target in targets)
            {
                string extension = Path.GetExtension(target);
                if (!AnalyzeCommand.ValidAnalysisFileExtensions.Contains(extension))
                {
                    Assert.True(false, "Test file with unexpected extension encountered: " + target);
                }

                context = AnalyzeCommand.CreateContext(logger, null, target);
                if (!context.PE.IsPEFile) { continue; }

                context.Rule = skimmer;

                string reasonForNotAnalyzing;
                AnalysisApplicability applicability;
                applicability = skimmer.CanAnalyze(context, out reasonForNotAnalyzing);
                if (applicability != expectedApplicability)
                {
                    sb.AppendLine("CanAnalyze did not indicate target was invalid for analysis (return was " +
                        applicability + "): " +
                        Path.GetFileName(target));
                    continue;
                }
            }

            if (sb.Length > 0)
            {
                _testOutputHelper.WriteLine(sb.ToString());
            }

            Assert.Equal(0, sb.Length);
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            Pdb di = context.Pdb;

            if (di == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            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 disclosre, double-free and
                // other security-related vulnerabilities do not exist in code.
                context.Logger.Log(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableCriticalCompilerWarnings_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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableCriticalCompilerWarnings_UnknownModuleLanguage_Fail,
                        unknownLanguageModules.ToString()));
            }

            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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableCriticalCompilerWarnings_InsufficientWarningLevel_Fail,
                        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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableCriticalCompilerWarnings_WarningsDisabled_Fail,
                        exampleDisabledWarningCommandLine,
                        disabledWarningModules.CreateTruncatedObjectList()));
            }
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            Pdb pdb = context.Pdb;

            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                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(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableStackProtection_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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableStackProtection_UnknownModuleLanguage_Fail,
                        unknownLanguageModules.ToString()));
            }

            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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableStackProtection_Fail,
                        noGsModules.ToString()));
            }
        }
        public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            PE portableExecutable = context.PE;
            AnalysisApplicability result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

            reasonForNotAnalyzing = MetadataConditions.ImageIsKernelModeBinary;
            if (portableExecutable.IsKernelMode) { return result; }

            reasonForNotAnalyzing = MetadataConditions.ImageIsNot64BitBinary;
            if (portableExecutable.PEHeaders.PEHeader.Magic != PEMagic.PE32Plus) { return result; }

            // TODO need to put a check here for verifying that the
            // compiler that built the target supports high entropy va

            return AnalysisApplicability.ApplicableToSpecifiedTarget;
        }
 public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
 {
     return StackProtectionUtilities.CommonCanAnalyze(context, out reasonForNotAnalyzing);
 }
Ejemplo n.º 29
0
        public AnalysisApplicability CanAnalyze(BinaryAnalyzerContext context, out string reasonForNotAnalyzing)
        {
            PE portableExecutable = context.PE;
            AnalysisApplicability result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

            reasonForNotAnalyzing = MetadataConditions.ImageIsILOnlyManagedAssembly;
            if (portableExecutable.IsILOnly) { return result; }

            reasonForNotAnalyzing = MetadataConditions.ImageIsResourceOnlyBinary;
            if (portableExecutable.IsResourceOnly) { return result; }

            // Checks for missing policy should always be evaluated as the last action, so that
            // we do not raise an error in cases where the analysis would not otherise be applied.
            reasonForNotAnalyzing = RulesResources.DoNotShipVulnerabilities_MissingPolicy_InternalError;
            if (context.Policy == null) { return AnalysisApplicability.NotApplicableToAnyTargetWithoutPolicy; }

            return AnalysisApplicability.ApplicableToSpecifiedTarget;
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            if (context.Pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                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(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.InitializeStackProtection_NoCode_Pass));
                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 make use of the stack protection
                // buffer security feature. It is therefore not required to initialize the feature.
                context.Logger.Log(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.InitializeStackProtection_NoFeatureUse_Pass));
                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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.InitializeStackProtection_Fail));
                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(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                   RulesResources.InitializeStackProtection_Pass));
        }
Ejemplo n.º 31
0
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.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(MessageKind.Pass, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableSafeSEH_NoSEH_Pass));
                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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableSafeSEH_NoLoadConfigurationTable_Fail));
                return;
            }

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

            Int32 seHandlerSize = (Int32)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.Size);
            if (seHandlerSize < 72)
            {
                // contains an unexpectedly small load configuration table {size 0}
                string seHandlerSizeText = String.Format(RulesResources.EnableSafeSEH_LoadConfigurationIsTooSmall_Fail, seHandlerSize.ToString());

                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableSafeSEH_Formatted_Fail,
                        RulesResources.EnableSafeSEH_LoadConfigurationIsTooSmall_Fail,
                        seHandlerSizeText));
                return;
            }

            UInt32 seHandlerTable = (UInt32)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.SEHandlerTable);
            UInt32 seHandlerCount = (UInt32)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 = RulesResources.EnableSafeSEH_EmptySEHandlerTable_Fail;
                }
                else if (seHandlerCount == 0)
                {
                    // has zero SE handlers in the load configuration table
                    failureKind = RulesResources.EnableSafeSEH_ZeroCountSEHandlers_Fail;
                }

                // '{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(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                        RulesResources.EnableSafeSEH_Formatted_Fail, 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(MessageKind.Pass, context,
                RuleUtilities.BuildMessage(context,
                    RulesResources.EnableSafeSEH_SafeSEHEnabled_Pass));
        }
        private bool Validate32BitImage(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            SafePointer sp = new SafePointer(context.PE.ImageBytes, peHeader.LoadConfigTableDirectory.RelativeVirtualAddress);
            SafePointer loadConfigVA = context.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 context.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)
            {
                // '{0}' is a C or C++binary that enables the stack protection feature but the security cookie could not be located. The binary may be corrupted.
                context.Logger.Log(MessageKind.Fail, context,
                        RuleUtilities.BuildMessage(context,
                            RulesResources.DoNotModifyStackProtectionCookie_CouldNotLocateCookie_Fail));
                return false;
            }

            UInt64 fileCookieOffset = (cookieVA - baseAddress) - (sectionVA - baseAddress) + (UInt32)ish.PointerToRawData;
            SafePointer fileCookiePtr = loadConfigVA;
            fileCookiePtr.Address = (int)fileCookieOffset;
            UInt32 cookie = BitConverter.ToUInt32(fileCookiePtr.GetBytes(8), 0);

            if (!StackProtectionUtilities.DefaultCookiesX86.Contains(cookie) && context.PE.Machine == Machine.I386)
            {
                // '{0}' is a C or C++ binary that interferes with 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 relies on a random number, called
                // the "security cookie", to detect these buffer overflows. This 'cookie'
                // is statically linked with your binary from a Visual C++ library in the
                // form of the symbol __security_cookie. On recent Windows versions, the
                // loader looks for the magic statically linked value of this cookie, and
                // initializes the cookie with a far better source of entropy -- the system's
                // secure random number generator -- rather than the limited random number
                // generator available early in the C runtime startup code. When this symbol
                // is not the default value, the additional entropy is not injected by the
                // operating system, reducing the effectiveness of the stack protector. To
                // resolve this issue, ensure that your code does not reference or create a
                // symbol named __security_cookie or __security_cookie_complement. NOTE:
                // the modified cookie value detected was: {1}
                context.Logger.Log(MessageKind.Fail, context,
                    RuleUtilities.BuildMessage(context,
                    RulesResources.DoNotModifyStackProtectionCookie_Fail, cookie.ToString("x")));
                return false;
            }

            return true;
        }