挂钩并替换已导出的ELF中的导出功能(.so共享库)

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()没有做我想做的事。

Bas*_*tch 5

您应该详细阅读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),那么您试图实现的目标可能是非法的。问律师。从技术上讲,您违反了共享库提供的大多数抽象。如果可能的话,请共享库的作者提供帮助,并可能在其中实现一些插件机制。