Skip to content

Commit

Permalink
Improve handling of directories and sub section data
Browse files Browse the repository at this point in the history
  • Loading branch information
xoofx committed Sep 21, 2024
1 parent d595071 commit 61533b8
Show file tree
Hide file tree
Showing 34 changed files with 1,047 additions and 485 deletions.
8 changes: 8 additions & 0 deletions src/LibObjectFile/Diagnostics/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,12 @@ public enum DiagnosticId
PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3045,
PE_ERR_ImportAddressTableNotFound = 3046,
PE_ERR_InvalidInternalState = 3047,

// PE Export
PE_ERR_ExportAddressTableInvalidRVA = 3060,
PE_ERR_ExportDirectoryInvalidAddressOfNames = 3061,
PE_ERR_ExportDirectoryInvalidAddressOfFunctions = 3062,
PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3063,
PE_ERR_ExportDirectoryInvalidName = 3064,
PE_ERR_ExportNameTableInvalidRVA = 3065,
}
11 changes: 8 additions & 3 deletions src/LibObjectFile/ObjectFileElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,16 @@ internal set
/// <summary>
/// Checks if the specified offset is contained by this instance.
/// </summary>
/// <param name="offset">The offset to check if it belongs to this instance.</param>
/// <param name="position">The offset to check if it belongs to this instance.</param>
/// <returns><c>true</c> if the offset is within the segment or section range.</returns>
public bool Contains(ulong offset)
public bool Contains(ulong position)
{
return offset >= Position && offset < Position + Size;
return position >= Position && position < Position + Size;
}

public bool Contains(ulong position, uint size)
{
return position >= Position && position + size <= Position + Size;
}

/// <summary>
Expand Down
10 changes: 4 additions & 6 deletions src/LibObjectFile/ObjectFileReaderWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibObjectFile.Diagnostics;
using LibObjectFile.IO;
Expand Down Expand Up @@ -64,7 +65,7 @@ public ulong Length
public int Read(byte[] buffer, int offset, int count) => Stream.Read(buffer, offset, count);

public int Read(Span<byte> buffer) => Stream.Read(buffer);

public void ReadExactly(Span<byte> buffer) => Stream.ReadExactly(buffer);

/// <summary>
Expand Down Expand Up @@ -191,11 +192,8 @@ public unsafe bool TryReadData<T>(int sizeToRead, out T data) where T : unmanage
data = default;
}

fixed (void* pData = &data)
{
var span = new Span<byte>(pData, sizeToRead);
byteRead = Stream.Read(span);
}
Unsafe.SkipInit(out data);
byteRead = Stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data, 1)));
}
return byteRead == sizeToRead;
}
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public PEBaseRelocation(PEBaseRelocationType type, ushort virtualOffset)
public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask);

/// <summary>
/// Gets the virtual offset of the base relocation relative to the offset of the associated <see cref="PEBaseRelocationPageBlock"/>.
/// Gets the virtual offset of the base relocation relative to the offset of the associated <see cref="PEBaseRelocationBlock"/>.
/// </summary>
public ushort OffsetInBlockPart => (ushort)(_value & VirtualOffsetMask);

Expand Down
125 changes: 125 additions & 0 deletions src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.

using LibObjectFile.Diagnostics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace LibObjectFile.PE;

/// <summary>
/// A block of base relocations for a page.
/// </summary>
[DebuggerDisplay("{ToString(),nq}")]
public sealed class PEBaseRelocationBlock
{
/// <summary>
/// Initializes a new instance of the <see cref="PEBaseRelocationBlock"/> class.
/// </summary>
/// <param name="sectionLink">The section link.</param>
public PEBaseRelocationBlock(PESectionLink sectionLink)
{
SectionLink = sectionLink;
}

/// <summary>
/// Gets or sets the linked <see cref="PESection"/> and its virtual offset within it.
/// </summary>
public PESectionLink SectionLink { get; set; }

/// <summary>
/// Gets the list of relocations for this block.
/// </summary>
public List<PEBaseRelocationPageBlockPart> Parts { get; } = new();

/// <summary>
/// Internal buffer used to read the block before transforming it into parts.
/// </summary>
internal Memory<byte> BlockBuffer { get; set; }

/// <summary>
/// Gets the size of this block.
/// </summary>
internal uint CalculateSizeOf()
{
// If we have a block buffer (when reading an image), use it directly until it is transformed into parts
if (!BlockBuffer.IsEmpty)
{
return (uint)BlockBuffer.Length;
}

uint size = 0;
foreach (var part in Parts)
{
size += part.SizeOf;
}

return size;
}

internal void ReadAndBind(PEImageReader reader)
{
var buffer = BlockBuffer;

var relocSpan = MemoryMarshal.Cast<byte, PEBaseRelocation>(buffer.Span);

// Remove padding zeros at the end of the block
if (relocSpan.Length > 0 && relocSpan[^1].IsZero)
{
relocSpan = relocSpan.Slice(0, relocSpan.Length - 1);
}

PEBaseRelocationPageBlockPart? currentBlockPart = null;
var blockBaseAddress = SectionLink.RVA();

var peFile = reader.File;

// Iterate on all relocations
foreach (var relocation in relocSpan)
{
if (relocation.IsZero)
{
continue;
}

var va = blockBaseAddress + relocation.OffsetInBlockPart;

// Find the section data containing the virtual address
if (!peFile.TryFindVirtualContainer(va, out var vObj))
{
reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}");
continue;
}

var sectionData = vObj as PESectionData;
if (sectionData is null)
{
reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"The section data containing the virtual address 0x{va:X4} is not a section data");
continue;
}

var offsetInSectionData = va - sectionData.VirtualAddress;

// Create a new block part if the section data is different, or it is the first relocation
if (currentBlockPart is null || currentBlockPart.SectionDataLink.SectionData != sectionData)
{
currentBlockPart = new PEBaseRelocationPageBlockPart(new(sectionData, offsetInSectionData));
Parts.Add(currentBlockPart);
}

var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataLink.Offset));
currentBlockPart.Relocations.Add(newRelocation);
}

// Clear the buffer, as we don't need it anymore
BlockBuffer = Memory<byte>.Empty;
}

public override string ToString()
{
return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Section?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Parts[{Parts.Count}]";
}
}
Loading

0 comments on commit 61533b8

Please sign in to comment.