using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace X86Disassembler.PE.Parsers
{
///
/// Parser for the Export Directory of a PE file
///
public class ExportDirectoryParser
{
private readonly PEUtility _utility;
public ExportDirectoryParser(PEUtility utility)
{
_utility = utility;
}
///
/// Parse the Export Directory from the binary reader
///
/// The binary reader
/// The RVA of the Export Directory
/// The parsed Export Directory
public ExportDirectory Parse(BinaryReader reader, uint rva)
{
ExportDirectory directory = new ExportDirectory();
reader.BaseStream.Seek(_utility.RvaToOffset(rva), SeekOrigin.Begin);
directory.Characteristics = reader.ReadUInt32();
directory.TimeDateStamp = reader.ReadUInt32();
directory.MajorVersion = reader.ReadUInt16();
directory.MinorVersion = reader.ReadUInt16();
directory.Name = reader.ReadUInt32();
directory.Base = reader.ReadUInt32();
directory.NumberOfFunctions = reader.ReadUInt32();
directory.NumberOfNames = reader.ReadUInt32();
directory.AddressOfFunctions = reader.ReadUInt32();
directory.AddressOfNames = reader.ReadUInt32();
directory.AddressOfNameOrdinals = reader.ReadUInt32();
// Read the DLL name
try
{
uint dllNameRVA = directory.Name;
uint dllNameOffset = _utility.RvaToOffset(dllNameRVA);
reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin);
// Read the null-terminated ASCII string
StringBuilder nameBuilder = new StringBuilder();
byte b;
while ((b = reader.ReadByte()) != 0)
{
nameBuilder.Append((char)b);
}
directory.DllName = nameBuilder.ToString();
}
catch (Exception)
{
directory.DllName = "Unknown";
}
return directory;
}
///
/// Parse the exported functions using the export directory information
///
/// The binary reader
/// The Export Directory
/// The RVA of the Export Directory
/// The size of the Export Directory
/// List of exported functions
public List ParseExportedFunctions(BinaryReader reader, ExportDirectory directory, uint exportDirRva, uint exportDirSize)
{
List exportedFunctions = new List();
if (directory == null)
{
return exportedFunctions;
}
// Read the array of function addresses (RVAs)
uint[] functionRVAs = new uint[directory.NumberOfFunctions];
reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfFunctions), SeekOrigin.Begin);
for (int i = 0; i < directory.NumberOfFunctions; i++)
{
functionRVAs[i] = reader.ReadUInt32();
}
// Read the array of name RVAs
uint[] nameRVAs = new uint[directory.NumberOfNames];
reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNames), SeekOrigin.Begin);
for (int i = 0; i < directory.NumberOfNames; i++)
{
nameRVAs[i] = reader.ReadUInt32();
}
// Read the array of name ordinals
ushort[] nameOrdinals = new ushort[directory.NumberOfNames];
reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNameOrdinals), SeekOrigin.Begin);
for (int i = 0; i < directory.NumberOfNames; i++)
{
nameOrdinals[i] = reader.ReadUInt16();
}
// Create a dictionary to map ordinals to names
Dictionary ordinalToName = new Dictionary();
for (int i = 0; i < directory.NumberOfNames; i++)
{
// Read the function name
reader.BaseStream.Seek(_utility.RvaToOffset(nameRVAs[i]), SeekOrigin.Begin);
List nameBytes = new List();
byte b;
while ((b = reader.ReadByte()) != 0)
{
nameBytes.Add(b);
}
string name = Encoding.ASCII.GetString(nameBytes.ToArray());
// Map the ordinal to the name
ordinalToName[nameOrdinals[i]] = name;
}
// Create the exported functions
for (ushort i = 0; i < directory.NumberOfFunctions; i++)
{
uint functionRVA = functionRVAs[i];
if (functionRVA == 0)
{
continue; // Skip empty entries
}
ExportedFunction function = new ExportedFunction();
function.Ordinal = (ushort)(i + directory.Base);
function.Address = functionRVA;
// Check if this function has a name
if (ordinalToName.TryGetValue(i, out string? name))
{
function.Name = name ?? $"Ordinal_{function.Ordinal}";
}
else
{
function.Name = $"Ordinal_{function.Ordinal}";
}
// Check if this is a forwarder
uint exportDirEnd = exportDirRva + exportDirSize;
if (functionRVA >= exportDirRva && functionRVA < exportDirEnd)
{
function.IsForwarder = true;
// Read the forwarder string
reader.BaseStream.Seek(_utility.RvaToOffset(functionRVA), SeekOrigin.Begin);
List forwarderBytes = new List();
byte b;
while ((b = reader.ReadByte()) != 0)
{
forwarderBytes.Add(b);
}
function.ForwarderName = Encoding.ASCII.GetString(forwarderBytes.ToArray());
}
exportedFunctions.Add(function);
}
return exportedFunctions;
}
}
}