簡體   English   中英

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

[英]Is it possible to override main method using LD_PRELOAD?

這主要是出於好奇。 我知道如果我用我自己的庫函數定義 LD_PRELOAD 我自己的庫,則可以替換庫函數的定義(?)。 我可以對可執行文件的主要方法執行相同的操作嗎?

也就是說,在不重建可執行文件的情況下,我可以對運行時做些什么,以便調用不同的 main() 嗎?

不,您不能使用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;
}

put.c

#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()函數具有已知地址,而將從 glibc 中提取的puts()是未知的。

因此,我們可以強制運行時鏈接器使用我們的 puts 代替:

$ 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()是靜態鏈接的,這導致符號在(靜態)鏈接時被解析。

正如@FatalError 所說,您不能使用LD_PRELOADmain上直接攔截,因為此函數已鏈接到您的二進制文件中。 因此,這不是庫攔截的目標。

但是,您可以攔截調用 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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM