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

[cDAC] DAC like entry point #112653

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

max-charlamb
Copy link
Contributor

No description provided.

Copy link
Contributor

Tagging subscribers to this area: @tommcdon
See info in area-owners.md if you want to be subscribed.

Comment on lines 29 to 73
using BinaryReader reader = new(new DataTargetStream(_dataTarget, _baseAddress));

ushort dosMagic = reader.ReadUInt16();
if (dosMagic != 0x5A4D) // "MZ"
return false;

// PE Header offset is at 0x3C in DOS header
reader.BaseStream.Seek(0x3C, SeekOrigin.Begin);
_peSigOffset = reader.ReadUInt32();

// Read PE signature
reader.BaseStream.Seek(_peSigOffset, SeekOrigin.Begin);
uint peSig = reader.ReadUInt32();
if (peSig != 0x00004550) // "PE00"
return false;

// Seek to beginning of opt header and read magic
reader.BaseStream.Seek(_peSigOffset + 0x18, SeekOrigin.Begin);
_optHeaderMagic = reader.ReadUInt16();

// Seek back to beginning of opt header and parse
reader.BaseStream.Seek(_peSigOffset + 0x18, SeekOrigin.Begin);
uint rva;
switch (_optHeaderMagic)
{
case 0x10B: // PE32
IMAGE_OPTIONAL_HEADER32 optHeader32 = new(reader);
rva = optHeader32.DataDirectory[0].VirtualAddress;
break;
case 0x20B: // PE32+
IMAGE_OPTIONAL_HEADER64 optHeader64 = new(reader);
rva = optHeader64.DataDirectory[0].VirtualAddress;
break;
// unknown type, invalid
default:
return false;
}

// Seek to export directory and parse
reader.BaseStream.Seek(rva, SeekOrigin.Begin);
_exportDir = new IMAGE_EXPORT_DIRECTORY(reader);

return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use System.Reflection.PortableExecutable.PEReader to get to the export directory here?

Something like the following:

using Stream stream = new DataTargetStream(_dataTarget, _baseAddress);
using PEReader reader = new(stream);

var exportsDirectory = reader.PEHeaders.PEHeader.ExportTableDirectory;

if (!reader.PEHeaders.TryGetDirectoryOffset(exportsDirectory, out var offset))
{
    return false;
}

stream.Seek(offset, SeekOrigin.Begin);
_exportDir = new IMAGE_EXPORT_DIRECTORY(new BinaryReader(stream));

return true;

This would let you get out of most of the PE decoding.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw the PEReader and based some of the code off of it.

My concern is that the PEReader does not have the signature checking semantics I was looking for.

Because the DAC only targets a single platform, it invoked the exact reader for the executable format.

As far as I can tell, using the ICLRDataTarget, we don't know which platform we are targeting. Unless I'm overlooking something, my plan is to run the PEDecoder, ELFDecoder, and MACHODecoder on each target which will be verified using the known signatures.

It looks like the PEDecoder assumes that the PE is a COFF file if the PE DOS magic isn't correct. In this case, I'd want to bail.

private static void SkipDosHeader(ref PEBinaryReader reader, out bool isCOFFOnly)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at it again, if the file parses as COFFOnly, the PEHeader won't be available.

I can check if it's valid using that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the PEReader is not safe to use on non-PE/COFF files. Even if the signatures fail to parse, the PEHeaders.cs will attempt to use the garbage COFFHeader to read an arbitrary number of sections potentially causing stream overrun.

private ImmutableArray<SectionHeader> ReadSectionHeaders(ref PEBinaryReader reader)
{
int numberOfSections = _coffHeader.NumberOfSections;
if (numberOfSections < 0)
{
throw new BadImageFormatException(SR.InvalidNumberOfSections);
}
var builder = ImmutableArray.CreateBuilder<SectionHeader>(numberOfSections);
for (int i = 0; i < numberOfSections; i++)
{
builder.Add(new SectionHeader(ref reader));
}
return builder.MoveToImmutable();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang... Yeah that's not particularly helpful in this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #112830 to add this fail-fast capability to PEReader; this is not the first time it was desired. In the meantime you can do a minimal signature validation yourself and create the PEReader afterwards:

using DataTargetStream stream = new(_dataTarget, _baseAddress);
using BinaryReader reader = new(stream, Encoding.UTF8, leaveOpen: true);

ushort dosMagic = reader.ReadUInt16();
if (dosMagic != 0x5A4D) // "MZ"
    return false;

// PE Header offset is at 0x3C in DOS header
reader.BaseStream.Seek(0x3C, SeekOrigin.Begin);
uint peSigOffset = reader.ReadUInt32();

// Read PE signature
reader.BaseStream.Seek(peSigOffset, SeekOrigin.Begin);
uint peSig = reader.ReadUInt32();
if (peSig != 0x00004550) // "PE00"
    return false;

using PEReader reader = new(stream);

var exportsDirectory = reader.PEHeaders.PEHeader!.ExportTableDirectory;

if (!reader.PEHeaders.TryGetDirectoryOffset(exportsDirectory, out var offset))
{
    return false;
}

stream.Seek(offset, SeekOrigin.Begin);
_exportDir = new IMAGE_EXPORT_DIRECTORY(reader);

return true;


public override long Position { get => _offset; set => _offset = value; }

public override unsafe int Read(byte[] buffer, int offset, int count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also override the Read overload that takes a span.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Diagnostics-coreclr enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants