Home > Blockchain >  Is it possible to override C syscall open without LD_PRELOAD?
Is it possible to override C syscall open without LD_PRELOAD?

Time:10-10

The source gets printed, but no open: or open64: gets printed. How to fix this? Thanks!

/*
gcc -o emload emload.c -ldl
./emload
*/

// emload.c

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <stdarg.h>

typedef int   (*orig_open_func_type)(const char *__file, int flags, ...);
typedef int   (*orig_openat_func_type)(int dirfd, const char *__file, int flags, ...);


int open(const char *__file, int __oflag, ...)
{
    orig_open_func_type orig_func;
    orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open");
    int res = 0;

    if (__oflag & O_CREAT) {
        va_list ap;
        va_start(ap, __oflag);
        int mode = va_arg(ap, unsigned);
        res = orig_func(__file, __oflag, mode);
        va_end(ap);
    }
    else
        res = orig_func(__file, __oflag);

    printf("open: %d (%s)\n", res, __file);
    return res;
}

int open64(const char *__file, int __oflag, ...)
{
    orig_open_func_type orig_func;
    orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open64");
    int res = 0;

    if (__oflag & O_CREAT) {
        va_list ap;
        va_start(ap, __oflag);
        int mode = va_arg(ap, unsigned);
        res = orig_func(__file, __oflag, mode);
        va_end(ap);
    }
    else
        res = orig_func(__file, __oflag);

    printf("open64: %d (%s)\n", res, __file);
    return res;
}

int openat(int dirfd, const char *__file, int __oflag, ...)
{
    orig_openat_func_type orig_func = (orig_openat_func_type)dlsym(RTLD_NEXT, "openat");
    int res = 0;

    if (__oflag & O_CREAT) {
        va_list ap;
        va_start(ap, __oflag);
        int mode = va_arg(ap, unsigned);
        res = orig_func(dirfd, __file, __oflag, mode);
        va_end(ap);
    }
    else
        res = orig_func(dirfd, __file, __oflag);

    printf("openat: %d (%s)\n", res, __file);
    return res;
}

char source[2 << 20];

int main(int argc, char **argv, char **env)
{
    FILE* f = fopen("emload.c", "r");
    fread(source, sizeof(source), 1, f);
    puts(source);
    fclose(f);
    return 0;
}

CodePudding user response:

GLIBC's fopen calls the syscall wrapper directly, without any address resolution, so you're out of luck here. Your other options are:

  • Use ptrace(2) from a separate process, tracing syscalls via PTRACE_SYSCALL requests. This will stop on any syscall, not just the ones you need, so may potentially degrade performance.
  • Patch the wrapper in memory to a jump to your override. This may make it harder to execute the original code, though, because you'd need to analyze its first several instructions (that you overwrite) to reproduce their effect. Or, you can just reimplement the wrapper completely, avoiding the return to the original version.
  • Related