Skip to content

Commit

Permalink
Import Address tables and started work on Import Hint Name tables.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lloyd Kinsella committed Aug 3, 2018
1 parent 4f3fce7 commit c42b900
Show file tree
Hide file tree
Showing 12 changed files with 440 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/Workshell.PE.Testbed/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ static async Task RunAsync(string[] args)
var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.DelayImportDescriptor];
var content = await dataDirectory.GetContentAsync().ConfigureAwait(false);

var table = await ImportAddressTables.GetLookupTableAsync(image);

//var table = await ImportHintNameTable.LoadAsync(image).ConfigureAwait(false);
//var entries = table.ToArray();
}
Expand Down
13 changes: 13 additions & 0 deletions src/Workshell.PE/Content/Imports/DelayedImportAddressTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content
{
public sealed class DelayedImportAddressTable : ImportAddressTableBase<DelayedImportAddressTableEntry>
{
internal DelayedImportAddressTable(PortableExecutableImage image, uint rva, ulong[] entries) : base(image, rva, entries, true)
{
}
}
}
13 changes: 13 additions & 0 deletions src/Workshell.PE/Content/Imports/DelayedImportAddressTableEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content
{
public sealed class DelayedImportAddressTableEntry : ImportAddressTableEntryBase
{
internal DelayedImportAddressTableEntry(PortableExecutableImage image, ulong entryOffset, ulong entryValue, uint entryAddress, ushort entryOrdinal, bool isOrdinal) : base(image, entryOffset, entryValue, entryAddress, entryOrdinal, isOrdinal, true)
{
}
}
}
91 changes: 91 additions & 0 deletions src/Workshell.PE/Content/Imports/DelayedImportAddressTables.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

using Workshell.PE.Extensions;

