8

我编写了一个共享对象,它修改了 FreeTypeFT_Load_GlyphFT_Render_Glyph函数的参数,目前通过将其插入LD_PRELOADdlsym

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

  • 给所有在给定主机上使用 FreeType 的程序(例如运行 Debian);
  • 不会破坏任何实际上未链接到 FreeType 的程序;
  • 无需简单地将 aLD_PRELOAD应用于主机上的所有程序;
  • 除非 FreeType 的 soname 改变,否则不需要任何维护;和
  • 无需修改任何 FreeType 的文件,也无需修改主机上任何程序的文件。

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

  • 所有的LD_PRELOAD程序,所有的时间,似乎缓慢而脆弱;或者
  • 复制例如libfreetype.so.6.12.3libxxxxtype.so.6.12.3;然后
    • 将 soname 修补libxxxxtype.so.6.12.3libxxxxtype.so.6;
    • 将插入的共享对象链接到libxxxxtype.so.6; 和
    • 将共享对象安装为例如libfreetype.so.6.999

我本质上想透明地修补共享对象中的几个函数,同时让其余函数通过,而不必访问共享对象的源或使用它的程序,但是如果我制作了一个假的共享对象使用 soname libfreetype.so.6,我看不到将其链接到(或dlopen)真实的干净方法libfreetype.so.6

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

4

3 回答 3

3

您可以尝试使用uprobes动态窃取某些功能的控制权吗?

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

uprobes:用户级动态跟踪,添加到 Linux 3.5 并在 Linux 3.14 中进行了改进。它使您可以跟踪用户级功能;例如,从所有正在运行的 bash shell 中返回 readline() 函数,并返回字符串:

# ./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

还有其他跟踪用户空间函数的解决方案,如 ftrace、systemtap、dtrace、lttng。其中一些需要在程序中重新编译和静态定义跟踪点;uprobes 是“用户级动态跟踪”。

关于uprobes的一些链接:

handler许多长袍pt_regs。正如在最后一个链接中所说:“ Uprobes 因此实现了一种机制,只要进程执行特定的指令位置,就可以调用内核函数。 ”它表明 uprobes 可能会取代一些基于 ptrace/gdb 的解决方案;因此,有可能通过更改其 eip/rip (PC) 寄存器来更改任何遇到活动 uprobe 的程序的执行。

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

于 2016-06-29T02:54:37.240 回答
2

一直到 LD_PRELOAD 所有程序,这似乎很慢而且很脆弱

这是一个很好的解决方案(对于您想要的)。我没有看到更好的。

  • 它并不脆弱。它以记录的方式向运行时链接器提供信息。你不会对任何事情发疯,假装某事不是它本来的样子。您只是在更改函数名称解析的首选项层次结构。

  • 这并不慢。链接器必须在某个时候做点什么。必须检查是否LD_PRELOAD已定义,这在任何情况下都是用户空间操作。所以它将遵循这条路径,并在执行大量其他工作之前加载您的库。如果在正常情况下时间甚至可以测量,我会感到惊讶。

我有两个担忧,但它们与技术正交。代码实际上必须在所有情况下都可以工作,并且您必须深入研究流程创建框架以确保LD_PRELOAD确实在任何地方都定义了。除此之外,ld.so 为您的预期用途精确定义其环境变量。谁来争辩?

于 2016-06-29T02:49:18.830 回答
2

另一种解决方案是使用自定义 libfreetype 为 lib 制作系统范围的“覆盖”,然后将未修改的方法代理到真正的 lib。

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

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

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

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

于 2016-06-28T10:02:46.390 回答