一次Art Hook失败问题的跟进

0x00 缘起

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

以下环境以Android 5.0 x86 为例。

0x01 迷途

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

    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看了以下,发现以下日志很可疑:

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函数中找到了如下代码:

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的值作为编译类型。

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权限。

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

  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

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函数,里面包含如下代码:

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里,我们找到了以下配置:

<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,重打包应用,然后安装。

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信息:

    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框架还不支持解释模式编译的应用。

分享