Skip to content

Instantly share code, notes, and snippets.

@skeeto
Last active February 7, 2024 20:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skeeto/d019f8723c80fce3a411f701fdacd0d7 to your computer and use it in GitHub Desktop.
Save skeeto/d019f8723c80fce3a411f701fdacd0d7 to your computer and use it in GitHub Desktop.
Function hotpatch example
// Function hotpatch example
// $ gcc -nostartfiles -o hotpatch.exe hotpatch.c
// $ clang -nostdlib -Wl,/subsystem:console -o hotpatch.exe hotpatch.c -lkernel32
// This is free and unencumbered software released into the public domain.
#include <stddef.h>
#include <stdint.h>
#define W32 __attribute((dllimport, stdcall))
W32 void *CreateThread(void *, size_t, void *, void *, int, int *);
W32 int FlushInstructionCache(void *, void *, size_t);
W32 void *GetStdHandle(int);
W32 void Sleep(int);
W32 int VirtualProtect(uintptr_t, size_t, int, int *);
W32 int WriteFile(void *, void *, int, int *, void *);
// Does nothing, but supports hotpatching so that it can be replaced
// with another definition at run time.
__attribute((aligned(8), naked))
static void message(void)
{
// 8-byte nop; ret
asm (".byte 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0; ret");
}
static void hello(void)
{
void *stdout = GetStdHandle(-11);
WriteFile(stdout, "hello\n", 6, &(int){0}, 0);
}
static void goodbye(void)
{
void *stdout = GetStdHandle(-11);
WriteFile(stdout, "goodbye\n", 8, &(int){0}, 0);
}
__attribute((stdcall))
static void threadfunc(void *arg)
{
(void)arg;
for (;;) {
message();
Sleep(100);
}
}
void mainCRTStartup(void)
{
CreateThread(0, 0, threadfunc, 0, 0, 0);
// Allow modifying "message" function
enum { PAGE=4096, RWX=0x40 };
VirtualProtect((uintptr_t)message&-PAGE, PAGE, RWX, &(int){0});
uintptr_t funcs[] = {(uintptr_t)hello, (uintptr_t)goodbye};
for (unsigned i = 0;; i++) {
uintptr_t dest = funcs[i&1];
uintptr_t source = (uintptr_t)message;
uint64_t rel32 = dest - source - 5;
*(volatile uint64_t *)message = 0xe9 | rel32<<8; // jmp dest
FlushInstructionCache(0, message, 8); // just in case
Sleep(1000);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment