我正在编写一些C代码来挂钩加载到内存中的.so ELF(共享库)函数。
我的C代码应该能够重定向加载到app / program内存中的另一个.so库的导出函数。
这里有一点阐述:
Android应用会加载多个.so文件。我的C代码必须查看属于另一个共享.so库的导出函数(在本例中称为target.so)
这不是一个常规的dlsym方法,因为我不只是想要一个函数的地址,但我想用我自己的函数替换它;在那个:当另一个库调用它自己的函数然后我的hook_func被调用,然后从我的hook_func我应该调用original_func。
对于导入功能,这可以工作。但对于出口功能,我不知道该怎么做。 导入函数使符号表中的条目在重定位表中具有相应的条目,最终给出全局偏移表(GOT)中的条目地址。 但是对于导出函数,符号的st_value元素本身具有过程的地址而不是GOT地址(如果我错了,请纠正我)。
如何为导出功能执行挂钩?
从理论上讲,我应该得到导出函数的动态符号表条目st_value
元素的Elf32_Sym
元素的内存位置。如果我得到那个位置,那么我应该能够用我的hook_func的地址替换该位置的值。但是,到目前为止我无法写入此位置。我必须假设动态符号表的内存是只读的。如果这是真的,那么那种情况下的解决方法是什么?
非常感谢阅读和帮助我。
更新: LD_PRELOAD只能用我自己的功能替换原来的功能,但是我不确定是否有办法调用原件。 以我为例:
应用通过调用Audio_System_Create
初始化音频引擎,并将AUDIO_SYSTEM
对象的引用传递给Audio_System_Create(AUDIO_SYSTEM **);
AUDIO API分配此结构/对象和函数返回。
现在,如果我只能访问该AUDIO_SYSTEM
对象,我会轻松地将回调附加到此对象并开始接收音频数据。
因此,我的最终目标是获得AUIOD_SYSTEM
对象的引用;在我的理解中,如果我拦截了首先通过Audio_System_Create(AUIOD_SYSTEM **)
分配该对象的调用,我只能得到它。
目前没有直接的方法来获取android的输出音频。 (所有示例都谈到录制仅来自麦克风的音频)
UPDATE2: 正如Basile在他的回答中所建议的,我使用了dladdr(),但奇怪的是它给了我与传递给它的地址相同的地址。
void *pFunc=procedure_addr; //procedure address calculated from the st_value of symbol from symbol table in ELF file (not from loaded file)
int nRet;
// Lookup the name of the function given the function pointer
if ((nRet = dladdr(pFunc, &DlInfo)) != 0)
{
LOGE("Symbol Name is: %s", DlInfo.dli_sname);
if(DlInfo.dli_saddr==NULL)
LOGE("Symbol Address is: NULL");
else
LOGE("Symbol Address is: 0x%x", DlInfo.dli_saddr);
}
else
LOGE("dladdr failed");
这是我得到的结果:
entry_addr = 0x75a28cfc
entry_addr_through_dlysm = 0x75a28cfc
符号名称为:AUDIO_System_Create
符号地址是:0x75a28cfc
这里通过dlysm获得的地址或通过ELF文件计算的地址是程序的地址;虽然我需要这个地址本身所在的位置;这样我就可以用hook_func
地址替换这个地址。 dladdr()
没有做我认为会做的事情。
答案 0 :(得分:5)
您应该详细阅读Drepper的论文:how to write shared libraries - 尤其要理解为什么使用LD_PRELOAD
是不够的。您可能想要在ld-linux.so
内研究动态链接器(libc
)的源代码。您可以尝试使用mprotect(2)和/或mmap(2)和/或mremap(2)相关网页进行更改。您可以使用/proc/self/maps
&来查询proc(5)的内存映射。 /proc/self/smaps
。然后,您可以以特定于体系结构的方式,通过跳转替换original_func
代码的起始字节(可能使用asmjit或GNU lightning)到你的hook_func
函数(你可能需要更改其结尾,将覆盖的指令放在original_func
- 那里......)
如果original_func
众所周知并且始终相同,那么事情可能会稍微容易一些。然后,您可以研究其源代码和汇编代码,并仅为其编写修补函数和hook_func
。
也许使用dladdr(3)也可能有帮助(但可能不是)。
或者,破解动态链接器以根据需要进行更改。您可以学习musl-libc
的源代码请注意,您可能需要<{>>覆盖original_func
地址的机器代码(由dlsym
上的"original_func"
指定)。或者,您需要relocate在所有已加载的共享对象中发生对该函数的每次调用(我相信它更难;如果您坚持看{4}})。
如果您需要通用解决方案(对于任意original_func
),您需要实现一些二进制代码分析器(或反汇编程序)来修补该功能。如果您只想破解特定的original_func
,则应将其反汇编,并修补其机器代码,并让hook_func
执行您已覆盖的original_func
部分。
这种可怕且耗时的黑客攻击(你需要几个星期才能使它工作)让我更喜欢使用dl_iterate_phdr(3)(从那以后,修补共享库的源代码并重新编译它会简单得多)。
当然,这一切都不容易。您需要详细了解free software共享对象的内容,另请参阅ELF和阅读elf(5)
注意:请注意,如果您正在攻击专有库(例如Levine's book: Linkers and Loaders),那么您要实现的目标可能是非法的。问律师。从技术上讲,您违反了共享库提供的大多数抽象。如果可能的话,请求共享库的作者提供帮助,并在其中实现一些插件机制。