Skip to content

Commit

Permalink
Enable analysis for managed binary (#335)
Browse files Browse the repository at this point in the history
* Enable analysis for managed binary

* updating sarif expected results

* updating logic to pdblocation

* removing old comment

* updating pdb loader for checksum and tests

* updating logic to check if we can process and tests

* adding more tests

* addressing pr comments

* Moving from NotApplicable to FailureLevel = Error
  • Loading branch information
eddynaka authored Jan 25, 2021
1 parent 9a0d252 commit a7816c7
Show file tree
Hide file tree
Showing 108 changed files with 1,701 additions and 1,399 deletions.
42 changes: 24 additions & 18 deletions src/BinSkim.Rules/PERules/BA2004.EnableSecureSourceCodeHashing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,13 @@ public class EnableSecureSourceCodeHashing : WindowsBinaryAndPdbSkimmerBase, IOp
protected override IEnumerable<string> MessageResourceNames => new string[] {
nameof(RuleResources.BA2004_Pass),
nameof(RuleResources.BA2004_Warning_NativeWithInsecureStaticLibraryCompilands),
nameof(RuleResources.BA2004_Warning_Managed),
nameof(RuleResources.BA2004_Error_Managed),
nameof(RuleResources.BA2004_Error_NativeWithInsecureDirectCompilands),
nameof(RuleResources.NotApplicable_InvalidMetadata)
};

public override AnalysisApplicability CanAnalyzePE(PEBinary target, Sarif.PropertiesDictionary policy, out string reasonForNotAnalyzing)
{
PE portableExecutable = target.PE;
AnalysisApplicability result = AnalysisApplicability.NotApplicableToSpecifiedTarget;

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

// TODO: currently our test binary for this check is a dll that does not
// compile against any external library. BinSkim regards this as a resource
// only binary and skips the test. We should improve the IsResourceOnly
// helper to properly identify this dependency-free test binary as code.

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

reasonForNotAnalyzing = null;
return AnalysisApplicability.ApplicableToSpecifiedTarget;
}
Expand All @@ -64,11 +50,31 @@ public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext conte
AnalyzeNativeBinaryAndPdb(context);
}

#pragma warning disable IDE0060 // Remove unused parameter
private void AnalyzeManagedAssemblyAndPdb(BinaryAnalyzerContext context)
#pragma warning restore IDE0060 // Remove unused parameter
{
// TODO: use DiaSymReader?
PEBinary target = context.PEBinary();
Pdb di = target.Pdb;

if (target.PE.ManagedPdbSourceFileChecksumAlgorithm(di.FileType) != ChecksumAlgorithmType.Sha256)
{
// '{0}' is a managed binary compiled with an insecure (SHA-1) source code hashing algorithm.
// SHA-1 is subject to collision attacks and its use can compromise supply chain integrity.
// Pass '-checksumalgorithm:SHA256' on the csc.exe command-line or populate the project
// <ChecksumAlgorithm> property with 'SHA256' to enable secure source code hashing.
context.Logger.Log(this,
RuleUtilities.BuildResult(ResultKind.Fail, context, null,
nameof(RuleResources.BA2004_Error_Managed),
context.TargetUri.GetFileName()));
return;
}

// '{0}' is a {1} binary which was compiled with a secure (SHA-256)
// source code hashing algorithm.
context.Logger.Log(this,
RuleUtilities.BuildResult(ResultKind.Pass, context, null,
nameof(RuleResources.BA2004_Pass),
context.TargetUri.GetFileName(),
"managed"));
}

public void AnalyzeNativeBinaryAndPdb(BinaryAnalyzerContext context)
Expand Down
18 changes: 9 additions & 9 deletions src/BinSkim.Rules/RuleResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/BinSkim.Rules/RuleResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ Modules triggering this check were:
<value>The PDB for '{0}' was found and loaded. Probing details:
{1}</value>
</data>
<data name="BA2004_Warning_Managed" xml:space="preserve">
<data name="BA2004_Error_Managed" xml:space="preserve">
<value>'{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 &lt;ChecksumAlgorithm&gt; property with 'SHA256' to enable secure source code hashing.</value>
</data>
<data name="BA2004_Pass" xml:space="preserve">
Expand Down
1 change: 1 addition & 0 deletions src/BinaryParsers/BinaryParsers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<PropertyGroup>
<RootNamespace>Microsoft.CodeAnalysis.BinaryParsers</RootNamespace>
<TargetFramework>$(NetStandardVersion)</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down
26 changes: 26 additions & 0 deletions src/BinaryParsers/ChecksumAlgorithmType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.CodeAnalysis.BinaryParsers
{
/// <summary>
/// Checksum Algorithm for managed binary.
/// </summary>
public enum ChecksumAlgorithmType
{
/// <summary>
/// Unknown format.
/// </summary>
Unknown,

/// <summary>
/// SHA1 algorithm.
/// </summary>
Sha1,

/// <summary>
/// SHA256 algorithm.
/// </summary>
Sha256,
}
}
5 changes: 0 additions & 5 deletions src/BinaryParsers/PEBinary/PEBinary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ public PEBinary(

private Pdb LoadPdb()
{
// We should never be required to load a PDB for a managed assembly that does
// not incorporate native code, as no managed-relevant rule currently crawls
// PDBs for its analysis.
Debug.Assert(!this.PE.IsManaged || this.PE.IsMixedMode);

Pdb pdb = null;
try
{
Expand Down
73 changes: 70 additions & 3 deletions src/BinaryParsers/PEBinary/PortableExecutable/PE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
using System.Reflection.PortableExecutable;
using System.Security.Cryptography;

using Microsoft.DiaSymReader;
using Microsoft.DiaSymReader.Tools;

namespace Microsoft.CodeAnalysis.BinaryParsers.PortableExecutable
{
public class PE : IDisposable
Expand All @@ -29,6 +32,8 @@ public class PE : IDisposable
private PEReader peReader;
internal SafePointer pImage; // pointer to the beginning of the file in memory
private readonly MetadataReader metadataReader;
private const string sha256 = "8829d00f-11b8-4213-878b-770e8597ac16";
private static readonly Guid sha256guid = new Guid(sha256);

public PE(string fileName)
{
Expand Down Expand Up @@ -262,7 +267,6 @@ public byte[] ImageBytes
}
}


/// <summary>
/// Calculate SHA1 over the file contents
/// </summary>
Expand Down Expand Up @@ -365,7 +369,6 @@ public long Length
}
}


/// <summary>
/// Windows OS file version information
/// </summary>
Expand All @@ -381,7 +384,6 @@ public FileVersionInfo FileVersion
}
}


public Packer Packer
{
get
Expand Down Expand Up @@ -789,5 +791,70 @@ public bool IsWixBinary
return this.isWixBinary.Value;
}
}

public ChecksumAlgorithmType ManagedPdbSourceFileChecksumAlgorithm(PdbFileType pdbFileType)
{
return pdbFileType == PdbFileType.Windows
? ChecksumAlgorithmForFullPdb()
: ChecksumAlgorithmForPortablePdb();
}

private ChecksumAlgorithmType ChecksumAlgorithmForPortablePdb()
{
if (!this.peReader.TryOpenAssociatedPortablePdb(
this.FileName,
filePath => File.Exists(filePath) ? File.OpenRead(filePath) : null,
out MetadataReaderProvider pdbProvider,
out _))
{
return ChecksumAlgorithmType.Unknown;
}

MetadataReader metadataReader = pdbProvider.GetMetadataReader();
foreach (DocumentHandle document in metadataReader.Documents)
{
Document doc = metadataReader.GetDocument(document);

Guid hashGuid = metadataReader.GetGuid(doc.HashAlgorithm);

return hashGuid == sha256guid ? ChecksumAlgorithmType.Sha256 : ChecksumAlgorithmType.Sha1;
}

return ChecksumAlgorithmType.Unknown;
}

