挂钩并替换已导出的ELF中的导出功能(.so共享库) [英] Hook and Replace Export Function in the Loaded ELF ( .so shared library )

查看:107
本文介绍了挂钩并替换已导出的ELF中的导出功能(.so共享库)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing some C code to hook some function of .so ELF (shared-library) loaded into memory.

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

My C code should be able to re-direct an export function of another .so library that was loaded into the app/program's memory.

这里有一些阐述:

Android应用程序将加载多个.so文件.我的C代码必须仔细检查属于另一个共享.so库(在本例中称为target.so)的导出功能

Android app will have multiple .so files loaded. My C code has to look through export function that belongs to another shared .so library (called target.so in this case)

这不是常规的dlsym方法,因为我不只是想要函数的地址,而是想用自己的功能替换它;这样做的方式是:当另一个库调用其自身的函数时,将改为调用我的hook_func,然后从我的hook_func中调用original_func.

This is not a regular dlsym approach because I don't just want address of a function but I want to replace it with my own fuction; in that: when another library makes the call to its own function then instead my hook_func gets called, and then from my hook_func I should call the original_func.

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

For import functions this can work. But for export functions I'm not sure how to do it. Import functions have the entries in the symbol table that have corresponding entry in relocation table that eventually gives the address of entry in global offset table (GOT). But for the export functions, the symbol's st_value element itself has address of the procedure and not GOT address (correct me if I'm wrong).

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

从理论上讲,我应该获取导出功能的动态符号表条目(Elf32_Sym)的st_value元素的存储位置.如果得到该位置,则应该可以用我的hook_func的地址替换该位置中的值.但是,到目前为止,我无法写入该位置.我必须假设动态符号表的内存是只读的.如果是这样,那么在这种情况下的解决方法是什么?

Theoretically speaking, I should get the memory location of the st_value element of dynamic symbol table entry ( Elf32_Sym ) of export function. If I get that location then I should be able to replace the value in that location with my hook_func's address. However, I'm not able to write into this location so far. I have to assume the dynamic symbol table's memory is read-only. If that is true then what is the workaround in that case?

非常感谢您的阅读和帮助.

Thanks a lot for reading and helping me out.

更新:LD_PRELOAD只能用我自己的函数替换原始函数,但是我不确定是否有任何方法可以调用原始函数. 以我为例:

Update: LD_PRELOAD can only replace the original functions with my own, but then I'm not sure if there any way to call the originals. In my case for example:

App通过调用Audio_System_Create初始化音频引擎,并将AUDIO_SYSTEM对象的引用传递给Audio_System_Create(AUDIO_SYSTEM **); AUDIO API分配此结构/对象并返回函数. 现在,只要我可以访问该AUDIO_SYSTEM对象,就可以轻松地向该对象附加一个回调并开始接收音频数据. 因此,我的最终目标是获得对AUIOD_SYSTEM对象的引用.根据我的理解,只有在我第一次通过Audio_System_Create(AUIOD_SYSTEM **)分配该对象的地方拦截该调用时,我才能得到该信息. 当前没有直接的方法来在android上获取输出音频. (所有示例都谈到仅录制来自麦克风的音频)

App initializes the audio engine by calling Audio_System_Create and passes a reference of AUDIO_SYSTEM object to Audio_System_Create(AUDIO_SYSTEM **); AUDIO API allocates this struct/object and function returns. Now if only I could access that AUDIO_SYSTEM object, I would easily attach a callback to this object and start receiving audio data. Hence, my ultimate goal is to get the reference to AUIOD_SYSTEM object; and in my understanding, I can only get that if I intercept the call where that object is first getting allocated through Audio_System_Create(AUIOD_SYSTEM **). Currently there is no straight way to grab the output audio at android. (all examples talk about recording audio that comes from microphone only)

Update2: 正如Basile在他的回答中所建议的那样,我使用了dladdr(),但奇怪的是它给了我与传递给它相同的地址.

Update2: As advised by Basile in his answer, I made use of dladdr() but strangely enough it gives me the same address as I pass to it.

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 =0x75a28cfc

entry_addr_through_dlysm = 0x75a28cfc

entry_addr_through_dlysm =0x75a28cfc

符号名称是:AUDIO_System_Create

Symbol Name is: AUDIO_System_Create

符号地址为:0x75a28cfc

Symbol Address is: 0x75a28cfc

这里通过dlysm获得或通过ELF文件计算出的地址是程序的地址;而我需要此地址本身所在的位置;这样我就可以用我的hook_func地址替换该地址. dladdr()没有按照我的预期去做.

Here address obtained through dlysm or calculated through ELF file is the address of procedure; while I need the location where this address itself is; so that I can replace this address with my hook_func address. dladdr() didn't do what I thought it will do.

推荐答案

您应该详细阅读Drepper的论文: mprotect(2)和/或进行更改 mmap(2)和/或 asmjit 或GNU 闪电)跳转到<

