之前一篇文章说明过 elf 当中的符号决议顺序,最先被解析的符号先入为主,作为最终被使用的符号,本篇的用到的代码同之前的一篇相同。
LD_PRELOAD
LD_PRELOAD 的原理就是在先于所有依赖的动态库,提前加载 LD_PRELOAD 环境变量当中指定的库。
main.out 依赖外部动态库 libmyprintf.so 的 myprintf 函数,依赖的动态库在编译时,被写入到可执行文件当中,可以使用
readelf -d
来查看。
加入此时另外有个动态库 libmsgprint.so 当中,也导出了函数 myprintf,可以使用 libmsgprint.so 中的 myprintf 来覆盖 libmsgprint.so 当中的 myprintf.
patchelf
相较于 LD_PRELOAD 的那种非侵入的方式,可以使用 patchelf 来直接修改可执行的 elf 二进制文件,将 libmsgprint.so 库也加入到可执行文件的依赖库列表当中,而且要在之前的 libmyprintf.so 函数之前。
hook
如果在先入为主的函数当中调用因为滞后被加载而被覆盖的符号,这样就能实现 hook 的效果。hook 这里使用 RTLD_NEXT 这个伪句柄,需要起始就定义 _GNU_SOURCE 宏,因为并没有被 SYSv3 标准定义。
//msgprint.c
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
void myprintf(char* msg)
{
printf("comes from libmsgprint.so %s\n", msg);
void (*out)(char* msg) = dlsym(RTLD_NEXT,"myprintf");
out(msg);
}