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

Ank*_*rVj 3 c++ linux gnu elf dynamic-linking

这主要是出于好奇.我理解库函数的定义可以替换(?)如果我LD_PRELOAD我自己的库与我自己的库函数定义.我可以对可执行文件的主要方法执行相同的操作吗?

也就是说,如果不重建可执行文件,我可以对运行时执行某些操作,以便调用不同的main()吗?

Fat*_*ror 6

不,你不能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.
Run Code Online (Sandbox Code Playgroud)

LD_PRELOAD为您提供的是能够注入动态链接的符号,以便在运行时链接程序解析它们时,它会找到您的替换而不是它通常找到的替换.我们来看这个例子:

main.c中:

#include <stdio.h>

int main (void)
{
  puts("Hello, world!");
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

puts.c

#include <stdio.h>

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

如果编译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
Run Code Online (Sandbox Code Playgroud)

请注意,main()此处列出的函数具有已知地址,而puts()将从glibc中提取的函数未知.

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

$ gcc -o puts.so -shared -fPIC puts.c
$ LD_PRELOAD=./puts.so ./main
Hijacked puts: Hello, world!
Run Code Online (Sandbox Code Playgroud)

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

$ 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!
Run Code Online (Sandbox Code Playgroud)

我们的覆盖不再有效,因为它puts()是静态链接的,这导致符号在(静态)链接时解析.