/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see .
*/
using System;
using System.ComponentModel.Composition;
using dnSpy.Contracts.Hex;
using dnSpy.Contracts.Hex.Files;
using dnSpy.Contracts.Hex.Files.DotNet;
using dnSpy.Contracts.Hex.Files.PE;
namespace dnSpy.AsmEditor.Hex.PE {
abstract class PEStructureProviderFactory {
public abstract PEStructureProvider? TryGetProvider(HexBufferFile file);
}
[Export(typeof(PEStructureProviderFactory))]
sealed class PEStructureProviderFactoryImpl : PEStructureProviderFactory {
public override PEStructureProvider? TryGetProvider(HexBufferFile file) {
if (file is null)
throw new ArgumentNullException(nameof(file));
var peHeaders = file.GetHeaders();
if (peHeaders is null)
return null;
return file.Properties.GetOrCreateSingletonProperty(typeof(PEStructureProviderImpl), () => new PEStructureProviderImpl(file, peHeaders));
}
}
abstract class PEStructureProvider {
public abstract HexBufferFile BufferFile { get; }
public abstract HexBuffer Buffer { get; }
public abstract HexSpan PESpan { get; }
public abstract ImageDosHeaderVM ImageDosHeader { get; }
public abstract ImageFileHeaderVM ImageFileHeader { get; }
public abstract ImageOptionalHeaderVM ImageOptionalHeader { get; }
public abstract ImageSectionHeaderVM[] Sections { get; }
/// Can be null if it's not a .NET file
public abstract ImageCor20HeaderVM? ImageCor20Header { get; }
/// Can be null if it's not a .NET file
public abstract StorageSignatureVM? StorageSignature { get; }
/// Can be null if it's not a .NET file
public abstract StorageHeaderVM? StorageHeader { get; }
public abstract StorageStreamVM[] StorageStreams { get; }
/// Can be null if it's not a .NET file
public abstract TablesStreamVM? TablesStream { get; }
public abstract HexPosition RvaToBufferPosition(uint rva);
public abstract uint BufferPositionToRva(HexPosition position);
}
sealed class PEStructureProviderImpl : PEStructureProvider {
public override HexBufferFile BufferFile => file;
public override HexBuffer Buffer => file.Buffer;
public override HexSpan PESpan => file.Span;
public override ImageDosHeaderVM ImageDosHeader => imageDosHeader;
public override ImageFileHeaderVM ImageFileHeader => imageFileHeader;
public override ImageOptionalHeaderVM ImageOptionalHeader => imageOptionalHeader;
public override ImageSectionHeaderVM[] Sections => sections;
public override ImageCor20HeaderVM? ImageCor20Header => imageCor20Header;
public override StorageSignatureVM? StorageSignature => storageSignature;
public override StorageHeaderVM? StorageHeader => storageHeader;
public override StorageStreamVM[] StorageStreams => storageStreams;
public override TablesStreamVM? TablesStream => tablesStream;
readonly HexBufferFile file;
readonly PeHeaders peHeaders;
readonly ImageDosHeaderVM imageDosHeader;
readonly ImageFileHeaderVM imageFileHeader;
readonly ImageOptionalHeaderVM imageOptionalHeader;
readonly ImageSectionHeaderVM[] sections;
readonly ImageCor20HeaderVM? imageCor20Header;
readonly StorageSignatureVM? storageSignature;
readonly StorageHeaderVM? storageHeader;
readonly StorageStreamVM[] storageStreams;
readonly TablesStreamVM? tablesStream;
public PEStructureProviderImpl(HexBufferFile file, PeHeaders peHeaders) {
if (file is null)
throw new ArgumentNullException(nameof(file));
if (peHeaders is null)
throw new ArgumentNullException(nameof(peHeaders));
if (peHeaders != file.GetHeaders())
throw new ArgumentException();
this.file = file;
this.peHeaders = peHeaders;
var buffer = file.Buffer;
imageDosHeader = new ImageDosHeaderVM(buffer, peHeaders.DosHeader);
imageFileHeader = new ImageFileHeaderVM(buffer, peHeaders.FileHeader);
if (peHeaders.OptionalHeader.Is32Bit)
imageOptionalHeader = new ImageOptionalHeader32VM(buffer, (PeOptionalHeader32Data)peHeaders.OptionalHeader);
else
imageOptionalHeader = new ImageOptionalHeader64VM(buffer, (PeOptionalHeader64Data)peHeaders.OptionalHeader);
sections = new ImageSectionHeaderVM[peHeaders.Sections.FieldCount];
for (int i = 0; i < sections.Length; i++)
sections[i] = new ImageSectionHeaderVM(buffer, peHeaders.Sections[i].Data);
var dnHeaders = file.GetHeaders();
storageStreams = Array.Empty();
if (dnHeaders is not null) {
imageCor20Header = new ImageCor20HeaderVM(buffer, dnHeaders.Cor20);
var mdHeaders = dnHeaders.MetadataHeaders;
if (mdHeaders is not null) {
storageSignature = new StorageSignatureVM(buffer, mdHeaders.MetadataHeader);
storageHeader = new StorageHeaderVM(buffer, mdHeaders.MetadataHeader);
storageStreams = new StorageStreamVM[mdHeaders.Streams.Count];
for (int i = 0; i < storageStreams.Length; i++) {
var ssh = mdHeaders.MetadataHeader.StreamHeaders.Data[i].Data;
var heap = mdHeaders.Streams[i];
storageStreams[i] = new StorageStreamVM(buffer, heap, ssh, i);
}
var metadataTables = new MetadataTableVM[0x40];
if (mdHeaders.TablesStream is not null) {
tablesStream = new TablesStreamVM(buffer, mdHeaders.TablesStream, metadataTables);
var stringsHeapSpan = GetSpan(mdHeaders.StringsStream);
var guidHeapSpan = GetSpan(mdHeaders.GUIDStream);
foreach (var mdTable in mdHeaders.TablesStream.MDTables) {
if (mdTable.Rows != 0)
metadataTables[(int)mdTable.Table] = MetadataTableVM.Create(buffer, tablesStream, mdTable, stringsHeapSpan, guidHeapSpan);
}
}
}
}
}
static HexSpan GetSpan(DotNetHeap? heap) => heap?.Span.Span ?? default;
public override HexPosition RvaToBufferPosition(uint rva) =>
peHeaders.RvaToBufferPosition(rva);
public override uint BufferPositionToRva(HexPosition position) =>
peHeaders.BufferPositionToRva(position);
}
}