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

时间:2016-05-10 21:00:46

标签: linux shared-libraries function-interposition

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

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

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

我能够提出的唯一两个“解决方案”是难看的黑客:

  • LD_PRELOAD所有的节目,一直都是缓慢而脆弱的;或
  • 复制,例如libfreetype.so.6.12.3libxxxxtype.so.6.12.3;然后
    • libxxxxtype.so.6.12.3中的soname修补为libxxxxtype.so.6;
    • 将插入的共享对象与libxxxxtype.so.6相关联;和
    • 安装共享对象,例如libfreetype.so.6.999

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

这是我第一次使用共享库的真正实验,所以如果这个问题做出一些不正确的假设,或者没有任何意义,请耐心等待。

3 个答案:

答案 0 :(得分:3)

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

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

  

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

# ./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是“用户级动态追踪”。

关于衣服的一些链接:

handler个探测器有pt_regs。正如在上一个链接中所说:“ Uprobes因此实现了一种机制,通过该机制,只要进程执行特定的指令位置就可以调用内核函数。”并且它表明uprobes可以替换一些基于ptrace / gdb的解决方案;因此,有可能通过更改其eip / rip(PC)寄存器来更改任何触及主动uprobe的程序的执行。

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

答案 1 :(得分:2)

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

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

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

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

我可以想到这个解决方案不起作用的一个案例:如果app正在加载捆绑的libfreetype或者按完整路径加载它,而不是按名称加载。

答案 2 :(得分:2)

  

到LD_PRELOAD所有的程序,所有的时间,这似乎是缓慢而脆弱的

这是一个很好的解决方案(为了你想要的)。我看不到更好的一个。

  • 它并不脆弱。它以文档化的方式向运行时链接程序提供信息。你不会做任何事情,假装某事并不是什么。您只需更改功能名称解析的首选项层次结构。

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

我有两个问题,但他们与该技术正交。代码实际上必须适用于所有情况,并且您必须深入研究流程创建框架,以确保LD_PRELOAD真正定义无处不在。除此之外,ld.so还会根据您的预期用途定义其环境变量。谁来争辩?