简体   繁体   中英

Is it possible to override main method using LD_PRELOAD?

This is mostly out of curiosity. I understand that definitions for library functions can be replaced (?) if I LD_PRELOAD my own library with my own definition for the library function. Can I do the same for the main method of an executable?

That is, without rebuilding the executable, can I do something to the runtime so that a different main() is called?

No, you cannot use LD_PRELOAD to override the main function of a binary.

 LD_PRELOAD A whitespace-separated list of additional, user-specified, ELF shared libraries to be loaded before all others. This can be used to selectively override functions in other shared libraries. For setuid/setgid ELF binaries, only libraries in the standard search directories that are also setgid will be loaded.

What LD_PRELOAD gives you is the ability to inject symbols that are dynamically linked so that when the runtime linker goes to resolve them, it finds your replacement instead of the one it'd normally find. Let's take this example:

main.c:

#include <stdio.h>

int main (void)
{
  puts("Hello, world!");
  return 0;
}

puts.c

#include <stdio.h>

int puts (const char *s)
{
  return printf("Hijacked puts: %s\n", s);
}

If compile main.c, check out its symbols:

$ gcc -o main main.c
$ objdump -t main | grep 'main\|puts'
main:     file format elf64-x86-64
0000000000000000 l    df *ABS*  0000000000000000              main.c
0000000000000000       F *UND*  0000000000000000              puts@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              __libc_start_main@@GLIBC_2.2.5
00000000004004f4 g     F .text  0000000000000015              main

Notice that the main() function is listed here with a known address, whereas puts() , which will be pulled from glibc, is unknown.

Thus, we can force the runtime linker to use our puts instead:

$ gcc -o puts.so -shared -fPIC puts.c
$ LD_PRELOAD=./puts.so ./main
Hijacked puts: Hello, world!

In contrast, if we statically link our original binary:

$ gcc -o main -static main.c
$ objdump -t main | grep 'main\|puts'
main:     file format elf64-x86-64
00000000006c27c0 l     O .data  0000000000000888 main_arena
0000000000000000 l    df *ABS*  0000000000000000 main.c
00000000006c5580 l     O .bss   0000000000000008 _nl_loaded_domains
00000000004957d0 g     F __libc_freeres_fn  00000000000000d6 _nl_unload_domain
000000000041bcb0 g     F .text  000000000000170c _nl_load_domain
00000000006c60e0 g     O .bss   0000000000000008 _nl_domain_bindings
0000000000402050  w    F .text  0000000000000189 puts
...

$ LD_PRELOAD=./puts.so ./main
Hello, world!

Our override no longer worked because puts() was statically linked, which caused the symbol to be resolved at (static) link time.

As @FatalError said, you cannot intercept directly on main using LD_PRELOAD because this function is linked into your binary. That's thus not a target for library interception.

BUT, you can intercept on the libc's function that calls main instead. Here is the trick, from https://gist.github.com/apsun/1e144bf7639b22ff0097171fa0f8c6b1 :

/*
 * Hook main() using LD_PRELOAD, because why not?
 * Obviously, this code is not portable. Use at your own risk.
 *
 * Compile using 'gcc hax.c -o hax.so -fPIC -shared -ldl'
 * Then run your program as 'LD_PRELOAD=$PWD/hax.so ./a.out'
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

/* Trampoline for the real main() */
static int (*main_orig)(int, char **, char **);

/* Our fake main() that gets called by __libc_start_main() */
int main_hook(int argc, char **argv, char **envp)
{
    for (int i = 0; i < argc; ++i) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    printf("--- Before main ---\n");
    int ret = main_orig(argc, argv, envp);
    printf("--- After main ----\n");
    printf("main() returned %d\n", ret);
    return ret;
}

/*
 * Wrapper for __libc_start_main() that replaces the real main
 * function with our hooked version.
 */
int __libc_start_main(
    int (*main)(int, char **, char **),
    int argc,
    char **argv,
    int (*init)(int, char **, char **),
    void (*fini)(void),
    void (*rtld_fini)(void),
    void *stack_end)
{
    /* Save the real main function address */
    main_orig = main;

    /* Find the real __libc_start_main()... */
    typeof(&__libc_start_main) orig = dlsym(RTLD_NEXT, "__libc_start_main");

    /* ... and call it with our custom main function */
    return orig(main_hook, argc, argv, init, fini, rtld_fini, stack_end);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM