Usm*_*.3D 3 c linux shared-libraries elf symbol-table
我正在编写一些C代码,以挂接加载到内存中的.so ELF(共享库)的某些功能。
我的C代码应该能够重定向另一个已加载到应用程序/程序的内存中的.so库的导出函数。
这里有一些阐述:

Android应用将加载多个.so文件。我的C代码必须浏览属于另一个共享.so库(在本例中称为target.so)的导出功能
这不是常规的dlsym方法,因为我不仅想要函数的地址,而且想用自己的功能替换它;这样做的方式是:当另一个库调用其自己的函数时,将改为调用我的hook_func,然后从我的hook_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,以分配此struct / object和函数返回。现在,只要我可以访问该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");
Run Code Online (Sandbox Code Playgroud)
这是我得到的结果:
entry_addr = 0x75a28cfc
entry_addr_through_dlysm = 0x75a28cfc
符号名称为:AUDIO_System_Create
符号地址为:0x75a28cfc
这里通过dlysm获得的地址或通过ELF文件计算的地址是程序的地址;而我需要此地址本身所在的位置;这样我就可以用我的hook_func地址代替这个地址。dladdr()没有做我想做的事。
您应该详细阅读Drepper的论文:如何编写共享库 -尤其是要了解为什么使用LD_PRELOAD不足。您可能需要研究ld-linux.so内置的动态链接器()的源代码libc。您可以尝试使用mprotect(2)和/或mmap(2)和/或mremap(2)更改相关页面。您可以使用&通过proc(5)查询内存映射。然后,您可以采用特定于体系结构的方式,通过跳转到替换代码的起始字节(也许使用asmjit或GNU lightning)/proc/self/maps/proc/self/smapsoriginal_funchook_func函数(您可能需要更改其结尾,以将被覆盖的指令最初放置在original_func此处...)
如果original_func众所周知并且总是一样的话,事情可能会稍微容易一些。然后,您可以研究其源代码和汇编代码,并编写修补功能,并且hook_func仅此功能。
也许使用dladdr(3)也可能有帮助(但可能没有帮助)。
或者,修改您的动态链接器以根据需要进行更改。您可能会研究musl-libc的源代码
请注意,您可能需要覆盖地址为original_func (由dlsymon 给出"original_func")的机器代码。或者,您将需要在所有已加载的共享库中重新定位对该函数的每次调用(我相信这会更困难;如果您坚持要参见dl_iterate_phdr(3))。
如果您想要一个通用的解决方案(对于任意original_func),则需要实现一些二进制代码分析器(或反汇编程序)以对该函数进行修补。如果您只想破解某个特定文件original_func,则应将其拆解并修补其机器代码,然后hook_func执行original_func已被覆盖的部分。
如此恐怖且耗时的黑客攻击(您需要花费数周的时间才能使其工作),使我更喜欢使用自由软件(从那时起,修补共享库的源代码并重新编译要容易得多)。
当然,这并非易事。您需要详细了解什么是ELF共享对象,另请参阅elf(5)并阅读Levine的书:链接器和加载器
注意:当心,如果您要攻击专有库(例如unity3d),那么您试图实现的目标可能是非法的。问律师。从技术上讲,您违反了共享库提供的大多数抽象。如果可能的话,请共享库的作者提供帮助,并可能在其中实现一些插件机制。