我写了一个共享对象,修改FreeType的FT_Load_Glyph
和FT_Render_Glyph
函数的参数,目前通过插入LD_PRELOAD
和dlsym
。
这工作正常,但我很想知道是否有办法做出这些更改:
LD_PRELOAD
应用于主机上的所有程序; 只有两个“解决scheme”,我已经能够拿出是丑陋的黑客:
LD_PRELOAD
所有的程序,所有的时间,这似乎缓慢和脆弱; 要么 libfreetype.so.6.12.3
复制到libxxxxtype.so.6.12.3
; 然后
libxxxxtype.so.6.12.3
的soname libxxxxtype.so.6.12.3
到libxxxxtype.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的程序。
您可以尝试一些其他动态检测工具,如pin
或dyninst
; 但它们是针对每个进程的使用而设计的。
另一个解决方案是使用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会根据您的预期用途定义其环境变量。 谁来争辩?