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