rendered paste body#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <dlfcn.h>#include <errno.h>#include <string.h>#include <interix/security.h>#include <stdarg.h>#ifndef FORK_RETRY#define FORK_RETRY 32#endif#ifndef LIBC_NAME#error Please define LIBC_NAME to the filename of your libc (RTLD_NEXT not working on interix somehow)#endif#ifndef INSTALL_LIBNAME#error Please define INSTALL_LIBNAME to the final destination filename of this preload library!#endif#ifdef DEBUG#define DBG fprintf#else#define DBG(...)#endifint __argc = 0;char ** __argv = NULL;extern char * const * environ;void* __get_libc() { static void* __libc = NULL; if(!__libc) { if(!(__libc = dlopen(LIBC_NAME, RTLD_LOCAL))) { perror("dlopen"); abort(); } } return __libc;}int _fork() { static int(*__fork)() = NULL; int rv = -1, i = 0; DBG(stderr, "ENTER: fork (%d)\n", getpid()); if(!__fork) { if(!(__fork = dlsym(__get_libc(), "fork"))) { perror("dlsym"); abort(); } } while(i <= FORK_RETRY) { rv = __fork(); if(rv >= 0 || errno != EAGAIN) { DBG(stderr, "LEAVE: fork (success) (%d)\n", getpid()); return rv; } ++i; fprintf(stderr, "!!! %d => fork: retrying (%d)\n", getpid(), i); } DBG(stderr, "LEAVE: fork (failed) (%d)\n", getpid()); return -1;}static char const * __lookup(char const * p) { char * path = getenv("PATH"); char * start = path, * end = path; char buf[260]; DBG(stderr, "ENTER: lookup (%d)\n", getpid()); if(!path) { DBG(stderr, "LEAVE: lookup (PATH empty) (%d)\n", getpid()); return p; } while(end && *end != '\0') { int sep = 0; while(*end != ':' && *end != '\0') ++end; if(*end == ':') { *end = '\0'; sep = 1; } if(strlen(start) > 260) { fprintf(stderr, "!!! ERROR: PATH contains a path that is longer than 260 chars!\n"); abort(); } if(strlen(start) + strlen(p) + 2 > 260) { fprintf(stderr, "!!! WARNING: PATH component + lookup component is longer than 260 chars, skipping\n"); goto docont; } strcpy(buf, start); strcat(buf, "/"); strcat(buf, p); DBG(stderr, "STATUS: checking X_OK on %s (%d)\n", buf, getpid()); if(access(buf, X_OK) == 0) { DBG(stderr, "LEAVE: lookup (found %s) (%d)\n", buf, getpid()); return strdup(buf); } docont: if(sep) *end = ':'; else break; start = ++end; } DBG(stderr, "LEAVE: lookup (unsure, using %s) (%d)\n", p, getpid()); return p;}int _execve(const char * p, char * const a[], char * const e[]) { static int(*__execve)(const char *, char * const[], char * const[]) = NULL; char * const * pe = e; char ** ppe = NULL; int cnt = 0; int alloced = 0; int status = -1; DBG(stderr, "ENTER: execve (%d)\n", getpid()); if(!__execve) { if(!(__execve = dlsym(__get_libc(), "execve"))) { perror("dlsym"); abort(); } } while(pe && *pe) { ++cnt; if(strstr(*pe, "LD_PRELOAD") && strstr(*pe, INSTALL_LIBNAME)) { ppe = (char **)e; goto doit; } ++pe; } fprintf(stderr, "!!! %d => execve: re-adding fork preload!\n", getpid()); ppe = malloc(sizeof(char const *) * cnt + 2); alloced = 1; pe = e; cnt = 0; while(pe && *pe) { ppe[cnt++] = *pe++; } ppe[cnt++] = "LD_PRELOAD=" INSTALL_LIBNAME; ppe[cnt] = NULL;doit: DBG(stderr, "STATUS: execve (replacing process) (%d)\n", getpid()); status = __execve(p, a, ppe); if(alloced) free(ppe); DBG(stderr, "LEAVE: execve (failed) (%d)\n", getpid()); return status;}int _execv(const char* p, char * const a[]) { int status; DBG(stderr, "ENTER: execv (%d)\n", getpid()); status = _execve(p, a, environ); DBG(stderr, "LEAVE: execv (failed) (%d)\n", getpid()); return status;}int _execvp(const char* p, char* const a[]) { char const * f; int status; DBG(stderr, "ENTER: execvp (%d)\n", getpid()); f = __lookup(p); status = _execve(f, a, environ); if(f != p) free((char*)f); DBG(stderr, "LEAVE: execvp (failed) (%d)\n", getpid()); return status;}#define EXECL_TO_V \ va_list l; \ char * tmp[4096]; \ int cnt = 0; \ va_start(l, p); \ while((tmp[cnt++] = va_arg(l, char* const)) != NULL) {} \ tmp[cnt] = NULL; \int _execle(const char* p, ...) { int status; EXECL_TO_V; DBG(stderr, "ENTER: execle (%d)\n", getpid()); status = _execve(p, tmp, va_arg(l, char * const *)); va_end(l); DBG(stderr, "LEAVE: execle (failed) (%d)\n", getpid()); return status;}int _execl(const char* p, ...) { int status; EXECL_TO_V; DBG(stderr, "ENTER: execle (%d)\n", getpid()); status = _execv(p, tmp); va_end(l); DBG(stderr, "LEAVE: execl (failed) (%d)\n", getpid()); return status;}int _execlp(const char* p, ...) { int status; EXECL_TO_V; DBG(stderr, "ENTER: execlp (%d)\n", getpid()); status = _execvp(p, tmp); va_end(l); DBG(stderr, "LEAVE: execlp (failed) (%d)\n", getpid()); return status;}/* * vfork() would require some magic. we would have to save the * return address of this vfork() replacement in some means of * global storage (not stack!) and make the parent jump to that * address after the real call to vfork immediately! * * Below is a first go at it, but things aren't quite right yet: * vfork works, and both parent and child return properly - *if* * the parent isn't too fast. seems the parent isn't blocked until * the child does an exec* call (as described in the linux man page). * This leads to a crash if the parent continues immediately after * vfork. if you delay the program using a debugger, things work. * However in my test program the exit code of the program was some * seemingly random value, even though everything seemed ok... * Maybe someone has ideas? *int _vfork () { static void* __libc = NULL; static int(*__vfork)() = NULL; int rv = -1, i = 0; int ret_addr; if(!__libc) { if(!(__libc = dlopen("libc.so.5.2", RTLD_LOCAL))) { perror("dlopen"); abort(); } } if(!__vfork) { if(!(__vfork = dlsym(__libc, "vfork"))) { perror("dlsym"); abort(); } } asm ( "movl 0x4(%%ebp), %0": "=b" (ret_addr) ); while(i <= FORK_RETRY) { rv = __vfork(); if(rv >= 0 || errno != EAGAIN) { switch(rv) { case -1: return -1; case 0: return 0; default: // need to jump directly to return address, since // child took care of cleaning up the shared stack asm ( "jmp *%0": : "g" (ret_addr) ); } } ++i; fprintf(stderr, "!!! %d => vfork: retrying (%d)\n", getpid(), i); } return -1;} */