private ChecksumAlgorithmType ChecksumAlgorithmForFullPdb()
{
string fileName = Path.GetFileName(this.FileName);
string extension = Path.GetExtension(fileName);
string pdbPath = this.FileName.Replace(fileName, fileName.Replace(extension, ".pdb"));

if (!File.Exists(pdbPath))
{
return ChecksumAlgorithmType.Unknown;
}

using var pdbStream = new FileStream(pdbPath, FileMode.Open, FileAccess.Read);

var metadataProvider = new SymMetadataProvider(this.metadataReader);
object importer = SymUnmanagedReaderFactory.CreateSymReaderMetadataImport(metadataProvider);
ISymUnmanagedReader3 reader = SymUnmanagedReaderFactory.CreateReaderWithMetadataImport<ISymUnmanagedReader3>(pdbStream, importer, SymUnmanagedReaderCreationOptions.UseComRegistry);

try
{
Guid algorithm = Guid.Empty;
foreach (ISymUnmanagedDocument document in reader.GetDocuments())
{
document.GetChecksumAlgorithmId(ref algorithm);
return algorithm == sha256guid ? ChecksumAlgorithmType.Sha256 : ChecksumAlgorithmType.Sha1;
}
}
finally
{
_ = ((ISymUnmanagedDispose)reader).Destroy();
}

return ChecksumAlgorithmType.Unknown;
}
}
}
105 changes: 105 additions & 0 deletions src/BinaryParsers/PEBinary/PortableExecutable/SymMetadataProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

namespace Microsoft.DiaSymReader.Tools
{
internal sealed class SymMetadataProvider : ISymWriterMetadataProvider, ISymReaderMetadataProvider
{
private readonly MetadataReader _reader;

internal SymMetadataProvider(MetadataReader reader)
{
_reader = reader;
}

public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length)
{
var sigHandle = (StandaloneSignatureHandle)MetadataTokens.Handle(standaloneSignatureToken);
if (sigHandle.IsNil)
{
signature = null;
length = 0;
return false;
}

StandaloneSignature sig = _reader.GetStandaloneSignature(sigHandle);
BlobReader blobReader = _reader.GetBlobReader(sig.Signature);

signature = blobReader.StartPointer;
length = blobReader.Length;
return true;
}

public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, [NotNullWhen(true)] out string namespaceName, [NotNullWhen(true)] out string typeName, out TypeAttributes attributes)
{
var handle = (TypeDefinitionHandle)MetadataTokens.Handle(typeDefinitionToken);
if (handle.IsNil)
{
namespaceName = null;
typeName = null;
attributes = 0;
return false;
}

TypeDefinition typeDefinition = _reader.GetTypeDefinition(handle);
namespaceName = _reader.GetString(typeDefinition.Namespace);
typeName = _reader.GetString(typeDefinition.Name);
attributes = typeDefinition.Attributes;
return true;
}

public bool TryGetTypeReferenceInfo(int typeReferenceToken, [NotNullWhen(true)] out string namespaceName, [NotNullWhen(true)] out string typeName)
{
var handle = (TypeReferenceHandle)MetadataTokens.Handle(typeReferenceToken);
if (handle.IsNil)
{
namespaceName = null;
typeName = null;
return false;
}

TypeReference typeReference = _reader.GetTypeReference(handle);
namespaceName = _reader.GetString(typeReference.Namespace);
typeName = _reader.GetString(typeReference.Name);
return true;
}

public bool TryGetEnclosingType(int nestedTypeToken, out int enclosingTypeToken)
{
TypeDefinition nestedTypeDef = _reader.GetTypeDefinition(MetadataTokens.TypeDefinitionHandle(nestedTypeToken));
TypeDefinitionHandle declaringTypeHandle = nestedTypeDef.GetDeclaringType();

if (declaringTypeHandle.IsNil)
{
enclosingTypeToken = 0;
return false;
}
else
{
enclosingTypeToken = MetadataTokens.GetToken(declaringTypeHandle);
return true;
}
}

public bool TryGetMethodInfo(int methodDefinitionToken, [NotNullWhen(true)] out string methodName, out int declaringTypeToken)
{
var handle = (MethodDefinitionHandle)MetadataTokens.Handle(methodDefinitionToken);
if (handle.IsNil)
{
methodName = null;
declaringTypeToken = 0;
return false;
}

MethodDefinition methodDefinition = _reader.GetMethodDefinition(handle);
methodName = _reader.GetString(methodDefinition.Name);
declaringTypeToken = MetadataTokens.GetToken(methodDefinition.GetDeclaringType());
return true;
}
}
}
Loading

0 comments on commit a7816c7

Please sign in to comment.