Code:
program ApiTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
WinAPI.Windows;
{----------------------------------------------------------------------------------------------------------------------}
{ in Windows.pas }
{function MessageBoxW(hWnd: HWND; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall; external user32 name 'MessageBoxW';}
var
MessageBoxW : FUNCTION (hWnd:HWND; lpText, lpCaption: LPCWSTR; uType: UINT) : Integer; STDCALL;
{----------------------------------------------------------------------------------------------------------------------}
// Direct test, vanuitgaande dat de functie bestaat;
function GetProcAddressFromDLL1 (aHandle: THandle; aFunctionName: PChar) : Pointer;
begin
result := nil;
if aHandle = 0 then exit;
if Trim(aFunctionName) = '' then exit;
result := GetProcAddress(aHandle, aFunctionName);
end;
{----------------------------------------------------------------------------------------------------------------------}
// Indirect test, checking if the requested FunctionName exists in the DLL.
// https://dev.to/wireless90/getting-the-windows-pe-internals-kka
// Information 32bit structure: https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
//
// RVA (relative virtual address)
// In an image file, the address of an item after it is loaded into memory, with the base address of the image file
// subtracted from it. The RVA of an item almost always differs from its position within the file on disk (file pointer).
// VA (virtual address)
// Same as RVA, except that the base address of the image file is not subtracted. The address is called a “VA” because
// Windows creates a distinct VA space for each process, independent of physical memory. For almost all purposes,
// a VA should be considered just an address
// Info opgezocht met PPEE (PPEE (puppy) is a Professional PE file Explorer) https://www.mzrst.com/
// File: user32.dll | 07-03-2022 19:14 | 1701896 bytes Windows 10 Pro/x64
// Base = 000005DE = 1502
// NumberOfFunctions = 000004BF = 1215
// NumberOfNames = 000003E6 = 998
// AddressOfFunctions = 0009D238 = 643640
// AddressOfNames = 0009E534 = 648500
// AddressNamesOfNamesOrdinals = 0009F4CC = 652492
// MessageBoxW | Ordinal $086C = 2156 | RVA = $00080EC0 = 528064 | NameRVA = $000A2B0D = 666381
function GetProcAddressFromDLL2 (aPEbase: HMODULE; aFunctionName: PChar) : Pointer;
Type
BytePtr = ^Byte;
TDWordArray = array[0..0] of DWord;
PDWordArray = ^TDWordArray;
var
ImageDosHeader : PImageDosHeader;
ImageNTHeaders : PImageNTHeaders;
ImageExportDirectory : PImageExportDirectory;
NameIndex : Cardinal;
Name : PChar;
begin
result := nil;
if aPEbase = 0 then exit;
// PEbase points to IMAGE_DOS_HEADER structure
ImageDosHeader := pointer(aPEbase);
if NOT assigned (ImageDosHeader) then
begin
WriteLn(format('ERR %-35s : $%p (%d)',['ImageDosHeader', ImageDosHeader, cardinal(ImageDosHeader)]));
end
else
begin
WriteLn(format('OK %-35s : $%p (%d)',['ImageDosHeader', ImageDosHeader, cardinal(ImageDosHeader)]));
//Checking for a valid 'MZ' signature
if NOT ImageDosHeader.e_magic = IMAGE_DOS_SIGNATURE then
begin
WriteLn(format('ERR %-35s : $%x (%d)',['ImageDosHeader MZ Signature', ImageDosHeader.e_magic, ImageDosHeader.e_magic]));
end
else
begin
WriteLn(format('OK %-35s : $%x (%d)',['ImageDosHeader MZ Signature', ImageDosHeader.e_magic, ImageDosHeader.e_magic]));
// Checking for a valid PE signature in the ImageDosHeader
// _lfanew contains a RVA whose offset leads to the PImageNTHeaders structure
// lfanew = long file address of the new executable header
// To compute the destination (RVA), take Base Address (PEbase) and use the lfnew as offset
ImageNTHeaders := Pointer(dword(aPEbase) + dword(ImageDosHeader._lfanew));
if NOT assigned(ImageNTHeaders) then
begin
WriteLn(format('ERR %-35s : $%p (%d)',['ImageNTHeaders', ImageNTHeaders, cardinal(ImageNTHeaders)]));
end
else
begin
WriteLn(format('OK %-35s : $%p (%d)',['ImageNTHeaders', ImageNTHeaders, cardinal(ImageNTHeaders)]));
//Checking for a valid 'PE' signature
if NOT ImageNTHeaders.Signature = IMAGE_NT_SIGNATURE then
begin
WriteLn(format('ERR %-35s : $%x (%d)',['ImageNTHeaders PE Signature', ImageNTHeaders.Signature, ImageNTHeaders.Signature]));
end
else
begin
WriteLn(format('OK %-35s : $%x (%d)',['ImageNTHeaders PE Signature', ImageNTHeaders.Signature, ImageNTHeaders.Signature]));
// Get RVA for the 'ExportTable Data Directory' from the OptionalHeader DataDirectory Virtual Adress. RVA = PEbase + Virtual Address
ImageExportDirectory := Pointer(ImageNTHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + cardinal(aPEbase));
if NOT assigned(ImageExportDirectory) then
begin
WriteLn(format('ERR %-35s : $%p (%d)',['ImageExportDirectory', ImageExportDirectory, cardinal(ImageExportDirectory)]));
end
else
begin
WriteLn(format('OK %-35s : $%p (%d)',['ImageExportDirectory', ImageExportDirectory, cardinal(ImageExportDirectory)]));
// Get ExportAddressTable, NameOrdinalsArray and NameAddressArray
// https://dev.to/wireless90/exploring-the-export-table-windows-pe-internals-4l47 (voorbeeld)
//
// int nameIndex = 0;
// for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
// {
// char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
// if (strcmp("MessageBoxA", name) == 0)
// {
// WORD ordinal = nameOrdinalsPointer[nameIndex];
// PDWORD targetFunctionAddress = (PDWORD)((unsigned char*)peBase + exportAddressTable[ordinal]);
// }
// }
NameIndex := 0;
repeat //
Name := PChar ( aPEbase + cardinal(ImageExportDirectory.AddressOfNames) + NameIndex); //<-------- ISSUE
//--> TODO if Name = aFunctionName then result := GetProcAddress(aPEbase, aFunctionName);
inc(NameIndex);
until NameIndex ImageExportDirectory.NumberOfNames;
end;
end;
end;
end;
end;
end;
{----------------------------------------------------------------------------------------------------------------------}
var
DLLHandle : THandle = 0;
begin
try
DLLHandle := LoadLibrary ('user32.dll');
try
{
// Werkt
MessageBoxW := nil;
MessageBoxW := GetProcAddressFromDLL1(DLLHandle,'MessageBoxW');
if assigned(MessageboxW) then MessageBoxW(0,'Test 1 - Direct','Direct API Call',0);
}
// Werkt NIET
MessageBoxW := nil;
MessageBoxW := GetProcAddressFromDLL2(DLLHandle,'MessageBoxW');
if assigned(MessageboxW) then MessageBoxW(0,'Test 2 - Indirect','Indirect API Call',0);
finally
FreeLibrary(DLLHandle);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
Bookmarks