Today I was asked it it's possible to implement something similar to Linux's
LD_PRELOAD
for Windows, to allow override of particular symbol.
I answered yes, and here's how I did it (some dynamic code generation involved :)).#include<windows.h> #include<stdio.h> #include<stdlib.h>typedefunsignedchar* address;class Code { address buf; int size;int idx; enum reg { ax=0, cx, dx, bx, sp, bp, si, di };void init_sys();void generate(void* where, void* new_where);bool put_byte(unsignedchar b) {if (idx >= size) {return false; } buf[idx++] = b;return true; }bool put_int(int i) {if (idx >= size-4) {return false; } *(int*)(buf+idx) = i; idx +=4; return true; }void pop(reg r) { put_byte(0x58 | r); }void push(reg r) { put_byte(0x50 | r); }void add(reg r, int imm8) { put_byte(0x83); put_byte(0xc0 | r); put_byte(imm8 & 0xff); }void jmp(reg r) { put_byte(0xff); put_byte(0xe0 | r); }void jmp(address where) {int off = buf_rel(where); put_byte(0xe9); put_int(off); }void call(address where) {int off = buf_rel(where); put_byte(0xe8); put_int(off); }static address abs(address base, int off) {return base+off+5; }staticint rel(address dest, address cur) {return (int)(dest - cur - 5); }int buf_rel(address w) {return rel(w, buf+idx); }public: Code(void* sym, void* new_where); ~Code(); };voidCode::init_sys() { SYSTEM_INFO si; GetSystemInfo(&si); size = si.dwPageSize; }Code::Code(void* sym, void* new_where) : buf(NULL), size(-1), idx(0) { init_sys(); buf = (address) VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);// compute addressint off = *(int*)((address)sym+1); address real_sym = abs((address)sym, off); generate(real_sym, new_where); off = rel(buf, (address)sym);// and patch jumptable DWORD old = 0; VirtualProtect(sym, 5, PAGE_READWRITE, &old); *(int*)((address)sym+1) = off; VirtualProtect(sym, 5, old, &old); }Code::~Code() { VirtualFree(buf, size, MEM_RELEASE); buf = NULL; size = 0; }voidCode::generate(void* old, void* new_where) { call((address)new_where); jmp((address)old); FlushInstructionCache(GetCurrentProcess(), buf, size); }// must be void/void to not corrupt stackvoidmy_exit(void) { printf("custom exit\n"); getchar(); }intmain(int argc, char* argv[]) {// intentional leak, to avoid execution of code in unmapped areanew Code(&exit, &my_exit); printf("OK\n"); //exit(0);return 0; }