Skip to content

Commit

Permalink
Added Exports support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lloyd Kinsella committed Jul 30, 2018
1 parent ed71f02 commit 1fe51e4
Show file tree
Hide file tree
Showing 11 changed files with 597 additions and 11 deletions.
6 changes: 4 additions & 2 deletions Src/Workshell.PE/DataDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public sealed class DataDirectory
private readonly Lazy<string> _sectionName;
private readonly Lazy<Section> _section;

internal DataDirectory(PortableExecutableImage image, DataDirectoryCollection dataDirs, DataDirectoryType dirType, IMAGE_DATA_DIRECTORY dataDirectory, ulong imageBase)
internal DataDirectory(PortableExecutableImage image, DataDirectories dataDirs, DataDirectoryType dirType, IMAGE_DATA_DIRECTORY dataDirectory, ulong imageBase)
{
_image = image;
_type = dirType;
Expand Down Expand Up @@ -101,6 +101,8 @@ public async Task<DataContent> GetContentAsync()
return await DebugDirectory.LoadAsync(_image).ConfigureAwait(false);
case DataDirectoryType.BaseRelocationTable:
return await RelocationTable.LoadAsync(_image).ConfigureAwait(false);
case DataDirectoryType.ExportTable:
return await ExportDirectory.LoadAsync(_image).ConfigureAwait(false);
default:
{
var calc = _image.GetCalculator();
Expand Down Expand Up @@ -198,7 +200,7 @@ private Section DoGetSection()

#region Properties

public DataDirectoryCollection Directories { get; }
public DataDirectories Directories { get; }
public DataDirectoryType DirectoryType => _type;
public uint VirtualAddress => _header.VirtualAddress;
public uint Size => _header.Size;
Expand Down
4 changes: 2 additions & 2 deletions Src/Workshell.PE/NTHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class NTHeaders : ISupportsLocation, ISupportsBytes

private readonly PortableExecutableImage _image;

internal NTHeaders(PortableExecutableImage image, ulong headerOffset, ulong imageBase, FileHeader fileHeader, OptionalHeader optHeader, DataDirectoryCollection dataDirs)
internal NTHeaders(PortableExecutableImage image, ulong headerOffset, ulong imageBase, FileHeader fileHeader, OptionalHeader optHeader, DataDirectories dataDirs)
{
_image = image;

Expand Down Expand Up @@ -51,7 +51,7 @@ public async Task<byte[]> GetBytesAsync()
public Location Location { get; }
public FileHeader FileHeader { get; }
public OptionalHeader OptionalHeader { get; }
public DataDirectoryCollection DataDirectories { get; }
public DataDirectories DataDirectories { get; }

#endregion
}
Expand Down
8 changes: 5 additions & 3 deletions src/Workshell.PE.Testbed/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Workshell.PE.Content;

namespace Workshell.PE.Testbed
{
Expand All @@ -14,11 +15,12 @@ static void Main(string[] args)

static async Task RunAsync(string[] args)
{
var image = await PortableExecutableImage.FromFileAsync(@"C:\Users\lkinsella\Downloads\IISCrypto.exe");
//var image = await PortableExecutableImage.FromFileAsync(@"C:\Windows\System32\shell32.dll");
var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.BaseRelocationTable];
//var image = await PortableExecutableImage.FromFileAsync(@"C:\Users\lkinsella\Downloads\IISCrypto.exe");
var image = await PortableExecutableImage.FromFileAsync(@"C:\Windows\System32\shell32.dll");
var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.ExportTable];
var content = await dataDirectory.GetContentAsync().ConfigureAwait(false);

var exports = await Exports.GetAsync(image).ConfigureAwait(false);
}
}
}
46 changes: 46 additions & 0 deletions src/Workshell.PE/Content/Exports/Export.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Content
{
public sealed class Export
{
internal Export(uint entryPoint, string name, uint ord, string forwardName)
{
EntryPoint = entryPoint;
Name = name;
Ordinal = ord;
ForwardName = forwardName;
}

#region Methods

public override string ToString()
{
string result;

if (string.IsNullOrWhiteSpace(ForwardName))
{
result = $"0x{EntryPoint:X8} {Ordinal:D4} {Name}";
}
else
{
result = $"0x{EntryPoint:X8} {Ordinal:D4} {Name} -> {ForwardName}";
}

return result;
}

#endregion

#region Properties

public uint EntryPoint { get; }
public string Name { get; }
public uint Ordinal { get; }
public string ForwardName { get; }

#endregion
}
}
218 changes: 218 additions & 0 deletions src/Workshell.PE/Content/Exports/ExportDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Workshell.PE.Annotations;
using Workshell.PE.Extensions;
using Workshell.PE.Native;

