4

我正在编写一些 C 代码来挂钩加载到内存中的 .so ELF(共享库)的某些功能。

我的 C 代码应该能够重定向另一个加载到应用程序/程序内存中的 .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()没有做我认为它会做的事。

4

1 回答 1

7

您应该详细阅读 Drepper 的论文:如何编写共享库- 特别是要了解为什么使用LD_PRELOAD还不够。您可能想ld-linux.so研究libc. 您可以尝试使用mprotect(2)和/或mmap(2)和/或mremap(2) 更改相关页面。您可以使用&查询通过proc(5)的内存映射。然后,您可以以特定于体系结构方式,通过跳转到您的/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_funcoriginal_func你覆盖的部分。

如此可怕和耗时的 hack(你需要几周的时间才能让它工作)让我更喜欢使用免费软件(从那时起,修补共享库的源并重新编译它要简单得多)。

当然,这一切都不容易。您需要详细了解ELF共享对象是什么,另请参阅elf(5)并阅读Levine 的书:Linkers and Loaders


注意:请注意,如果您正在攻击专有库(例如unity3d),那么您试图实现的目标可能是非法的。问律师。从技术上讲,您违反了共享库提供的大多数抽象。如果可能的话,请共享库的作者提供帮助,并可能在其中实现一些插件机制。

于 2015-04-20T04:58:03.797 回答