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

时间:2015-04-15 11:23:43

标签: c linux shared-libraries elf symbol-table

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

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

这里有一点阐述:

enter image description here

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()没有做我认为会做的事情。

1 个答案:

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