【原创】Android5.1 Art Hook 技术分享
Hi,大家好,很多次的在各种技术论坛上看到大牛的分享,学到了很多。本着共建社区,共享知识的目的,在这里我和大家分享一下我最近研究到的关于Android5.1的ART HOOK方案。还是demo阶段,请大家多多指正。可以加我QQ 313199058一起探讨。
废话不说,切入正题。
之前看过低端码农关于ART HOOK的思路,有了启发,大家可以先到他的博客上看看他对于ART虚拟机的理解以及他做的hook方案。
http://blog.csdn.net/l173864930/article/details/45035521
他做的是基于android 4.4的hook方案,但是是停留在仅仅打log的阶段,在我后面的测试中发现这其实离真正的hook应用还相去甚远。下面我罗列了一些我需要解决的问题。这些问题,低端码农有解决过一些,有的没有;xposed有解决过一些,有的没有。
我们需要解决如下的几个问题:
1. 如何hook到一个函数
2. 如何回避在输出调用栈时的虚拟机崩溃
3. 如何处理参数
4. 如何处理返回值
5. 如何在不替换libart.so的情况下完成hook
6. 如何在不引用android源码头文件及库的情况下,用NDK直接编译出我们的so
0x00
老调重弹,我先简单介绍一下ART虚拟机关于方法的调用方式。不同于dalvik虚拟机,ART其实包含了两种调用方式——解释执行和机器码执行,首先他没有完全丢弃解释执行的调用方式,因为有些情况下还是需要通过解释执行完成一个函数的运行;接着ART不同于dalvik是因为引入了机器码的运行方式,其实就是在dex opt的时候dex里的一个函数体被优化成了汇编语言编写的机器码,这样运行效率当然高了。
下面看一下oatdump出的某函数片段
4: void com.example.atry.MainActivity.onClick(android.view.View) (dex_method_idx=18)
DEX CODE:
0x0000: const/4 v0, #+2
0x0001: const-wide/16 v2, #+5
0x0003: invoke-virtual {v4, v0, v2, v3}, void com.example.atry.MainActivity.nativeTest(int, long) // method@17
0x0006: return-void
OAT DATA:
frame_size_in_bytes: 64
core_spill_mask: 0x00008060 (r5, r6, r15)
fp_spill_mask: 0x00000000
vmap_table: 0xf722d58a (offset=0x0000258a)
v3/r5, v4/r6, v65535/r15
mapping_table: 0xf722d584 (offset=0x00002584)
gc_map: 0xf722d590 (offset=0x00002590)
CODE: 0xf722d51d (offset=0x0000251d size=104)...
0xf722d51c: f8d9c010 ldr.w r12, [r9, #16] ; stack_end_
0xf722d520: e92d4060 push {r5, r6, lr}
0xf722d524: f2ad0e34 subw lr, sp, #52
0xf722d528: 45e6 cmp lr, r12
0xf722d52a: f0c08024 bcc.w +72 (0xf722d576)
0xf722d52e: 46f5 mov sp, lr
0xf722d530: 9000 str r0, [sp, #0]
0xf722d532: 1c0e mov r6, r1
0xf722d534: 9212 str r2, [sp, #72]
0xf722d536: 2202 movs r2, #2
0xf722d538: 9208 str r2, [sp, #32]
0xf722d53a: 2305 movs r3, #5
0xf722d53c: f04f0c00 mov.w r12, ThumbExpand(0)
0xf722d540: e9cd3c0a
0xf722d544: 9b0b ldr r3, [sp, #44]
0xf722d546: 1c31 mov r1, r6
0xf722d548: f8d1e000 ldr.w lr, [r1, #0]
0xf722d54c: 9304 str r3, [sp, #16]
0xf722d54e: 9304 str r3, [sp, #16]
0xf722d550: f8dee034 ldr.w lr, [lr, #52]
0xf722d554: 9b0a ldr r3, [sp, #40]
0xf722d556: 2202 movs r2, #2
0xf722d558: f8de0544 ldr.w r0, [lr, #1348]
0xf722d55c: f8d0e028 ldr.w lr, [r0, #40]
0xf722d560: 47f0 blx lr
suspend point dex PC: 0x0003
GC map objects: v4 (r6), v5 ([sp + #72])
0xf722d562: 3c01 subs r4, #1
0xf722d564: f0008003 beq.w +6 (0xf722d56e)
0xf722d568: b00d add sp, sp, #52
0xf722d56a: e8bd8060 pop {r5, r6, pc}
0xf722d56e: f8d9e25c ldr.w lr, [r9, #604] ; pTestSuspend
0xf722d572: 47f0 blx lr
suspend point dex PC: 0x0006
0xf722d574: e7f8 b -16 (0xf722d568)
0xf722d576: f8dde008 ldr.w lr, [sp, #8]
0xf722d57a: b003 add sp, sp, #12
0xf722d57c: f8d9c274 ldr.w r12, [r9, #628] ; pThrowStackOverflow
0xf722d580: 4760 bx r12
0xf722d582: 0000 lsls r0, r0, #0
class MANAGED ArtMethod : public Object {
…
protected:
Class* declaring_class_;
uint32_t access_flags_;
uint32_t code_item_offset_;
const void* entry_point_from_compiled_code_;
EntryPointFromInterpreter* entry_point_from_interpreter_;
….
}
ENTRY art_quick_to_interpreter_bridge
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP)
ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
ldr lr, [sp, #44] @ restore lr
add sp, #48 @ pop frame
.cfi_adjust_cfa_offset -48
cbnz r2, 1f @ success if no exception is pending
bx lr @ return on success
1:
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
4: void com.example.atry.MainActivity.onClick(android.view.View) (dex_method_idx=18)
DEX CODE:
0x0000: const/4 v0, #+2
0x0001: const-wide/16 v2, #+5
0x0003: invoke-virtual {v4, v0, v2, v3}, void com.example.atry.MainActivity.nativeTest(int, long) // method@17
0x0006: return-void
CODE: 0xf722d51d (offset=0x0000251d size=104)...
0xf722d51c: f8d9c010 ldr.w r12, [r9, #16] ; stack_end_
0xf722d520: e92d4060 push {r5, r6, lr}
0xf722d524: f2ad0e34 subw lr, sp, #52
0xf722d528: 45e6 cmp lr, r12
0xf722d52a: f0c08024 bcc.w +72 (0xf722d576)
//上面都是检查是否调用函数层数太多,防止栈溢出。
0xf722d52e: 46f5 mov sp, lr
0xf722d530: 9000 str r0, [sp, #0]
0xf722d532: 1c0e mov r6, r1
0xf722d534: 9212 str r2, [sp, #72]
0xf722d536: 2202 movs r2, #2
0xf722d538: 9208 str r2, [sp, #32]
0xf722d53a: 2305 movs r3, #5
0xf722d53c: f04f0c00 mov.w r12, ThumbExpand(0)
0xf722d540: e9cd3c0a
0xf722d544: 9b0b ldr r3, [sp, #44]
0xf722d546: 1c31 mov r1, r6
0xf722d548: f8d1e000 ldr.w lr, [r1, #0]
0xf722d54c: 9304 str r3, [sp, #16]
0xf722d54e: 9304 str r3, [sp, #16]
0xf722d550: f8dee034 ldr.w lr, [lr, #52]
0xf722d554: 9b0a ldr r3, [sp, #40]
0xf722d556: 2202 movs r2, #2//上面都是在构造参数,准备调用下个函数
0xf722d558: f8de0544 ldr.w r0, [lr, #1348] //找到了被调用函数nativeTest
0xf722d55c: f8d0e028 ldr.w lr, [r0, #40]//取出被调用函数首地址偏移40的地址
0xf722d560: 47f0 blx lr//跳转到偏移40处的地址
…
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!