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_mode
为false
时,系统会使用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框架还不支持解释模式编译的应用。