namespace Workshell.PE.Content
{
public sealed class DelayedImportAddressTables: ImportAddressTablesBase<DelayedImportAddressTable, DelayedImportAddressTableEntry>
{
internal DelayedImportAddressTables(PortableExecutableImage image, DataDirectory directory, Location location, Tuple<uint, ulong[]>[] tables) : base(image, directory, location, tables, false)
{
}

#region Static Methods

public static async Task<DelayedImportAddressTables> GetLookupTableAsync(PortableExecutableImage image, DelayedImportDirectory directory = null)
{
return await GetTableAsync(image, directory, (entry) => entry.DelayNameTable).ConfigureAwait(false);
}

public static async Task<DelayedImportAddressTables> GetAddressTableAsync(PortableExecutableImage image, DelayedImportDirectory directory = null)
{
return await GetTableAsync(image, directory, (entry) => entry.DelayAddressTable).ConfigureAwait(false);
}

private static async Task<DelayedImportAddressTables> GetTableAsync(PortableExecutableImage image, DelayedImportDirectory directory, Func<DelayedImportDirectoryEntry, uint> thunkHandler)
{
if (directory == null)
directory = await DelayedImportDirectory.LoadAsync(image).ConfigureAwait(false);

var calc = image.GetCalculator();
var stream = image.GetStream();
var tables = new List<Tuple<uint, ulong[]>>();

foreach (var dirEntry in directory)
{
var thunk = thunkHandler(dirEntry);

if (thunk == 0)
continue;

var entries = new List<ulong>();
var offset = calc.RVAToOffset(thunk);

stream.Seek(offset.ToInt64(), SeekOrigin.Begin);

while (true)
{
var entry = (!image.Is64Bit ? await stream.ReadUInt32Async().ConfigureAwait(false) : await stream.ReadUInt64Async().ConfigureAwait(false));

entries.Add(entry);

if (entry == 0)
break;
}

var table = new Tuple<uint, ulong[]>(thunk, entries.ToArray());

tables.Add(table);
}

var rva = 0u;

if (tables.Count > 0)
rva = tables.MinBy(table => table.Item1).Item1;

var imageBase = image.NTHeaders.OptionalHeader.ImageBase;
var va = imageBase + rva;
var fileOffset = calc.RVAToOffset(rva);
var fileSize = 0ul;

foreach (var table in tables)
{
var size = (table.Item2.Length + 1) * (!image.Is64Bit ? sizeof(uint) : sizeof(ulong));

fileSize += size.ToUInt32();
}

var section = calc.RVAToSection(rva);
var location = new Location(fileOffset, rva, va, fileSize, fileSize, section);
var result = new DelayedImportAddressTables(image, directory.Directory, location, tables.ToArray());

return result;
}

#endregion
}
}
13 changes: 13 additions & 0 deletions src/Workshell.PE/Content/Imports/DelayedImportHintNameEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content
{
public sealed class DelayedImportHintNameEntry : ImportHintNameEntryBase
{
internal DelayedImportHintNameEntry(PortableExecutableImage image, ulong offset, uint size, ushort entryHint, string entryName, bool isPadded) : base(image, offset, size, entryHint, entryName, isPadded, true)
{
}
}
}
2 changes: 1 addition & 1 deletion src/Workshell.PE/Content/Imports/ImportAddressTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content.Imports
namespace Workshell.PE.Content
{
public sealed class ImportAddressTable : ImportAddressTableBase<ImportAddressTableEntry>
{
Expand Down
91 changes: 91 additions & 0 deletions src/Workshell.PE/Content/Imports/ImportAddressTables.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

using Workshell.PE.Extensions;

namespace Workshell.PE.Content
{
public sealed class ImportAddressTables: ImportAddressTablesBase<ImportAddressTable, ImportAddressTableEntry>
{
internal ImportAddressTables(PortableExecutableImage image, DataDirectory directory, Location location, Tuple<uint, ulong[]>[] tables) : base(image, directory, location, tables, false)
{
}

#region Static Methods

public static async Task<ImportAddressTables> GetLookupTableAsync(PortableExecutableImage image, ImportDirectory directory = null)
{
return await GetTableAsync(image, directory, (entry) => entry.OriginalFirstThunk).ConfigureAwait(false);
}

public static async Task<ImportAddressTables> GetAddressTableAsync(PortableExecutableImage image, ImportDirectory directory = null)
{
return await GetTableAsync(image, directory, (entry) => entry.FirstThunk).ConfigureAwait(false);
}

private static async Task<ImportAddressTables> GetTableAsync(PortableExecutableImage image, ImportDirectory directory, Func<ImportDirectoryEntry, uint> thunkHandler)
{
if (directory == null)
directory = await ImportDirectory.LoadAsync(image).ConfigureAwait(false);

var calc = image.GetCalculator();
var stream = image.GetStream();
var tables = new List<Tuple<uint, ulong[]>>();

foreach (var dirEntry in directory)
{
var thunk = thunkHandler(dirEntry);

if (thunk == 0)
continue;

var entries = new List<ulong>();
var offset = calc.RVAToOffset(thunk);

stream.Seek(offset.ToInt64(), SeekOrigin.Begin);

while (true)
{
var entry = (!image.Is64Bit ? await stream.ReadUInt32Async().ConfigureAwait(false) : await stream.ReadUInt64Async().ConfigureAwait(false));

entries.Add(entry);

if (entry == 0)
break;
}

var table = new Tuple<uint, ulong[]>(thunk, entries.ToArray());

tables.Add(table);
}

var rva = 0u;

if (tables.Count > 0)
rva = tables.MinBy(table => table.Item1).Item1;

var imageBase = image.NTHeaders.OptionalHeader.ImageBase;
var va = imageBase + rva;
var fileOffset = calc.RVAToOffset(rva);
var fileSize = 0ul;

foreach (var table in tables)
{
var size = table.Item2.Length * (!image.Is64Bit ? sizeof(uint) : sizeof(ulong));

fileSize += size.ToUInt32();
}

var section = calc.RVAToSection(rva);
var location = new Location(fileOffset, rva, va, fileSize, fileSize, section);
var result = new ImportAddressTables(image, directory.Directory, location, tables.ToArray());

return result;
}

#endregion
}
}
59 changes: 59 additions & 0 deletions src/Workshell.PE/Content/Imports/ImportAddressTablesBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Workshell.PE.Content
{
public abstract class ImportAddressTablesBase<TTable, TTableEntry> : DataContent, IEnumerable<TTable>
where TTable : ImportAddressTableBase<TTableEntry>
where TTableEntry : ImportAddressTableEntryBase
{
private readonly TTable[] _tables;

protected internal ImportAddressTablesBase(PortableExecutableImage image, DataDirectory directory, Location location, Tuple<uint, ulong[]>[] tables, bool isDelayed) : base(image, directory, location)
{
_tables = new TTable[tables.Length];

var type = typeof(TTable);
var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var ctor = ctors.First();

for (var i = 0; i < tables.Length; i++)
{
var tuple = tables[i];
var table = (TTable)ctor.Invoke(new object[] { image, tuple.Item1, tuple.Item2 });

_tables[i] = table;
}

Count = _tables.Length;
IsDelayed = isDelayed;
}

#region Methods

public IEnumerator<TTable> GetEnumerator()
{
foreach (var table in _tables)
yield return table;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion

#region Properties

public int Count { get; }
public TTable this[int index] => _tables[index];
public bool IsDelayed { get; }

#endregion
}
}
13 changes: 13 additions & 0 deletions src/Workshell.PE/Content/Imports/ImportHintNameEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content
{
public sealed class ImportHintNameEntry : ImportHintNameEntryBase
{
internal ImportHintNameEntry(PortableExecutableImage image, ulong offset, uint size, ushort entryHint, string entryName, bool isPadded) : base(image, offset, size, entryHint, entryName, isPadded, false)
{
}
}
}
59 changes: 59 additions & 0 deletions src/Workshell.PE/Content/Imports/ImportHintNameEntryBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Workshell.PE.Extensions;

namespace Workshell.PE.Content
{
public abstract class ImportHintNameEntryBase : ISupportsLocation, ISupportsBytes
{
private readonly PortableExecutableImage _image;

protected internal ImportHintNameEntryBase(PortableExecutableImage image, ulong offset, uint size, ushort entryHint, string entryName, bool isPadded, bool isDelayed)
{
_image = image;

var calc = image.GetCalculator();
var rva = calc.OffsetToRVA(offset);
var va = image.NTHeaders.OptionalHeader.ImageBase + rva;

Location = new Location(calc, offset, rva, va, size, size);
Hint = entryHint;
Name = entryName;
IsPadded = isPadded;
IsDelayed = isDelayed;
}

#region Methods

public override string ToString()
{
return $"0x{hint:X4} {name}";
}

public byte[] GetBytes()
{
return GetBytesAsync().GetAwaiter().GetResult();
}

public async Task<byte[]> GetBytesAsync()
{
var stream = _image.GetStream();
var buffer = await stream.ReadBytesAsync(Location).ConfigureAwait(false);

return buffer;
}

#endregion

#region Properties
public Location Location { get; }
public ushort Hint { get; }
public string Name { get; }
public bool IsPadded { get; }
public bool IsDelayed { get; }

#endregion
}
}
17 changes: 17 additions & 0 deletions src/Workshell.PE/Content/Imports/ImportHintNameTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content
{
public sealed class ImportHintNameTable : ImportHintNameTableBase<ImportHintNameEntry>
{
internal ImportHintNameTable(PortableExecutableImage image, DataDirectory directory, Location location, Tuple<ulong, uint, ushort, string, bool>[] entries) : base(image, directory, location, entries, false)
{
}

#region Static Methods

#endregion
}
}
Loading

0 comments on commit c42b900

Please sign in to comment.