代码之家  ›  专栏  ›  技术社区  ›  AnkurVj

是否可以使用LD_PRELOAD覆盖主方法?

  •  5
  • AnkurVj  · 技术社区  · 10 年前

    这主要是出于好奇。我知道,如果我用自己的库函数定义LD_PRELOAD自己的库,库函数的定义可以被替换(?)。我可以对可执行文件的主方法执行同样的操作吗?

    也就是说,在不重建可执行文件的情况下,我可以对 运行时间 所以调用了不同的main()?

    2 回复  |  直到 10 年前
        1
  •  8
  •   FatalError    10 年前

    不,您不能使用 LD_PRELOAD 覆盖 main 二进制的函数。

       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.
    

    LD_PRELOAD为您提供的是注入动态链接的符号的能力,这样当运行时链接器去解析它们时,它就会找到您的替代品,而不是通常会找到的替代品。让我们举个例子:

    主.c:

    #include <stdio.h>
    
    int main (void)
    {
      puts("Hello, world!");
      return 0;
    }
    

    看跌期权

    #include <stdio.h>
    
    int puts (const char *s)
    {
      return printf("Hijacked puts: %s\n", s);
    }
    

    如果编译main.c,请检查其符号:

    $ 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
    

    注意 main() 此处列出了具有已知地址的函数,而 puts() ,将从glibc中删除,这是未知的。

    因此,我们可以强制运行时链接器使用我们的put:

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

    相反,如果我们静态链接原始二进制文件:

    $ 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!
    

    我们的超控不再有效,因为 puts() 被静态链接,这导致符号在(静态)链接时被解析。

        2
  •  0
  •   Martin Quinson vpram86    2 年前

    正如@FatalError所说,你不能在 main 使用 LD_PRELOAD 因为此函数链接到二进制文件中。因此,这不是图书馆拦截的目标。

    但是,您可以拦截调用main的libc函数。这是诀窍,来自 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);
    }