Add PE Exports

Post date: Jun 29, 2010 2:48:30 AM

When the PE loader runs a program, it loads the associated DLLs into the process address space. It then extracts information about the import functions from the main program. It uses the information to search the DLLs for the addresses of the functions to be patched into the main program. The place in the DLLs where the PE loader looks for the addresses of the functions is the export table.

http://win32assembly.online.fr/pe-tut7.html

This unit, written by steve10120 of ic0de.org, enables you to add exports to a PE.

unit uAddExports;
interface
uses Windows;
type
  TRVAArray = array of DWORD;
  TStringArray = array of string;
  TExportRec = packed record
    szDLLName:  string;
    Entries:    TStringArray;
  end;
function AddPEExports(szFilePath:string; szDestFile:string; ExportsData:TExportRec; FunctionAddrs:TRVAArray):Boolean;
implementation
function Align(dwValue:DWORD; dwAlign:DWORD):DWORD;
begin
  if dwAlign <> 0 then
  begin
    if dwValue mod dwAlign <> 0 then
    begin
      Result := (dwValue + dwAlign) - (dwValue mod dwAlign);
      Exit;
    end;
  end;
  Result := dwValue;
end;
function GetStringSizes(Strings:TStringArray):DWORD;
var
  i:  DWORD;
begin
  Result := 0;
  for i := 0 to Length(Strings) - 1 do
    Inc(Result, Length(Strings[i]));
end;
function AddPEExports(szFilePath:string; szDestFile:string; ExportsData:TExportRec; FunctionAddrs:TRVAArray):Boolean;
var
  IDH:    TImageDosHeader;
  INH:    TImageNtHeaders;
  ISH:    TImageSectionHeader;
  IED:    TImageExportDirectory;
  dwBase: DWORD;
  pTemp:  Pointer;
  dwTemp: DWORD;
  dwPos:  DWORD;
  RVAs:   TRVAArray;
  i:      WORD;
  dwNumberOfEntries:  DWORD;
  dwSizeOfStrings:    DWORD;
  dwSizeOfData:       DWORD;
  dwActualSize:       DWORD;
  wTemp:              WORD;
  pNamesBuff:         Pointer;
  pNumbsBuff:         Pointer;
  pAddrsBuff:         Pointer;
  hFile:              DWORD;
  dwNull:             DWORD;
begin
  Result := FALSE;
  CopyFile(PChar(szFilePath), PChar(szDestFile), FALSE);
  hFile := CreateFile(PChar(szDestFile), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    SetFilePointer(hFile, 0, nil, FILE_BEGIN);
    ReadFile(hFile, IDH, 64, dwNull, nil);
    if IDH.e_magic = IMAGE_DOS_SIGNATURE then
    begin
      SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN);
      ReadFile(hFile, INH, 248, dwNull, nil);
      if INH.Signature = IMAGE_NT_SIGNATURE then
      begin
        SetFilePointer(hFile, IDH._lfanew + 248 + (INH.FileHeader.NumberOfSections - 1) * 40, nil, FILE_BEGIN);
        ReadFile(hFile, ISH, 40, dwNull, nil);
        dwBase := Align(ISH.VirtualAddress + ISH.Misc.VirtualSize, INH.OptionalHeader.FileAlignment);
        dwNumberOfEntries := Length(ExportsData.Entries);
        dwSizeOfStrings := GetStringSizes(ExportsData.Entries);
        dwActualSize := SizeOf(IED) + dwSizeOfStrings + (dwNumberOfEntries * 4) +
        (dwNumberOfEntries * 2) + (dwNumberOfEntries * 4) + Length(ExportsData.szDLLName);
        dwSizeOfData := Align(dwActualSize, INH.OptionalHeader.FileAlignment);
        ZeroMemory(@IED, SizeOf(IED));
        dwPos := 0;
        IED.Base := 1;
        IED.MinorVersion := 1;
        IED.MajorVersion := 0;
        GetMem(pTemp, dwSizeOfData);
        ZeroMemory(pTemp, dwSizeOfData);
        dwTemp := Length(ExportsData.szDLLName);
        CopyMemory(pTemp, @ExportsData.szDLLName[1], dwTemp);
        IED.Name := dwBase;
        Inc(dwBase, dwTemp);
        Inc(dwPos, dwTemp);
        SetLength(RVAs, dwNumberOfEntries);
        for i := 0 to Length(ExportsData.Entries) - 1 do
        begin
          dwTemp := Length(ExportsData.Entries[i]);
          CopyMemory(Pointer(DWORD(pTemp) + dwPos), @ExportsData.Entries[i][1], dwTemp);
          RVAs[i] := dwBase;
          Inc(dwBase, dwTemp);
          Inc(dwPos, dwTemp);
        end;
        IED.NumberOfNames := dwNumberOfEntries;
        IED.NumberOfFunctions := dwNumberOfEntries;
        IED.AddressOfNames := Pointer(dwBase);
        GetMem(pNamesBuff, 4 * dwNumberOfEntries);
        dwTemp := 0;
        for i := 0 to Length(ExportsData.Entries) - 1 do
        begin
          CopyMemory(Pointer(DWORD(pNamesBuff) + dwTemp), @RVAs[i], 4);
          Inc(dwTemp, 4);
        end;
        GetMem(pNumbsBuff, 2 * dwNumberOfEntries);
        dwTemp := 0;
        for i := 0 to Length(ExportsData.Entries) - 1 do
        begin
          wTemp := i;
          CopyMemory(Pointer(DWORD(pNumbsBuff) + dwTemp), @wTemp, 2);
          Inc(dwTemp, 2);
        end;
        GetMem(pAddrsBuff, 4 * dwNumberOfEntries);
        dwTemp := 0;
        for i := 0 to Length(ExportsData.Entries) - 1 do
        begin
          CopyMemory(Pointer(DWORD(pAddrsBuff) + dwTemp), @FunctionAddrs[i], 4);
          Inc(dwTemp, 4);
        end;
        CopyMemory(Pointer(DWORD(pTemp) + dwPos), pNamesBuff, 4 * dwNumberOfEntries);
        Inc(dwBase, 4 * dwNumberOfEntries);
        Inc(dwPos, 4 * dwNumberOfEntries);
        IED.AddressOfNameOrdinals := Pointer(dwBase);
        CopyMemory(Pointer(DWORD(pTemp) + dwPos), pNumbsBuff, 2 * dwNumberOfEntries);
        Inc(dwBase, 2 * dwNumberOfEntries);
        Inc(dwPos, 2 * dwNumberOfEntries);
        IED.AddressOfFunctions := Pointer(dwBase);
        CopyMemory(Pointer(DWORD(pTemp) + dwPos), pAddrsBuff, 4 * dwNumberOfEntries);
        Inc(dwBase, 4 * dwNumberOfEntries);
        Inc(dwPos, 4 * dwNumberOfEntries);
        CopyMemory(Pointer(DWORD(pTemp) + dwPos), @IED, SizeOf(IED));
        INH.OptionalHeader.DataDirectory[0].VirtualAddress := dwBase;
        INH.OptionalHeader.DataDirectory[0].Size := dwActualSize;
        SetFilePointer(hFile, 0, nil, FILE_END);
        WriteFile(hFile, pTemp^, dwSizeOfData, dwNull, nil);
        ISH.SizeOfRawData := Align(ISH.SizeOfRawData + dwSizeOfData, INH.OptionalHeader.FileAlignment);
        ISH.Misc.VirtualSize := Align(ISH.Misc.VirtualSize + dwSizeOfData, INH.OptionalHeader.SectionAlignment);
        INH.OptionalHeader.SizeOfImage := Align(ISH.VirtualAddress + ISH.Misc.VirtualSize, INH.OptionalHeader.SectionAlignment);
        SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN);
        WriteFile(hFile, INH, 248, dwNull, nil);
        SetFilePointer(hFile, IDH._lfanew + 248 + (INH.FileHeader.NumberOfSections - 1) * 40, nil, FILE_BEGIN);
        WriteFile(hFile, ISH, 40, dwNull, nil);
        FreeMem(pTemp);
        FreeMem(pNamesBuff);
        FreeMem(pNumbsBuff);
        FreeMem(pAddrsBuff);
        Result := TRUE;
      end;
    end;
    CloseHandle(hFile);
  end;
end;
end.