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

时间:2021-08-10 13:19:50

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?

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

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

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

1 个解决方案

#1


6  

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

不,您不能使用LD_PRELOAD来覆盖二进制文件的主函数。

   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:

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

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:

如果编译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:

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

$ 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()是静态链接的,这导致符号在(静态)链接时解析。

#1


6  

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

不,您不能使用LD_PRELOAD来覆盖二进制文件的主函数。

   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:

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

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:

如果编译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:

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

$ 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()是静态链接的,这导致符号在(静态)链接时解析。