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.