【问题标题】:Why does LD_PRELOAD work with syscalls? ​为什么 LD_PRELOAD 与系统调用一起工作?​​​
【发布时间】:2020-06-12 11:05:28
【问题描述】:

LD_PRELOAD的思想是在原始共享库之前加载一个共享库,例如我可以编译mylib.so在libc.so之前加载它,所以当进程想要使用printf时它会在so 一个一个加载并在mylib.so 中找到它(因为首先加载了这个so)而不是libc.so,所以它使用mylib.so 中的printf 而不是@987654333 中的printf @。

我理解为什么这适用于在 so 中实现的函数,例如 libc.so 中的 printf。

但是当我想挂钩write 函数或另一个syscall 函数时,它为什么会起作用?进程不搜索so中的函数,直接进入内核。

  1. LD_PRELOAD 是否适用于静态编译的二进制文件?为什么? 在此重播中,https://stackoverflow.com/a/13866611 提到 LD PRELOAD 不适用于静态二进制

  2. 为什么LD_PRELOAD 在动态编译的二进制文件上工作以对系统调用进行挂钩?

架构是 ARM。

【问题讨论】:

  • 当您编码调用write 时,它不是直接调用系统调用。它正在调用一个设置系统调用然后陷入内核的包装器。所以 LD_PRELOAD 可以通过覆盖包装调用来工作。
  • @kaylum 你确定吗,write 是系统调用,没有包装,我可以在汇编中看到它。所以 LD_PLELOAD 也需要在静态二进制上工作??
  • 有一个名为 write 的系统调用,但这不是您直接调用的。 libc 中有系统调用包装器。预加载仅适用于动态链接的 exec。
  • 我已经调整了你的问题的措辞,让它更清楚,让我知道这样是否可以。
  • 简短回答:你被骗了。好的,这有点强,但是“系统调用”和“库函数”之间的区别是人为的,并不是很准确。它们实际上都是库函数,所谓的“系统调用”不再真正直接映射到实际的系统调用——实际的系统调用已经改变。例如,open() 可能会进行 openat() 系统调用。

标签: c linux arm system-calls ld-preload


【解决方案1】:

进程不搜索so中的函数,直接进入内核。

错了,您使用的系统调用函数(read()、write()、...)都是围绕真实系统调用的 libc 包装器,甚至是通用的 syscall() 函数。例如,Here's the code 用于 write() 函数。从程序直接进入内核的唯一方法是手动发出系统调用(见下文)。

LD_PRELOAD 是否适用于静态编译的二进制文件?为什么?

不,它没有。静态二进制文件不需要动态解析任何符号,因此不会调用动态加载器来解析通常的库函数符号。

为什么LD_PRELOAD 在动态编译的二进制文件上工作以对系统调用进行挂钩?

因为这些只是普通的 libc 函数,仅此而已。


除了静态编译之外,不通过 C 库(因此不进行符号解析)手动调用系统调用的唯一方法是使用inline assembly。您可以查看man 2 syscall 以查看要使用的寄存器和指令。例如,在 ARM AArch64 上,您可以通过加载寄存器 x8 中的系统调用号、x0 到 x5 中的参数来调用系统调用,然后执行 svc #0 指令。

【讨论】:

  • 所以如果在我的代码中我会直接用syscall 调用write,就像syscall(WRITE_SYSCALL,fd, buf,nbytes); 一样,即使它是动态编译的,nobady 也可以在这个过程中应用LD_PRELOAD 吗?我的解释写了吗? LD_PRELOAD 将我的 so 放在任何 so 之前,以便加载程序首先找到我的函数?
  • 没有。 syscall() 只是另一个 libc 包装器。正如我所说,这样做的唯一方法是(1)静态链接或(2)手动编写程序集。如果您不想在动态链接时挂钩库函数,那么不要使用库函数。
  • 谢谢 (1) 是我解释写的吗? LD_PRELOAD 将我的 so 放在任何 so 之前,所以加载程序首先找到我的函数,而不是原始函数? (2) 我可以在 /proc/pid 中看到 mylib.so 解析的特殊函数吗?
  • (1) 是的,没错。 (2) /proc/pid/* 并没有真正帮助你。如果您想查看函数是否通过mylib.so 解析,您应该使用调试信息(-g)进行编译,然后使用gdb 启动进程并执行next 直到调用您的函数。调用后,用p funcname检查它的地址,然后用info proc mappings检查地址是否在mylib.so里面。
  • 另一种绕过 LD_PRELOAD 拦截的方法是显式 dlopen 你想要的库,dlsym 到你想要的函数并直接调用它。
猜你喜欢
  • 2017-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-13
  • 2011-12-28
  • 2015-06-01
  • 1970-01-01
相关资源
最近更新 更多