简体   繁体   English

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

[英]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.我知道如果我用我自己的库函数定义 LD_PRELOAD 我自己的库,则可以替换库函数的定义(?)。 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?也就是说,在不重建可执行文件的情况下,我可以对运行时做些什么,以便调用不同的 main() 吗?

No, you cannot use LD_PRELOAD to override the main function of a binary.不,您不能使用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.

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. LD_PRELOAD 为您提供的是注入动态链接符号的能力,这样当运行时链接器去解析它们时,它会找到您的替换而不是它通常会找到的替换。 Let's take this example:让我们看这个例子:

main.c:主.c:

#include <stdio.h>

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

puts.c put.c

#include <stdio.h>

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

If compile main.c, check out its symbols:如果编译 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

Notice that the main() function is listed here with a known address, whereas puts() , which will be pulled from glibc, is unknown.请注意,此处列出的main()函数具有已知地址,而将从 glibc 中提取的puts()是未知的。

Thus, we can force the runtime linker to use our puts instead:因此,我们可以强制运行时链接器使用我们的 puts 代替:

$ 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.我们的覆盖不再起作用,因为puts()是静态链接的,这导致符号在(静态)链接时被解析。

As @FatalError said, you cannot intercept directly on main using LD_PRELOAD because this function is linked into your binary.正如@FatalError 所说,您不能使用LD_PRELOADmain上直接拦截,因为此函数已链接到您的二进制文件中。 That's thus not a target for library interception.因此,这不是库拦截的目标。

BUT, you can intercept on the libc's function that calls main instead.但是,您可以拦截调用 main 的 libc 函数。 Here is the trick, from https://gist.github.com/apsun/1e144bf7639b22ff0097171fa0f8c6b1 :这是诀窍,来自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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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