用soname插入共享对象的一部分

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

这工作正常,但我很想知道是否有办法做出这些更改:

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

只有两个“解决scheme”,我已经能够拿出是丑陋的黑客:

  • LD_PRELOAD所有的程序,所有的时间,这似乎缓慢和脆弱; 要么
  • libfreetype.so.6.12.3复制到libxxxxtype.so.6.12.3 ; 然后
    • libxxxxtype.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

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

你可以尝试使用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的一些链接:

有pt_regs的uprobes handler 。 正如在上一个链接中所说:“ 因此,Uprobe实现了一个机制,通过这个机制,一个进程执行一个特定的指令位置就可以调用内核函数。 ”这表明uprobes可能会取代一些基于ptrace / gdb的解决方案。 所以有可能通过改变它的eip / rip(PC)寄存器来改变执行任何激活的uprobe的程序。

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

另一个解决方案是使用libfreetype自定义系统来覆盖lib,然后将未经修改的方法代理到真正的lib。

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

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

Google说,要改变Debian上的运行时加载路径,你必须简单地编辑/etc/ld.so.conf 。 在开始处添加/opt/myapp/lib64路径,以便首先进行检查。 现在任何搜索freetype的应用程序都应该加载你的lib,你可以通过ldd <path to app>来检查它。

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

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

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

  • 这不是脆弱的。 它以文档化的方式向运行时链接程序提供信息。 你什么都不做,假装不是什么东西。 你只是改变了函数名解析的偏好层次结构。

  • 这并不慢。 链接器必须做某事。 需要检查是否定义了LD_PRELOAD ,在任何情况下都是用户空间操作。 所以它会遵循这个路径,并在做一堆其他工作之前加载你的库。 如果时间在一般情况下甚至是可衡量的,我会感到惊讶。

我有两个担心,但他们是正交的技术。 代码实际上必须在所有情况下工作,并且您必须深入挖掘流程创建框架,以确保LD_PRELOAD确实在任何地方定义。 除此之外,ld.so会根据您的预期用途定义其环境变量。 谁来争辩?