Group of Software Security In Progress

GoSSIP @ LoCCS.Shanghai Jiao Tong University

ArtHook: Callee-side Method Hook Injection on the New Android Runtime ART

文章介绍了一种在ART runtime上进行HOOK的技术。

首先,介绍了ART上的相关知识,包括Ahead-of-time compilation,OAT file format,Optimization,Method handling等。

优化包括:

  • direct jumps:framework中被调用的接口都在boot.art中,而boot.art每次都被mapped to the same location in virtual memory。因此编译好的oat直接就写地址,不需要在method table中查找再跳转。
  • Method inlining:只有一条返回指令的方法会被合并到调用方法中。
  • method merging:调用相同native代码的不同JNI入口会被合并。

之后介绍了已有的HOOK思路:

  • Static hooking:

    1. Injecting shared libraries:通过修改运行环境来改变某些库的调用,例如修改环境变量LD_PRELOAD使得调用跳转到hook新加的lib,之后再用dlsym来调用原来的lib。但Android不允许任意修改classpath modifications,且这种方法只能HOOK系统的lib,不能HOOK应用自己的代码。
    2. Static caller-side rewriting:在源码中找到发起调用的代码,修改其调用地址,或者添加一些代码,指向HOOK的代码。这种方法可以通过修改DEX来实现,但是HOOK不是发生在runtime,不够灵活。
    3. Static callee-side rewriting:将被调用的代码转移,替换为新的代码。这种更不可能,不灵活的同时,系统代码也不让修改。
  • Dynamic hooking:

    1. Call diversion:在大部分系统,当调用一个方法时,会首先在系统的method table中找到改方法在内存中的地址(动态链接)。通过修改method table中的地址可以做到改变调用。在dalvik中可以做到,但是在ART中,大部分方法的执行都采用direct jumps的机制。这种方式不可行。
    2. Dynamic callee-side rewriting:思路和Static callee-side rewriting一样,但是修改的不是lib的代码而是修改内存中的copy。

文章HOOK的思路是Dynamic callee-side rewriting。无论调用方式是direct jumps,JNI,还是反射,被调用的代码都是oat中编译好的代码(这里应该没考虑解释执行部分)。唯一的问题是Method inlining,只有一条指令的方法直接不存在了,因此也没法修改。

Callee-side code injection

用linux的系统方法mprotect来使内存中代码的部分可写。备份每个方法开头的prologue指令(sets up the stack and stores the processor state),替换为跳转指令(修改PC寄存器的值)。

Custom executable memory page

跳转到一个为每个method特别生成的memory page,并执行。(具体细节见论文) Library design and usage

具体应用。

最后文章对该技术进行了测试。效果很好,消耗低。

除此之外,还有Xposed on ART也做了类似工作,不过改了系统,修改了dex2oat,免去优化,使其不再direct jumps。