GetProcAddress API Alternative
In the past two days, I released examples about how to enumerate DLL export table through the PE Header.
We will see one concreate example of using the UntEnumDLLExport.pas library to dynamically load API without using the famous Windows API > GetProcAddress()
This technique is quite known and often used by some Malware, to mask which API's they are dynamically loading and avoid Antivirus detection.
To do so, we still need to use LoadLibrary() first to load a DLL in memory and retrieve it address, then iterate through loaded DLL export table and catch target function address.
Code (Require -> UntEnumDLLExport.pas)
// Jean-Pierre LESUEUR (@DarkCoderSc)
// ...
uses tlhelp32, Windows, SysUtils, UntEnumDLLExport;
// ...
{
Retrieve module full path from it handle (Returned by LoadLibrary()), we need
that information to parse it PE Header and retrieve function address.
}
function GetModuleImagePath(hModule : HMODULE) : String;
var ASnap : THandle;
AModuleEntry : TModuleEntry32;
const TH32CS_SNAPMODULE32 = $00000010;
begin
result := '';
///
ASnap := CreateToolHelp32Snapshot(TH32CS_SNAPMODULE or TH32CS_SNAPMODULE32, GetCurrentProcessId());
if ASnap = INVALID_HANDLE_VALUE then
Exit();
try
ZeroMemory(@AModuleEntry, SizeOf(TModuleEntry32));
AModuleEntry.dwSize := SizeOf(TModuleEntry32);
///
if NOT Module32First(ASnap, AModuleEntry) then
Exit();
if (AModuleEntry.hModule = hModule) then begin
result := AModuleEntry.szExePath;
Exit();
end;
while True do begin
ZeroMemory(@AModuleEntry, SizeOf(TModuleEntry32));
AModuleEntry.dwSize := SizeOf(TModuleEntry32);
///
if NOT Module32Next(ASnap, AModuleEntry) then
Break;
if (AModuleEntry.hModule = hModule) then begin
result := AModuleEntry.szExePath;
break;
end;
end;
finally
CloseHandle(ASnap);
end;
end;
{
Retrieve function address from DLL PE Header Export Function Table.
}
function GetProcAddress_ALT(hModule : HMODULE; lpProcName : LPCSTR) : Pointer;
var ADLLExport : TEnumDLLExport;
I : Integer;
begin
result := nil;
///
ADLLExport := TEnumDLLExport.Create(GetModuleImagePath(hModule));
if (ADLLExport.Enum > 0) then begin
for I := 0 to ADLLExport.Items.Count -1 do begin
if (ADLLExport.Items[i].Name.ToLower = String(lpProcName).ToLower) then begin
result := Pointer(hModule + ADLLExport.Items[i].RelativeAddr);
break;
end;
end;
end;
end;
Hello World MessageBox Example
//...
procedure LoadAndTriggerMessageBox();
var _MessageBoxW : function(hWnd: HWND; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall;
hModule : HMODULE;
begin
_MessageBoxW := nil;
hModule := LoadLibrary('user32.dll');
@_MessageBoxW := GetProcAddress_ALT(hModule, 'MessageBoxW');
if Assigned(_MessageBoxW) then
_MessageBoxW(0, 'Hello World', 'Hey', 0);
end;
begin
LoadAndTriggerMessageBox();
end.
//...
Written the Nov. 23, 2020, 10:12 a.m. by Jean-Pierre LESUEUR
Updated: 1 month, 4 weeks ago.