Skip to content

Commit

Permalink
Mono MD reader fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
wtfsck committed Apr 19, 2018
1 parent 9b86179 commit c40e148
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 20 deletions.
11 changes: 4 additions & 7 deletions src/DotNet/MD/MetadataFactory.cs
Expand Up @@ -128,23 +128,20 @@ enum MetadataType {
MetadataBase md = null;
try {
var dotNetDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[14];
// Mono doesn't check that the Size field is >= 0x48
if (dotNetDir.VirtualAddress == 0)
throw new BadImageFormatException(".NET data directory RVA is 0");
if (dotNetDir.Size < 0x48)
throw new BadImageFormatException(".NET data directory size < 0x48");
var cor20HeaderReader = peImage.CreateReader(dotNetDir.VirtualAddress, 0x48);
var cor20Header = new ImageCor20Header(ref cor20HeaderReader, verify);
if (cor20Header.Metadata.VirtualAddress == 0)
throw new BadImageFormatException(".NET metadata RVA is 0");
if (cor20Header.Metadata.Size < 16)
throw new BadImageFormatException(".NET metadata size is too small");
var mdSize = cor20Header.Metadata.Size;
var mdRva = cor20Header.Metadata.VirtualAddress;
var mdHeaderReader = peImage.CreateReader(mdRva, mdSize);
// Don't use the size field, Mono ignores it. Create a reader that can read to EOF.
var mdHeaderReader = peImage.CreateReader(mdRva);
var mdHeader = new MetadataHeader(ref mdHeaderReader, verify);
if (verify) {
foreach (var sh in mdHeader.StreamHeaders) {
if (sh.Offset + sh.StreamSize < sh.Offset || sh.Offset + sh.StreamSize > mdSize)
if ((ulong)sh.Offset + sh.StreamSize > mdHeaderReader.EndOffset)
throw new BadImageFormatException("Invalid stream header");
}
}
Expand Down
11 changes: 7 additions & 4 deletions src/DotNet/MD/MetadataHeader.cs
Expand Up @@ -91,8 +91,6 @@ public sealed class MetadataHeader : FileSection {
throw new BadImageFormatException("Invalid metadata header signature");
majorVersion = reader.ReadUInt16();
minorVersion = reader.ReadUInt16();
if (verify && !((majorVersion == 1 && minorVersion == 1) || (majorVersion == 0 && minorVersion >= 19)))
throw new BadImageFormatException($"Unknown metadata header version: {majorVersion}.{minorVersion}");
reserved1 = reader.ReadUInt32();
stringLength = reader.ReadUInt32();
versionString = ReadString(ref reader, stringLength);
Expand All @@ -101,8 +99,13 @@ public sealed class MetadataHeader : FileSection {
reserved2 = reader.ReadByte();
streams = reader.ReadUInt16();
streamHeaders = new StreamHeader[streams];
for (int i = 0; i < streamHeaders.Count; i++)
streamHeaders[i] = new StreamHeader(ref reader, verify);
for (int i = 0; i < streamHeaders.Count; i++) {
// Mono doesn't verify all of these so we can't either
var sh = new StreamHeader(ref reader, throwOnError: false, verify, out bool failedVerification);
if (failedVerification || (ulong)sh.Offset + sh.StreamSize > reader.EndOffset)
sh = new StreamHeader(0, 0, "<invalid>");
streamHeaders[i] = sh;
}
SetEndoffset(ref reader);
}

Expand Down
21 changes: 17 additions & 4 deletions src/DotNet/MD/StreamHeader.cs
Expand Up @@ -36,17 +36,30 @@ public sealed class StreamHeader : FileSection {
/// <param name="reader">PE file reader pointing to the start of this section</param>
/// <param name="verify">Verify section</param>
/// <exception cref="BadImageFormatException">Thrown if verification fails</exception>
public StreamHeader(ref DataReader reader, bool verify) {
public StreamHeader(ref DataReader reader, bool verify)
: this(ref reader, verify, verify, out _) {
}

internal StreamHeader(ref DataReader reader, bool throwOnError, bool verify, out bool failedVerification) {
failedVerification = false;
SetStartOffset(ref reader);
offset = reader.ReadUInt32();
streamSize = reader.ReadUInt32();
name = ReadString(ref reader, 32, verify);
name = ReadString(ref reader, 32, verify, ref failedVerification);
SetEndoffset(ref reader);
if (verify && offset + size < offset)
failedVerification = true;
if (throwOnError && failedVerification)
throw new BadImageFormatException("Invalid stream header");
}

static string ReadString(ref DataReader reader, int maxLen, bool verify) {
internal StreamHeader(uint offset, uint streamSize, string name) {
this.offset = offset;
this.streamSize = streamSize;
this.name = name ?? throw new ArgumentNullException(nameof(name));
}

static string ReadString(ref DataReader reader, int maxLen, bool verify, ref bool failedVerification) {
var origPos = reader.Position;
var sb = new StringBuilder(maxLen);
int i;
Expand All @@ -57,7 +70,7 @@ public sealed class StreamHeader : FileSection {
sb.Append((char)b);
}
if (verify && i == maxLen)
throw new BadImageFormatException("Invalid stream name string");
failedVerification = true;
if (i != maxLen)
reader.Position = origPos + (((uint)i + 1 + 3) & ~3U);
return sb.ToString();
Expand Down
2 changes: 2 additions & 0 deletions src/DotNet/MD/TablesStream.cs
Expand Up @@ -204,6 +204,8 @@ public TablesStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset,
var sizes = new uint[64];
for (int i = 0; i < 64; valid >>= 1, i++) {
uint rows = (valid & 1) == 0 ? 0 : reader.ReadUInt32();
// Mono ignores the high byte
rows &= 0x00FFFFFF;
if (i >= maxPresentTables)
rows = 0;
sizes[i] = rows;
Expand Down
2 changes: 1 addition & 1 deletion src/PE/IImageOptionalHeader.cs
Expand Up @@ -158,7 +158,7 @@ public interface IImageOptionalHeader : IFileSection {
uint NumberOfRvaAndSizes { get; }

/// <summary>
/// Returns the DataDirectories field
/// Returns the DataDirectories field. This array contains exactly 16 elements.
/// </summary>
ImageDataDirectory[] DataDirectories { get; }
}
Expand Down
3 changes: 2 additions & 1 deletion src/PE/ImageNTHeaders.cs
Expand Up @@ -36,7 +36,8 @@ public sealed class ImageNTHeaders : FileSection {
public ImageNTHeaders(ref DataReader reader, bool verify) {
SetStartOffset(ref reader);
signature = reader.ReadUInt32();
if (verify && signature != 0x4550)
// Mono only checks the low 2 bytes
if (verify && (ushort)signature != 0x4550)
throw new BadImageFormatException("Invalid NT headers signature");
imageFileHeader = new ImageFileHeader(ref reader, verify);
imageOptionalHeader = CreateImageOptionalHeader(ref reader, verify);
Expand Down
2 changes: 0 additions & 2 deletions src/PE/PEImage.cs
Expand Up @@ -347,8 +347,6 @@ public PEImage(IntPtr baseAddr)

ImageDebugDirectory[] ReadImageDebugDirectories() {
try {
if (6 >= ImageNTHeaders.OptionalHeader.DataDirectories.Length)
return Array2.Empty<ImageDebugDirectory>();
var dataDir = ImageNTHeaders.OptionalHeader.DataDirectories[6];
if (dataDir.VirtualAddress == 0)
return Array2.Empty<ImageDebugDirectory>();
Expand Down
10 changes: 9 additions & 1 deletion src/PE/PEInfo.cs
Expand Up @@ -43,7 +43,15 @@ sealed class PEInfo {
imageNTHeaders = new ImageNTHeaders(ref reader, verify);

reader.Position = (uint)imageNTHeaders.OptionalHeader.StartOffset + imageNTHeaders.FileHeader.SizeOfOptionalHeader;
imageSectionHeaders = new ImageSectionHeader[imageNTHeaders.FileHeader.NumberOfSections];
int numSections = imageNTHeaders.FileHeader.NumberOfSections;
if (numSections > 0) {
// Mono doesn't verify the section count
var tempReader = reader;
tempReader.Position += 0x14;
uint firstSectionOffset = tempReader.ReadUInt32();
numSections = Math.Min(numSections, (int)((firstSectionOffset - reader.Position) / 0x28));
}
imageSectionHeaders = new ImageSectionHeader[numSections];
for (int i = 0; i < imageSectionHeaders.Length; i++)
imageSectionHeaders[i] = new ImageSectionHeader(ref reader, verify);
}
Expand Down

0 comments on commit c40e148

Please sign in to comment.