一次Art Hook失败问题的跟进

0x00 缘起

最近在使用一款Art Hook框架对应用进行Hook的时候发现,函数Hook之后却总是没有被触发,于是怀疑是被dex2oat做了Inline处理。

以下环境以Android 5.0 x86 为例。

0x01 迷途

使用oatdump --oat-file=oat_path命令进行dump,却发现几乎所有的函数都无法看到汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
OatMethodOffsets (offset=0x00000000)

code_offset: 0x00000000

gc_map: (offset=0x00000000)

OatQuickMethodHeader (offset=0x00000000)

mapping_table: (offset=0x00000000)

vmap_table: (offset=0x00000000)

QuickMethodFrameInfo

frame_size_in_bytes: 0

core_spill_mask: 0x00000000

fp_spill_mask: 0x00000000

CODE: (code_offset=0x00000000 size_offset=0x00000000 size=0)

NO CODE!

看起来像是没有进行oat,但是dump的文件却真真实实是一个oat文件啊。不是说Android 5.0都是进行oat编译的吗,总不会是oatdump有问题吧!

0x02 明心

于是,又重新安装了一次,抓logcat看了以下,发现以下日志很可疑:

1
2
3
I/PackageManager( 1546): Running dexopt on: /data/app/com.autonavi.minimap-1/base.apk pkg=com.autonavi.minimap isa=x86 vmSafeMode=true

I/dex2oat ( 3779): /system/bin/dex2oat --zip-fd=5 --zip-location=/data/app/com.autonavi.minimap-1/base.apk --oat-fd=6 --oat-location=/data/dalvik-cache/x86/data@app@com.autonavi.minimap-1@base.apk@classes.dex --instruction-set=x86 --instruction-set-features=default --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=interpret-only

看起来是interpret-only导致使用了解释方式编译。虽然不确定是不是因为interpret导致Hook不生效,但是看不到汇编代码,心里总是没有底。

所以,现在就要将编译模式改成非解释方式。

/frameworks/native/cmds/installd/commands.c文件的run_dex2oat函数中找到了如下代码:

1
2
3
4
5
6
7
8
if (skip_compilation) {
strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
have_dex2oat_compiler_filter_flag = true;
} else if (vm_safe_mode) {
strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
have_dex2oat_compiler_filter_flag = true;
} else if (have_dex2oat_compiler_filter_flag) {
sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag);

看来就是这个vm_safe_mode导致使用了interpret-only模式,而上面logcat日志显示:vm_safe_mode的值是true

顺便提一句,当vm_safe_modefalse时,系统会使用dex2oat_compiler_filter_flag的值作为编译类型。

1
2
char dex2oat_compiler_filter_flag[PROPERTY_VALUE_MAX];
bool have_dex2oat_compiler_filter_flag = property_get("dalvik.vm.dex2oat-filter", dex2oat_compiler_filter_flag, NULL) > 0;

从上面的代码可以看出,dex2oat_compiler_filter_flag的值是取自dalvik.vm.dex2oat-filter这个系统属性。

可选的值有:verify-none | interpret-only | space | balanced | speed | everything 。而系统默认是没有设置这个属性的,如果我们想设置默认的编译类型,可以修改/system/build.prop文件,添加dalvik.vm.dex2oat-filter=interpret-only,保存文件并重启手机,但是这步操作需要root权限。

系统默认使用的编译类型设置如下:

1
2
3
4
5
6
7
8
  UsageError("  --compiler-filter=(verify-none|interpret-only|space|balanced|speed|everything):");
UsageError(" select compiler filter.");
UsageError(" Example: --compiler-filter=everything");
#if ART_SMALL_MODE
UsageError(" Default: interpret-only");
#else
UsageError(" Default: speed");
#endif

因此, 在应用没有设置vm_safe_mode,并且系统没有设置dalvik.vm.dex2oat-filter属性时,就会使用speed模式。

0x03 见性

下面来看vm_safe_mode是怎么设置的。

/frameworks/native/cmds/installd/commands.c

1
2
3
4
5
6
7
int dexopt(const char *apk_path, uid_t uid, bool is_public,
const char *pkgname, const char *instruction_set,
bool vm_safe_mode, bool is_patchoat){
// ......
run_dex2oat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set, vm_safe_mode);
// ......
}

一路向上,找到/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java中的performDexOptLI函数,里面包含如下代码:

1
2
3
4
5
6
7
8
final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;

Log.i(TAG, "Running dexopt on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode);

final int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
pkg.packageName, dexCodeInstructionSet, vmSafeMode);

可以看出,vmSafeMode是从应用信息里读出来的,最终在反编译出来的AndroidManifest.xml里,我们找到了以下配置:

1
<application android:allowBackup="false" android:hardwareAccelerated="true" android:icon="@0x7f0213b0" android:label="@0x7f090000" android:largeHeap="true" android:name="com.autonavi.minimap.MapApplication" android:resizeableActivity="false" android:roundIcon="@0x7f0213b0" android:supportsRtl="true" android:theme="@0x7f0b0065" android:vmSafeMode="true">

0x04 缘灭

解决方法就是,将android:vmSafeMode字段设为false,重打包应用,然后安装。

1
I/PackageManager( 1546): Running dexopt on: /data/app/com.autonavi.minimap-1/base.apk pkg=com.autonavi.minimap isa=x86 vmSafeMode=false

vmSafeMode已经被成功修改成false了。

使用oatduump命令查看oat信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
OatMethodOffsets (offset=0x0282dfa0)

code_offset: 0x0520ff08

gc_map: (offset=0x02c554ba)

OatQuickMethodHeader (offset=0x0520fef0)

mapping_table: (offset=0x03202b8b)

vmap_table: (offset=0x034095d5)

v65534/r5, v2/r6, v0/r7, v65535/r16

QuickMethodFrameInfo

frame_size_in_bytes: 48

core_spill_mask: 0x000100e0 (r5, r6, r7, r16)

fp_spill_mask: 0x00000000

CODE: (code_offset=0x0520ff08 size_offset=0x0520ff04 size=192)...

0x0520ff08: 85842400E0FFFF test eax, [esp + -8192]

suspend point dex PC: 0x0000

GC map objects: v2 (r6)

0x0520ff0f: 83EC2C sub esp, 44

0x0520ff12: 896C2420 mov [esp + 32], ebp

0x0520ff16: 89742424 mov [esp + 36], esi

0x0520ff1a: 897C2428 mov [esp + 40], edi

0x0520ff1e: 8BE8 mov ebp, eax

0x0520ff20: 890424 mov [esp], eax

0x0520ff23: 8BF1 mov esi, ecx

0x0520ff25: 8B4514 mov eax, [ebp + 20]

0x0520ff28: 8B80D4AC0100 mov eax, [eax + 109780]

0x0520ff2e: 85C0 test eax, eax

0x0520ff30: 746A jz/eq +106 (0x0520ff9c)

0x0520ff32: 8BF8 mov edi, eax

0x0520ff34: 8B4514 mov eax, [ebp + 20]

0x0520ff37: 8B805C7C0000 mov eax, [eax + 31836]

汇编指令也可以正常显示了。

经过测试,Hook也被正常调用了。所以,应该是该Hook框架还不支持解释模式编译的应用。