加载的ELF中的挂钩和替换导出功能(.so共享库)

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

我的C代码应该能够重定向加载到app / program内存中的另一个.so库的导出函数.

这里有一点阐述:

Android应用将加载多个.so文件.我的C代码必须查看属于另一个共享.so库的导出函数(在本例中称为target.so)

这不是一个常规的dlsym方法,因为我不只是想要一个函数的地址,但我想用我自己的函数替换它;在那个:当另一个库调用它自己的函数然后我的hook_func被调用,然后从我的hook_func我应该调用original_func.

对于导入功能,这可以工作.但对于出口功能,我不知道该怎么做.
导入函数使符号表中的条目在重定位表中具有相应的条目,最终给出全局偏移表(GOT)中的条目地址.
但是对于导出函数,符号的st_value元素本身具有过程的地址而不是GOT地址(如果我错了,请纠正我).

如何执行导出功能的挂钩?

从理论上讲,我应该得到导出函数的动态符号表条目(Elf32_Sym)的st_value元素的内存位置.如果我得到那个位置,那么我应该能够用我的hook_func的地址替换该位置的值.但是,到目前为止我无法写入此位置.我必须假设动态符号表的内存是只读的.如果这是真的那么那个案例的解决方法是什么?

非常感谢阅读和帮助我.

更新:LD_PRELOAD只能用我自己的函数替换原来的函数,但后来我不确定是否有办法调用原件.
在我的例子中:

App通过调用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()没有做我认为会做的事情.

解决方法:

你应该详细阅读Drepper的论文:how to write shared libraries – 特别要理解为什么使用LD_PRELOAD还不够.您可能想要在libc中研究动态链接器(ld-linux.so)的源代码.您可以尝试使用mprotect(2)和/或mmap(2)和/或mremap(2)更改相关页面.您可以使用/ proc / self / maps& amp;查询内存映射到proc(5).的/ proc /自/ smaps.然后,您可以以特定于体系结构的方式,通过跳转到您的hook_func函数(您可能需要更改其结尾,放置覆盖的指令)来替换original_func的代码的起始字节(可能使用asmjit或GNU lightning). -originally在original_func-那里…)

如果original_func众所周知并且始终相同,那么事情可能会稍微容易一些.然后,您可以研究其源代码和汇编代码,并仅为其编写修补函数和hook_func.

也许使用dladdr(3)可能也有帮助(但可能不是).

或者,破解动态链接器以根据需要进行更改.您可以学习musl-libc的源代码

请注意,您可能需要在original_func的地址处覆盖机器代码(由dlsym在“original_func”上给出).或者,你需要在所有已经加载的共享对象中每次出现对该函数的调用(我相信它更难;如果你坚持看到dl_iterate_phdr(3)).

如果你想要一个通用的解决方案(对于任意的original_func),你需要实现一些二进制代码分析器(或反汇编程序)来修补该函数.如果你只想破解一个特定的original_func,你应该反汇编它,并修补它的机器代码,让你的hook_func执行你已经覆盖的original_func的一部分.

这种可怕且耗时的黑客攻击(你需要几个星期才能使它工作)让我更喜欢使用free software(从那时起,修补共享库的源代码并重新编译它就简单得多).

当然,这一切并不容易.您需要详细了解ELF共享对象的内容,另请参阅elf(5)Levine’s book: Linkers and Loaders

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

上一篇:linux – 按需加载/卸载ELF部分?


下一篇:c – 使用ELF文件