namespace Workshell.PE.Content
{
public sealed class ExportDirectory : DataContent
{
private readonly string _name;
private readonly uint[] _functionAddresses;
private readonly uint[] _functionNameAddresses;
private readonly ushort[] _functionOrdinals;

internal ExportDirectory(PortableExecutableImage image, DataDirectory dataDirectory, Location location, IMAGE_EXPORT_DIRECTORY directory,
string name, uint[] functionAddresses, uint[] functionNameAddresses, ushort[] functionOrdinals) : base(image, dataDirectory, location)
{
_name = name;
_functionAddresses = functionAddresses;
_functionNameAddresses = functionNameAddresses;
_functionOrdinals = functionOrdinals;

Characteristics = directory.Characteristics;
TimeDateStamp = directory.TimeDateStamp;
MajorVersion = directory.MajorVersion;
MinorVersion = directory.MinorVersion;
Name = directory.Name;
Base = directory.Base;
NumberOfFunctions = directory.NumberOfFunctions;
NumberOfNames = directory.NumberOfNames;
AddressOfFunctions = directory.AddressOfFunctions;
AddressOfNames = directory.AddressOfNames;
AddressOfNameOrdinals = directory.AddressOfNameOrdinals;
}

#region Static Methods

internal static async Task<ExportDirectory> LoadAsync(PortableExecutableImage image)
{
if (!image.NTHeaders.DataDirectories.Exists(DataDirectoryType.ExportTable))
return null;

var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.ExportTable];

if (DataDirectory.IsNullOrEmpty(dataDirectory))
return null;

try
{
var calc = image.GetCalculator();
var rva = dataDirectory.VirtualAddress;
var imageBase = image.NTHeaders.OptionalHeader.ImageBase;
var va = imageBase + rva;
var section = calc.RVAToSection(rva);
var offset = calc.RVAToOffset(section, rva);
var size = Marshal.SizeOf<IMAGE_EXPORT_DIRECTORY>();
var location = new Location(offset, rva, va, size.ToUInt32(), size.ToUInt32(), section);
var stream = image.GetStream();

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

var exportDirectory = await stream.ReadStructAsync<IMAGE_EXPORT_DIRECTORY>(size).ConfigureAwait(false);
var name = await LoadNameAsync(calc, stream, exportDirectory).ConfigureAwait(false);
var functionAddresses = await LoadFunctionAddressesAsync(calc, stream, exportDirectory).ConfigureAwait(false);
var functionNameAddresses = await LoadFunctionNameAddressesAsync(calc, stream, exportDirectory).ConfigureAwait(false);
var functionOrdinals = await LoadFunctionOrdinalsAsync(calc, stream, exportDirectory).ConfigureAwait(false);

return new ExportDirectory(image, dataDirectory, location, exportDirectory, name, functionAddresses, functionNameAddresses, functionOrdinals);
}
catch (Exception ex)
{
throw new PortableExecutableImageException(image, "Could not read export directory from stream.", ex);
}
}

private static async Task<string> LoadNameAsync(LocationCalculator calc, Stream stream, IMAGE_EXPORT_DIRECTORY directory)
{
var builder = new StringBuilder(256);
var offset = calc.RVAToOffset(directory.Name).ToInt64();

stream.Seek(offset, SeekOrigin.Begin);

while (true)
{
int value = await stream.ReadByteAsync().ConfigureAwait(false);

if (value <= 0)
break;

var c = (char)value;

builder.Append(c);
}

return builder.ToString();
}

private static async Task<uint[]> LoadFunctionAddressesAsync(LocationCalculator calc, Stream stream, IMAGE_EXPORT_DIRECTORY directory)
{
var offset = calc.RVAToOffset(directory.AddressOfFunctions).ToInt64();

stream.Seek(offset, SeekOrigin.Begin);

var results = new uint[directory.NumberOfFunctions];

for (var i = 0; i < directory.NumberOfFunctions; i++)
results[i] = await stream.ReadUInt32Async().ConfigureAwait(false);

return results;
}

private static async Task<uint[]> LoadFunctionNameAddressesAsync(LocationCalculator calc, Stream stream, IMAGE_EXPORT_DIRECTORY directory)
{
var offset = calc.RVAToOffset(directory.AddressOfNames).ToInt64();

stream.Seek(offset, SeekOrigin.Begin);

var results = new uint[directory.NumberOfNames];

for (var i = 0; i < directory.NumberOfNames; i++)
results[i] = await stream.ReadUInt32Async().ConfigureAwait(false);

return results;
}

private static async Task<ushort[]> LoadFunctionOrdinalsAsync(LocationCalculator calc, Stream stream, IMAGE_EXPORT_DIRECTORY directory)
{
var offset = calc.RVAToOffset(directory.AddressOfNameOrdinals).ToInt64();

stream.Seek(offset, SeekOrigin.Begin);

var results = new ushort[directory.NumberOfNames];

for (var i = 0; i < directory.NumberOfNames; i++)
results[i] = await stream.ReadUInt16Async().ConfigureAwait(false);

return results;
}

#endregion

#region Methods

public DateTime GetTimeDateStamp()
{
return Utils.ConvertTimeDateStamp(TimeDateStamp);
}

public Version GetVersion()
{
return new Version(MajorVersion, MinorVersion);
}

public string GetName()
{
return _name;
}

public uint[] GetFunctionAddresses()
{
return _functionAddresses;
}

public uint[] GetFunctionNameAddresses()
{
return _functionNameAddresses;
}

public ushort[] GetFunctionOrdinals()
{
return _functionOrdinals;
}

#endregion

#region Properties

[FieldAnnotation("Characteristics")]
public uint Characteristics { get; private set; }

[FieldAnnotation("Date/Time Stamp")]
public uint TimeDateStamp { get; private set; }

[FieldAnnotation("Major Version")]
public ushort MajorVersion { get; private set; }

[FieldAnnotation("Minor Version")]
public ushort MinorVersion { get; private set; }

[FieldAnnotation("Name")]
public uint Name { get; private set; }

[FieldAnnotation("Base")]
public uint Base { get; private set; }

[FieldAnnotation("Number of Functions")]
public uint NumberOfFunctions { get; private set; }

[FieldAnnotation("Number of Names")]
public uint NumberOfNames { get; private set; }

[FieldAnnotation("Address of Functions")]
public uint AddressOfFunctions { get; private set; }

[FieldAnnotation("Address of Names")]
public uint AddressOfNames { get; private set; }

[FieldAnnotation("Address of Name Ordinals")]
public uint AddressOfNameOrdinals { get; private set; }

#endregion
}
}
Loading

0 comments on commit 1fe51e4

Please sign in to comment.