通过 soname 插入共享对象的一部分

Interposing part of a shared object by soname

我已经编写了一个共享对象来修改 FreeType 的 FT_Load_GlyphFT_Render_Glyph 函数的参数,目前通过将其插入 LD_PRELOADdlsym

这很好用,但我很想知道是否有办法进行这些更改:

我能想出的唯一两个“解决方案”是丑陋的黑客:

我基本上想透明地修补共享对象中的几个函数,同时让其余函数通过,而不必访问共享对象的源代码或使用它的程序,但是如果我用 soname libfreetype.so.6 创建一个假的共享对象,我看不到一个干净的方法来 link 它到(或 dlopen)真正的 libfreetype.so.6.

这是我第一次使用共享库进行真正的实验,所以如果这个问题做出了一些不正确的假设,或者根本没有意义,请多多包涵。

另一种解决方案是为 lib 创建系统范围 "overlay",使用自定义 libfreetype,然后将未修改的方法代理到真正的 lib。

您必须使自定义库与真实库兼容。您可以通过使用 dlopen 绝对路径 (例如 dlopen("/usr/lib64/libfreetype.so.6")), 复制真实的导出函数的定义并使用 dlsym 代理它们。 它认为为了便于维护,您可以将代理参数类型替换为简单的 void*。您只需要在 freetype 函数更改(参数计数、函数名称)时进行更改。

要创建库 "overlay",您可以将自定义库安装到例如。 “/opt/myapp/lib64/libfreetype.so.6”,然后将此路径添加到动态链接器 运行 时间路径。 如果原始实现发生变化,您可能必须为其他版本创建符号链接或编译新的自定义库。隐藏真实库并保持其他应用程序正常运行所需的一切:)

Google 表示要在 Debian 上更改 运行 时间加载路径,您只需编辑 /etc/ld.so.conf。在开头添加 /opt/myapp/lib64 路径,以便首先检查它。 现在任何搜索 freetype 的应用程序都应该加载您的库,您可以使用 ldd <path to app>.

检查它

我能想到只有一种情况下此解决方案不起作用:如果应用正在加载捆绑的 libfreetype 或通过完整路径而不是名称加载它。

to LD_PRELOAD all programs, all of the time, which seems slow and fragile

这是一个很好的解决方案(满足您的需求)。我没有看到更好的。

  • 它并不脆弱。它以记录的方式向运行时链接程序提供信息。你不是在胡思乱想,假装事情不是它的本来面目。您只是在更改函数名称解析的首选项层次结构。

  • 不慢。链接器有时必须做一些事情。它必须检查是否定义了 LD_PRELOAD,这在任何情况下都是 user-space 操作。因此它将遵循该路径,并在执行大量其他工作之前加载您的库。如果在正常情况下甚至可以测量时间,我会感到惊讶。

我有两个顾虑,但它们与技术正交。该代码实际上必须在所有情况下都能正常工作,并且您必须稍微深入研究流程创建框架以确保 LD_PRELOAD 确实在 无处不在 中定义。除此之外,ld.so 为您的预期用途精确定义了它的环境变量。谁来争论?

您可以尝试使用 uprobes 从某些函数中动态窃取控制权吗?

勾选http://www.brendangregg.com/blog/2015-06-28/linux-ftrace-uprobe.html

uprobes: user-level dynamic tracing, which was added to Linux 3.5 and improved in Linux 3.14. It lets you trace user-level functions; for example, the return of the readline() function from all running bash shells, with the returned string:

# ./uprobe 'r:bash:readline +0($retval):string'
Tracing uprobe readline (r:readline /bin/bash:0x8db60 +0($retval):string). Ctrl-C to end.
 bash-11886 [003] d... 19601837.001935: readline: (0x41e876 <- 0x48db60) arg1="ls -l"
 bash-11886 [002] d... 19601851.008409: readline: (0x41e876 <- 0x48db60) arg1="echo "hello world""
 bash-11886 [002] d... 19601854.099730: readline: (0x41e876 <- 0x48db60) arg1="df -h"
 bash-11886 [002] d... 19601858.805740: readline: (0x41e876 <- 0x48db60) arg1="cd .."
 bash-11886 [003] d... 19601898.378753: readline: (0x41e876 <- 0x48db60) arg1="foo bar"
^C
Ending tracing...

http://www.brendangregg.com/blog/2015-07-03/hacking-linux-usdt-ftrace.html

还有其他跟踪用户功能的解决方案-space,如 ftrace、systemtap、dtrace、lttng。其中一些需要重新编译并在程序中静态定义跟踪点;和 uprobes 是 "user-level dynamic tracing".

关于 Uprobes 的一些 links:

handler 个 uprobe,其中 pt_regs。正如最后 link 所说:“Uprobes 因此实现了一种机制,只要进程执行特定指令位置,就可以通过该机制调用内核函数。”,它表明 uprobes 可能替换一些基于 ptrace/gdb 的解决方案;所以有可能通过改变它的 eip/rip (PC) register.

来改变任何程序的执行。

您可以尝试其他一些动态检测工具,例如 pindyninst;但它们是为每个进程使用而设计的。