0

I'm curious if there are methods to run code after the main() program has closed?

So far, I've found atexit()

int atexit( void (*func)(void) );

Registers the function pointed to by func to be called on normal program termination (via exit() or returning from main()). The functions will be called in reverse order they were registered, i.e. the function registered last will be executed first.

For example, Chrome browser with pid 358440 calls the injected atexit().
Closing the browser normally launches the Windows calculator. Perhaps there are less intrusive ways?

fun

Bunny.c

// Windows x86-64: Create a remote thread to run atexit().
// Configuration:  MSVC 2019 Debug x64.

#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
#include <intrin.h>

typedef uint64_t QWORD;
typedef void (*funcPtr)(void);
typedef void (*AtexitPtr)(funcPtr);

// ------------------------------------------------------------
#define kExitFunctionAddress_offset 16
#define kWinExecAddress_offset      30
#define kAtExitAddress_offset       44
#define kRunAtexit_offset           58
// ------------------------------------------------------------


DWORD WINAPI start_of_function_code(void)
{
    // ------------------------------------------------------------
    QWORD kExitFunctionAddress = 0xDEADBEEFBAADF00D;
    QWORD kWinExecAddress      = 0xDEADBEEFBAADF00D;
    QWORD kAtExitAddress       = 0xDEADBEEFBAADF00D;
    QWORD kRunAtexit           = 0xDEADBEEFBAADF00D;
    // ------------------------------------------------------------

    if (kRunAtexit)
    {
        AtexitPtr atexit_ptr = (AtexitPtr)kAtExitAddress;
        atexit_ptr((funcPtr)kExitFunctionAddress);
    }
    else
    {
        const char kCalculator[] = { 'c','a','l','c','.','e','x','e','\0' };
        ((FARPROC(WINAPI*)(LPCSTR, UINT))(kWinExecAddress))(kCalculator, SW_SHOW);
    }

    return 0;
}


int main(int argc, char* argv[])
{
    DWORD                  targetPID;
    HANDLE                 hTargetProc;
    LPTHREAD_START_ROUTINE pfnThreadRoutine;
    HANDLE                 hRemoteThread;
    DWORD                  sizeoffunccode;
    QWORD                  onoff = 0;
    BYTE                   RETURN_OPCODE = 0xC3;

    // Get the target PID
    if (argc < 2) {
        printf("Usage: %s <target PID>\n", argv[0]);
        return 1;
    }

    targetPID = atoi(argv[1]);

    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, targetPID);
    if (hProcess == NULL)
    {
        printf("[+] Invalid PID %d\n", targetPID);
        exit(1);
    }
    CloseHandle(hProcess);

    // Get Function Code Size
    BYTE* end_of_function_code = (BYTE*)start_of_function_code;
    while (*end_of_function_code++ != RETURN_OPCODE) {};
    sizeoffunccode = (DWORD)(end_of_function_code - (BYTE*)start_of_function_code);
    printf("[+] Function Size %d bytes\n", sizeoffunccode);

    // Get function addresses for Winexec() and atexit()
    HMODULE kernel32 = LoadLibrary("Kernel32.dll");
    FARPROC winexec  = GetProcAddress(kernel32, "WinExec");
    printf("[+] Winexec() Address %p\n", winexec);
    HMODULE msvcrt   = LoadLibrary("msvcrt.dll");
    FARPROC atexit   = GetProcAddress(msvcrt, "atexit");
    printf("[+] atexit() Address %p\n", atexit);

    // Write the thread function to the target process
    hTargetProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
    pfnThreadRoutine = (LPTHREAD_START_ROUTINE)VirtualAllocEx(hTargetProc, NULL, sizeoffunccode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hTargetProc, pfnThreadRoutine, (BYTE*)start_of_function_code, sizeoffunccode, NULL);
    WriteProcessMemory(hTargetProc, (BYTE*)pfnThreadRoutine + kExitFunctionAddress_offset, &pfnThreadRoutine, sizeof(pfnThreadRoutine), NULL);
    WriteProcessMemory(hTargetProc, (BYTE*)pfnThreadRoutine + kWinExecAddress_offset, &winexec, sizeof(winexec), NULL);
    WriteProcessMemory(hTargetProc, (BYTE*)pfnThreadRoutine + kAtExitAddress_offset, &atexit, sizeof(atexit), NULL);
    printf("[+] Function Address %p (PID: %d)\n", pfnThreadRoutine, targetPID);

    // Create the remote thread and call atexit()
    hRemoteThread = CreateRemoteThread(hTargetProc, NULL, 0, pfnThreadRoutine, NULL, 0, NULL);
    WaitForSingleObject(hRemoteThread, INFINITE);
    printf("[+] atexit() Executed\n");
    
    // Toggle condition so WinExec() is called upon process exit
    WriteProcessMemory(hTargetProc, (BYTE*)pfnThreadRoutine + kRunAtexit_offset, &onoff, sizeof(onoff), NULL);
    printf("[+] WinExec() Primed\n");
    
    // Cleanup
    CloseHandle(hTargetProc);
    CloseHandle(hRemoteThread);

    printf("[+] Done.\n");

    return 0;
}
vengy
  • 1,548
  • 10
  • 18

0 Answers0