Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute deterministic mvid from metadata #587

Merged
merged 4 commits into from
May 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions Mono.Cecil.PE/ImageWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -814,14 +814,6 @@ public uint GetHeaderSize ()
return pe_header_size + SizeOfOptionalHeader () + (sections * section_header_size);
}

public void PatchMvid (Guid guid)
{
uint offset = GetRVAFileOffset (text, text_map.GetRVA (TextSegment.GuidHeap));
BaseStream.Seek (offset, SeekOrigin.Begin);
var arr = guid.ToByteArray ();
BaseStream.Write (arr, 0, arr.Length);
}

void PatchWin32Resources (ByteBuffer resources)
{
PatchResourceDirectoryTable (resources);
Expand Down
1 change: 0 additions & 1 deletion Mono.Cecil.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net40</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="ProjectInfo.cs" />
Expand Down
67 changes: 23 additions & 44 deletions Mono.Cecil/AssemblyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ static void Write (ModuleDefinition module, Disposable<Stream> stream, WriterPar

if (parameters.DeterministicMvid)
module.Mvid = Guid.Empty;

var metadata = new MetadataBuilder (module, fq_name, timestamp, symbol_writer_provider);
try {
module.metadata_builder = metadata;
Expand All @@ -117,62 +118,21 @@ static void Write (ModuleDefinition module, Disposable<Stream> stream, WriterPar
metadata.SetSymbolWriter (symbol_writer);
BuildMetadata (module, metadata);

if (parameters.DeterministicMvid)
metadata.ComputeDeterministicMvid ();

var writer = ImageWriter.CreateWriter (module, metadata, stream);
stream.value.SetLength (0);
writer.WriteImage ();

if (parameters.StrongNameKeyPair != null)
CryptoService.StrongName (stream.value, writer, parameters.StrongNameKeyPair);
if (parameters.DeterministicMvid) {
module.Mvid = ComputeGuid (stream.value);
writer.PatchMvid (module.Mvid);
}
}
} finally {
module.metadata_builder = null;
}
}

static void CopyStreamChunk (Stream stream, Stream dest_stream, byte [] buffer, int length)
{
while (length > 0) {
int read = stream.Read (buffer, 0, System.Math.Min (buffer.Length, length));
dest_stream.Write (buffer, 0, read);
length -= read;
}
}

static byte [] ComputeHash (Stream stream)
{
const int buffer_size = 8192;

var sha1 = new SHA1Managed ();

stream.Seek (0, SeekOrigin.Begin);
var buffer = new byte [buffer_size];

using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write))
CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length);
return sha1.Hash;
}

static unsafe Guid ComputeGuid (Stream stream)
{
byte[] hashCode = ComputeHash (stream);

// From corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs
Guid guid = default(Guid);
byte* guidPtr = (byte*)&guid;
for (var i = 0; i < 16; i++) {
guidPtr[i] = hashCode[i];
}
// modify the guid data so it decodes to the form of a "random" guid ala rfc4122
guidPtr[7] = (byte)((guidPtr[7] & 0x0f) | (4 << 4));
guidPtr[8] = (byte)((guidPtr[8] & 0x3f) | (2 << 6));

return guid;
}

static void BuildMetadata (ModuleDefinition module, MetadataBuilder metadata)
{
if (!module.HasImage) {
Expand Down Expand Up @@ -2684,6 +2644,25 @@ void AddSequencePoints (MethodDebugInformation info)

method_debug_information_table.rows [rid - 1].Col2 = GetBlobIndex (signature);
}

public void ComputeDeterministicMvid ()
{
var guid = CryptoService.ComputeGuid (CryptoService.ComputeHash (
data,
resources,
string_heap,
user_string_heap,
blob_heap,
table_heap,
code));

var position = guid_heap.position;
guid_heap.position = 0;
guid_heap.WriteBytes (guid.ToByteArray ());
guid_heap.position = position;

module.Mvid = guid;
}
}

sealed class SignatureWriter : ByteBuffer {
Expand Down
36 changes: 32 additions & 4 deletions Mono.Security.Cryptography/CryptoService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,48 @@ public static byte [] ComputeHash (string file)
if (!File.Exists (file))
return Empty<byte>.Array;

using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read))
return ComputeHash (stream);
}

public static byte [] ComputeHash (Stream stream)
{
const int buffer_size = 8192;

var sha1 = new SHA1Managed ();
var buffer = new byte [buffer_size];

using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write))
CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length);

var buffer = new byte [buffer_size];
return sha1.Hash;
}

using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write))
CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length);
public static byte [] ComputeHash (params ByteBuffer [] buffers)
{
var sha1 = new SHA1Managed ();

using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) {
for (int i = 0; i < buffers.Length; i++) {
crypto_stream.Write (buffers [0].buffer, 0, buffers [0].length);
}
}

return sha1.Hash;
}

public static Guid ComputeGuid (byte [] hash)
{
// From corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs
var guid = new byte [16];
Buffer.BlockCopy (hash, 0, guid, 0, 16);

// modify the guid data so it decodes to the form of a "random" guid ala rfc4122
guid [7] = (byte) ((guid [7] & 0x0f) | (4 << 4));
guid [8] = (byte) ((guid [8] & 0x3f) | (2 << 6));

return new Guid (guid);
}
}

static partial class Mixin {
Expand Down
14 changes: 14 additions & 0 deletions Test/Mono.Cecil.Tests/PortablePdbTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,5 +669,19 @@ public void UseCustomSymbolWriterToChangeDebugHeaderPdbPath ()
Assert.AreEqual (Path.GetFileName (debug_header_pdb_path), pdb_path);
}
}

[Test]
public void WriteAndReadAgainModuleWithDeterministicMvid ()
{
const string resource = "mylib.dll";
string destination = Path.GetTempFileName ();

using (var module = GetResourceModule (resource, new ReaderParameters { SymbolReaderProvider = new PortablePdbReaderProvider () })) {
module.Write (destination, new WriterParameters { DeterministicMvid = true, SymbolWriterProvider = new SymbolWriterProvider () });
}

using (var module = ModuleDefinition.ReadModule (destination, new ReaderParameters { SymbolReaderProvider = new PortablePdbReaderProvider () })) {
}
}
}
}