Android Art Hook 技术方案

Android Art Hook 技术方案 by 低端码农 at 2015.4.13 0x1 开始

Anddroid上的ART从5.0之后变成默认的选择,可见ART的重要性,目前关于Dalvik Hook方面研究的文章很多,但我在网上却找不到关于ART Hook相关的文章,甚至连鼎鼎大名的XPosed和Cydia Substrate到目前为止也不支持ART的Hook。当然我相信,技术方案他们肯定是的,估计卡在机型适配上的了。

既然网上找不到相关的资料,于是我决定自己花些时间去研究一下,终于黃天不负有心人,我找到了一个切实可行的方法,即本文所介绍的方法。

应该说明的是本文所介绍的方法肯定不是最好的,但大家看完本文之后,如果能启发大家找到更好的ART Hook方法,那我抛砖引玉的目的就达到了。废话不多说,我们开始吧。

运行环境: 4.4.2 ART模式的模拟器开发环境: Mac OS X 10.10.30×2 ART类方法加载及执行

在ART中类方法的执行要比在Dalvik中要复杂得多,Dalvik如果除去JIT部分,可以理解为是一个解析执行的虚拟机,而ART则同时包含本地指令执行和解析执行两种模式,同时所生成的oat文件也包含两种类型,分别是portable和quick。portable和quick的主要区别是对于方法的加载机制不相同,quick大量使用了Lazy Load机制,因此应用的启动速度更快,但加载流程更复杂。其中quick是作为默认选项,因此本文所涉及的技术分析都是基于quick类型的。

由于ART存在本地指令执行和解析执行两种模式,因此类方法之间并不是能直接跳转的,而是通过一些预先定义的bridge函数进行状态和上下文的切换,这里引用一下老罗博客中的示意图:

当执行某个方法时,如果当前是本地指令执行模式,则会执行ArtMethod::GetEntryPointFromCompiledCode()指向的函数,否则则执行ArtMethod::GetEntryPointFromInterpreter()指向的函数。因此每个方法,都有两个入口点,分别保存在ArtMethod::entry_point_from_compiled_code_和ArtMethod::entry_point_from_interpreter_。了解这一点非常重要,后面我们主要就是在这两个入口做文章。

在讲述原理之前,需要先把以下两个流程了解清楚,这里的内容要展开是非常庞大的,我针对Hook的关键点,简明扼要的描述一下,但还是强烈建议大家去老罗的博客里细读一下其中关于ART的几篇文章。

ArtMethod加载流程

这个过程发生在oat被装载进内存并进行类方法链接的时候,类方法链接的代码在art/runtime/class_linker.cc中的LinkCode,如下所示:

static oat_class, uint32_t method_index)SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Method shouldn’t have already been linked. DCHECK(method->GetEntryPointFromCompiledCode() == NULL); // Every kind of method should at least get an invoke stub from the oat_method. // non-abstract methods also get their code pointers. const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index); // 这里默认会把method::entry_point_from_compiled_code_设置oatmethod的code oat_method.LinkMethod(method.get()); // Install entry point from interpreter. Runtime* runtime = Runtime::Current(); bool enter_interpreter (enter_interpreter) {method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); } else {method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); } // 下面是设置本地指令执行的入口点 if (method->IsAbstract()) {method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());return; } method->IsConstructor()) {method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker())); } else if (enter_interpreter) {// Set entry point from compiled code if there’s no code or in interpreter only mode.method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge()); } if (method->IsNative()) {// Unregistering restores the dlsym lookup stub.method->UnregisterNative(Thread::Current()); } // Allow instrumentation its chance to hijack code. runtime->GetInstrumentation()->UpdateMethodsCode(method.get(),method->GetEntryPointFromCompiledCode());}

通过上面的代码我们可以得到,一个ArtMethod的入口主要有以下几种:

其中调用约定主要有两种,分别是:

typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result), 这种对应上述1,3两种入口;

剩下的2,4,5三种入口对应的是CompledCode的入口,代码中并没有直接给出,但我们通过分析ArtMethod::Invoke的方法调用,就可以知道其调用约定了。Invoke过程中会调用art_quick_invoke_stub(/art/runtime/arch/arm/quick_entrypoints_arm.S),代码如下所示:

/* * Quick invocation stub. * On entry: * r0 = method pointer * r1 = argument array or NULL for no argument methods * r2 = size of argument array in bytes * r3 = (managed) thread pointer * [sp] = JValue* result * [sp + 4] = result type char */ENTRY art_quick_invoke_stubpush {r0, r4, r5, r9, r11, lr}@ spill regs.save {r0, r4, r5, r9, r11, lr}_adjust_cfa_offset _rel_offset _rel_offset _rel_offset lr, , sp@ save the stack pointer, , , , #0xFFFFFFF0@ align frame size to 16 bytessub sp, r5@ reserve stack space for argument arrayadd r0, sp, #4@ pass stack pointer + method ptr as dest for memcpyblmemcpy@ memcpy (dest, src, bytes)ldr r0, [r11]@ restore method*ldr r1, [sp, #4]@ copy arg value for r1ldr r2, [sp, #8]@ copy arg value for r2ldr r3, [sp, #12]@ copy arg value for r3mov ip, #0@ set ip to 0str ip, [sp]@ store NULL for method* at bottom of frameldr ip, [r0, #METHOD_CODE_OFFSET] @ get pointer to the codeblx ip@ call the methodmov sp, r11@ restore the stack pointerldr ip, [sp, #24]@ load the result pointerstrd r0, [ip]@ store r0/r1 into result pointerpop {r0, r4, r5, r9, r11, lr}@ restore spill regs.cfi_adjust_cfa_offset -24bxlrEND art_quick_invoke_stub你在会议中吵架时,尼泊尔的背包客一起端起酒杯坐在火堆旁。

Android Art Hook 技术方案

相关文章:

你感兴趣的文章:

标签云: