3

I know that I can change the file a program writes to by interrupting the process in gdb, closing using the file descriptor and then re-opening with the file name I want. Is there a way to do the same thing at run time?

For example I know that the file I want to change uses file descriptor 5 so I tried

./myexe 5>/dev/null

But all that does is change things so the file of interest is on fd=6.

  • 2
    It's quite likely that the program in question doesn't explicitly open the FD at that time, it opens it dynamically after execution, and just picks the next free FD. – Chris Down Jun 22 '15 at 17:38
6

When a program opens a file, that file ends up on a file descriptor that's free at the time. By opening a file before the program starts, you're only making one more file descriptor busy, so the file you're interested in might end up on a different descriptor. If you want the program to open a different file, you'll need to modify the open operation when it takes place, or intervene afterwards.

One way to modify the operation is to wedge some code between the program and the system library, by preloading a small piece of code. This assumes that the program is a dynamically linked binary, or a script executed by a dynamically linked binary (i.e. it isn't statically linked). Write the following code to a file override_fopen.c:

#include <dlfcn.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef FROM
#error "Define FROM to the path to override in double quotes, e.g. -DFROM='\"/bad\"'"
#endif
#ifndef TO
#error "Define TO to the path to use instead in double quotes, e.g. -DFROM='\"/good\"'"
#endif
FILE *fopen(const char *path, const char *mode) {
    void *(*original_fopen)(const char *, const char *) = dlsym(RTLD_NEXT, "fopen");
    if (!strcmp(path, FROM)) {
        path = TO;
    }
    return original_fopen(path, mode);
}
int open(const char *path, int oflag, ...) {
    int (*original_open)(const char *, int, ...) = dlsym(RTLD_NEXT, "open");
    int ret;
    va_list args;
    if (!strcmp(path, FROM)) {
        path = TO;
    }
    va_start(args, oflag);
    if (oflag & O_CREAT) {
        ret = original_open(path, oflag, (mode_t)va_arg(args, mode_t));
    } else {
        ret = original_open(path, oflag);
    }
    va_end(args);
    return ret;
}

Compile with the following command (that's for Linux, other Unix variants may require different options). Note the quotes around the path you want to override.

gcc -DFROM='"/some/path"' -DTO='"/dev/null"' -D_GNU_SOURCE -O -Wall -fPIC -shared -o override_fopen.so override_fopen.c -ldl

Run the program as follows (on OSX, use DYLD_PRELOAD instead of LD_PRELOAD):

LD_PRELOAD=./override_fopen.so ./myexe

This only works if the program is calling the fopen or open library function. If it calls some other function, you'll need to override that one. You can use ltrace to see what library calls the program makes.

  • I'm unable to compile on Mint 18, using the exact code you provided. – Duncan X Simpson Aug 21 '16 at 3:29
  • pastebin.com/NmMBpSKg – Duncan X Simpson Aug 21 '16 at 3:32
  • @VirtualDXS I've replaced the code by working code. Note that the compilation command has changed as well. – Gilles Aug 21 '16 at 16:52
  • Questtion: What's the purpose of defining _GNU_SOURCE? – Duncan X Simpson Aug 21 '16 at 19:13
  • @VirtualDXS To get RTLD_NEXT (see man dlsym). – Gilles Aug 21 '16 at 19:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.