You should read in details Drepper's paper: how to write shared libraries - notably to understand why using LD_PRELOADis not enough. You may want to study the source code of the dynamic linker (ld-linux.so) inside your libc. You might try to change with mprotect(2) and/or mmap(2) and/or mremap(2) the relevant pages. You can query the memory mapping thru proc(5) using /proc/self/maps & /proc/self/smaps. Then you could, in an architecture-specific way, replace the starting bytes (perhaps using asmjit or GNU lightning) of the code of original_func by a jump to your hook_func function (which you might need to change its epilogue, to put the overwritten instructions -originally at original_func- there...)

如果original_func是众所周知的并且始终相同,则事情可能会稍微容易一些.然后,您可以研究其源代码和汇编代码,并编写补丁功能和您的hook_func.

Things might be slightly easier if original_func is well known and always the same. You could then study its source and assembler code, and write the patching function and your hook_func only for it.

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

Perhaps using dladdr(3) might be helpful too (but probably not).

或者,修改您的动态链接器以根据需要进行更改.您可能会研究 musl-libc

Alternatively, hack your dynamic linker to change it for your needs. You might study the source code of musl-libc

请注意,您可能需要在original_func的地址(如"original_func"上的dlsym所给出的)覆盖机器代码.另外,您需要重新定位已加载的共享对象(我相信这会更难;如果您坚持要查看 dl_iterate_phdr (3)).

Notice that you probably need to overwrite the machine code at the address of original_func (as given by dlsym on "original_func"). Alternatively, you'll need to relocate every occurrence of calls to that function in all the already loaded shared objects (I believe it is harder; if you insist see dl_iterate_phdr(3)).

如果要使用通用解决方案(对于任意original_func),则需要实现一些二进制代码分析器(或反汇编程序)以修补该功能.如果只想破解特定的original_func,则应将其拆解并修补其机器代码,并让您的hook_func执行您已覆盖的original_func的部分.

If you want a generic solution (for an arbitrary original_func) you'll need to implement some binary code analyzer (or disassembler) to patch that function. If you just want to hack a particular original_func you should disassemble it, and patch its machine code, and have your hook_func do the part of original_func that you have overwritten.

此类骇人且耗时的黑客攻击(您需要数周的时间才能使其正常工作),使我更喜欢使用免费软件(从那时起,对共享库的源进行修补并重新编译要简单得多.)

Such horrible and time consuming hacks (you'll need weeks to make it work) make me prefer using free software (since then, it is much simpler to patch the source of the shared library and recompile it).

当然,这并非易事.您需要详细了解什么是 ELF 共享对象,另请参见

Of course, all this isn't easy. You need to understand in details what ELF shared objects are, see also elf(5) and read Levine's book: Linkers and Loaders

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

NB: Beware, if you are hacking against a proprietary library (e.g. unity3d), what you are trying to achieve might be illegal. Ask a lawyer. Technically, you are violating most abstractions provided by shared libraries. If possible, ask the author of the shared library to give help and perhaps implement some plugin machinery in it.

这篇关于挂钩并替换已导出的ELF中的导出功能(.so共享库)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