public void AnalyzeNativeBinaryAndPdb()

in src/BinSkim.Rules/PERules/BA2004.EnableSecureSourceCodeHashing.cs [87:213]


        public void AnalyzeNativeBinaryAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb di = target.Pdb;

            var compilandsBinaryWithOneOrMoreInsecureFileHashes = new List<ObjectModuleDetails>();
            var compilandsLibraryWithOneOrMoreInsecureFileHashes = new List<ObjectModuleDetails>();

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

                if (omDetails.Language != Language.C &&
                    omDetails.Language != Language.Cxx &&
                    omDetails.Language != Language.MASM)
                {
                    continue;
                }

                if (!omDetails.HasDebugInfo)
                {
                    continue;
                }

                bool isMSVC = (omDetails.WellKnownCompiler == WellKnownCompilers.MicrosoftC ||
                               omDetails.WellKnownCompiler == WellKnownCompilers.MicrosoftCxx);

                string pchHeaderFile = string.Empty;
                string pchFileName = string.Empty;

                if (isMSVC)
                {
                    // Check to see if the object was compiled using /Yc or /Yu for precompiled headers
                    string[] pchOptionSwitches = { "/Yc", "/Yu" };
                    if (omDetails.GetOptionValue(pchOptionSwitches, OrderOfPrecedence.FirstWins, ref pchHeaderFile) == true)
                    {
                        // Now check to see if a pch file name was specified using /Fp:
                        string[] pchFileNameOptions = { "/Fp:" };
                        if (omDetails.GetOptionValue(pchFileNameOptions, OrderOfPrecedence.FirstWins, ref pchFileName) != true)
                        {
                            // no pch filename specified, so the filename defaults to the pchHeaderFile with the extension swapped to ".pch"
                            pchFileName = Path.ChangeExtension(pchHeaderFile, "pch");
                        }
                    }
                }

                CompilandRecord record = om.CreateCompilandRecord();

                foreach (DisposableEnumerableView<SourceFile> sfView in di.CreateSourceFileIterator(om))
                {
                    SourceFile sf = sfView.Value;

                    if (sf.HashType == HashType.None)
                    {
                        if (isMSVC)
                        {
                            // We know of 3 scenarios where this occurs today:
                            // If we encounter one of these, we should continue the loop to the next SourceFile,
                            // else fallthrough to the other checks for normal processing and errors.

                            string sfName = Path.GetFileName(sf.FileName);

                            // 1. Some compiler injected code that is listed as being in "predefined C++ types (compiler internal)"
                            if (sfName == MSVCPredefinedTypesFileName)
                            {
                                continue;
                            }
                            else if (pchFileName != string.Empty)
                            {
                                // 2. The file used to create a precompiled header using the /Yc switch
                                // TODO - We need a prepass on the library / final link to determine which file was
                                //        used to create the pch, as this is the file that will have a HashType.None
                                // 3. The pch file itself
                                if (sfName == Path.GetFileName(pchFileName))
                                {
                                    continue;
                                }
                                // TODO - check this against the filename used to create the pch.  For now just let it pass
                                else // if(sfName == pchCreationTUFileName)
                                {
                                    continue;
                                }
                            }
                        }
                    }

                    if (sf.HashType != HashType.SHA256)
                    {
                        if (!string.IsNullOrEmpty(record.Library))
                        {
                            compilandsLibraryWithOneOrMoreInsecureFileHashes.Add(omDetails);
                        }
                        else
                        {
                            compilandsBinaryWithOneOrMoreInsecureFileHashes.Add(omDetails);
                        }
                    }
                    // We only need to check a single source file per compiland, as the relevant
                    // command-line options will be applied to all files in the translation unit.
                    break;
                }
            }

            if (compilandsLibraryWithOneOrMoreInsecureFileHashes.Count > 0 || compilandsBinaryWithOneOrMoreInsecureFileHashes.Count > 0)
            {
                if (compilandsLibraryWithOneOrMoreInsecureFileHashes.Count > 0)
                {
                    GenerateCompilandsAndLog(context, compilandsLibraryWithOneOrMoreInsecureFileHashes, FailureLevel.Warning);
                }

                if (compilandsBinaryWithOneOrMoreInsecureFileHashes.Count > 0)
                {
                    GenerateCompilandsAndLog(context, compilandsBinaryWithOneOrMoreInsecureFileHashes, FailureLevel.Error);
                }

                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(),
                        "native"));
        }