Android: 使用oatdump反编译oat文件

网上经常看到有通过apktool将apk中的dex反编译成smali格式的文件,以便分析功能实现与破—解,确没怎么看到oat文件反通过oatdump反编译的,所以就写了一篇这样的文档。声明一下oat文件也是可以反编译的。本来想着通过oatdump处理之后,既可以看到dex代码又可以看到相应的汇编实现。反编译之后发现其实不然,难道是因为系统是eng版本的原因,有机会再验证看看。

先看一下这两个代码片段

1. JNI代码: jni/com_android_hello_HelloActivity.cpp

#include <jni.h>
#include <JNIHelp.h>

extern "C" void Java_com_android_hello_HelloActivity_sayHello(JNIEnv *, jobject)
{
	__builtin_trap();
}

使APP出现native crash

2. Java代码: src/com/android/hello/HelloActivity.java

public class HelloActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sayHello();
    }

    static {
        System.loadLibrary("hello-jni");
    }

    private static native void sayHello();
}

加载JNI库libhello-jni.so, 并且在app launch时调用JNI方法sayHello()。

APK安装时会执行dex2oat命令,生成oat文件:

03-11 23:06:38.748 11835 11902 D PackageManager: Renaming /data/app/vmdl111617587.tmp to /data/app/com.android.hello-1
03-11 23:06:38.774 11835 11902 I PackageManager: Running dexopt on: /data/app/com.android.hello-1/base.apk pkg=com.android.hello isa=arm vmSafeMode=false
03-11 23:06:38.800 18221 18221 I dex2oat : /system/bin/dex2oat --zip-fd=6 --zip-location=/data/app/com.android.hello-1/base.apk --oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@com.android.hello-1@base.apk@classes.dex --instruction-set=arm --instruction-set-features=div --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=interpret-only --swap-fd=8
03-11 23:06:38.818 18221 18221 I dex2oat : Decided to run without swap.
03-11 23:06:38.838 18221 18221 I dex2oat : dex2oat took 38.211ms (threads: 4) arena alloc=0B java alloc=7KB native alloc=75KB free=32KB

oat文件是什么:

$ adb pull /data/dalvik-cache/arm/data@app@com.android.hello-1@base.apk@classes.dex
1236 KB/s (12720 bytes in 0.010s)
$ file data\@app\@com.android.hello-1\@base.apk\@classes.dex 
data@app@com.android.hello-1@base.apk@classes.dex: ELF 32-bit LSB shared object, ARM, version 1 (GNU/Linux), dynamically linked, stripped

是一个ELF可执行文件。执行readelf/arm-linux-androideabi-readelf命令看看:

$ arm-linux-androideabi-readelf -a data\@app\@com.android.hello-1\@base.apk\@classes.dex 
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          12400 (bytes into file)
  Flags:                             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         5
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 7

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .dynsym           DYNSYM          000000d4 0000d4 000040 10   A  2   0  4
  [ 2] .dynstr           STRTAB          00000114 000114 00004f 01   A  0   0  1
  [ 3] .hash             HASH            00000164 000164 000020 04   A  1   0  4
  [ 4] .rodata           PROGBITS        00001000 001000 001000 00   A  0   0 4096
  [ 5] .text             PROGBITS        00002000 002000 000094 00  AX  0   0 4096
  [ 6] .dynamic          DYNAMIC         00003000 003000 000038 08   A  1   0 4096
  [ 7] .shstrtab         STRTAB          00000000 003038 000038 01      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00000034 0x00000034 0x000a0 0x000a0 R   0x4
  LOAD           0x000000 0x00000000 0x00000000 0x02000 0x02000 R   0x1000
  LOAD           0x002000 0x00002000 0x00002000 0x00094 0x00094 R E 0x1000
  LOAD           0x003000 0x00003000 0x00003000 0x00038 0x00038 RW  0x1000
  DYNAMIC        0x003000 0x00003000 0x00003000 0x00038 0x00038 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .dynsym .dynstr .hash .rodata 
   02     .text 
   03     .dynamic 
   04     .dynamic 

Dynamic section at offset 0x3000 contains 7 entries:
  Tag        Type                         Name/Value
 0x00000004 (HASH)                       0x164
 0x00000005 (STRTAB)                     0x114
 0x00000006 (SYMTAB)                     0xd4
 0x0000000b (SYMENT)                     16 (bytes)
 0x0000000a (STRSZ)                      79 (bytes)
 0x0000000e (SONAME)                     Library soname: [data@app@com.android.hello-1@base.apk@classes.dex]
 0x00000000 (NULL)                       0x0

There are no relocations in this file.

There are no unwind sections in this file.

Symbol table '.dynsym' contains 4 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00001000  4096 OBJECT  GLOBAL DEFAULT    4 oatdata
     2: 00002000   148 OBJECT  GLOBAL DEFAULT    5 oatexec
     3: 00002090     4 OBJECT  GLOBAL DEFAULT    5 oatlastword

Histogram for bucket list length (total of 2 buckets):
 Length  Number     % of total  Coverage
      0  0          (  0.0%)
      1  1          ( 50.0%)     33.3%
      2  1          ( 50.0%)    100.0%

No version information found in this file.

能使用objdump/arm-linux-androideabi-objdum反编译吗:

$ arm-linux-androideabi-objdump -d data\@app\@com.android.hello-1\@base.apk\@classes.dex 

data@app@com.android.hello-1@base.apk@classes.dex:     file format elf32-littlearm


Disassembly of section .text:

00002000 <oatexec>:
	...
    200c:	30 00 00 00 e0 4d 00 00 00 00 00 00 78 00 00 00     0....M......x...
    201c:	2d e9 e0 4d ad f2 14 0d 00 90 4f f0 01 0c cd f8     -..M......O.....
    202c:	08 c0 d9 f8 c8 c0 cd f8 04 c0 0d f2 04 0c c9 f8     ................
    203c:	c8 c0 d0 f8 08 c0 cd f8 0c c0 c9 f8 8c d0 4f f0     ..............O.
    204c:	00 0c c9 f8 90 c0 48 46 d0 f8 94 c1 e0 47 04 90     ......HF.....G..
    205c:	0d f2 0c 01 d9 f8 98 00 dd f8 00 c0 dc f8 28 c0     ..............(.
    206c:	e0 47 04 98 49 46 d1 f8 9c c1 e0 47 d9 f8 7c c0     .G..IF.....G..|.
    207c:	bc f1 00 0f 03 d1 0d f2 14 0d bd e8 e0 8d 60 46     ..............`F
    208c:	d9 f8 34 c2                                         ..4.

00002090 <oatlastword>:
    2090:	e0 47 00 be                                         .G..

没什么内容。

最后,我们用oatdump命令查看一下:

$ oatdump --oat-file=data\@app\@com.android.hello-1\@base.apk\@classes.dex 
MAGIC:
oat
045

CHECKSUM:
0x864eeade

INSTRUCTION SET:
Thumb2

INSTRUCTION SET FEATURES:
div

DEX FILE COUNT:
1

EXECUTABLE OFFSET:
0x00001000

INTERPRETER TO INTERPRETER BRIDGE OFFSET:
0x00000000

INTERPRETER TO COMPILED CODE BRIDGE OFFSET:
0x00000000

JNI DLSYM LOOKUP OFFSET:
0x00000000

PORTABLE IMT CONFLICT TRAMPOLINE OFFSET:
0x00000000

PORTABLE RESOLUTION TRAMPOLINE OFFSET:
0x00000000

PORTABLE TO INTERPRETER BRIDGE OFFSET:
0x00000000

QUICK GENERIC JNI TRAMPOLINE OFFSET:
0x00000000

QUICK IMT CONFLICT TRAMPOLINE OFFSET:
0x00000000

QUICK RESOLUTION TRAMPOLINE OFFSET:
0x00000000

QUICK TO INTERPRETER BRIDGE OFFSET:
0x00000000

IMAGE PATCH DELTA:
0 (0x00000000)

IMAGE FILE LOCATION OAT CHECKSUM:
0x8223fe58

IMAGE FILE LOCATION OAT BEGIN:
0x70dd0000

KEY VALUE STORE:
dex2oat-cmdline = --zip-fd=6 --zip-location=/data/app/com.android.hello-1/base.apk --oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@com.android.hello-1@base.apk@classes.dex --instruction-set=arm --instruction-set-features=div --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=interpret-only --swap-fd=8
dex2oat-host = Arm
image-location = /data/dalvik-cache/arm/system@framework@boot.art
pic = false

SIZE:
4244

OatDexFile:
location: /data/app/com.android.hello-1/base.apk
checksum: 0x2e3d006c
0: Lcom/android/hello/HelloActivity; (offset=0x00000578) (type_idx=2) (StatusVerified) (OatClassSomeCompiled)
  0: void com.android.hello.HelloActivity.<clinit>() (dex_method_idx=2)
    DEX CODE:
      0x0000: const-string v0, "hello-jni" // string@10
      0x0002: invoke-static {v0}, void java.lang.System.loadLibrary(java.lang.String) // method@6
      0x0005: return-void
    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!
  1: void com.android.hello.HelloActivity.<init>() (dex_method_idx=3)
    DEX CODE:
      0x0000: invoke-direct {v0}, void android.app.Activity.<init>() // method@0
      0x0003: return-void
    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!
  2: void com.android.hello.HelloActivity.sayHello() (dex_method_idx=5)
    DEX CODE:
    OatMethodOffsets (offset=0x00000584)
      code_offset: 0x0000101d 
      gc_map: (offset=0x00000000)
    OatQuickMethodHeader (offset=0x00001000)
      mapping_table: (offset=0x00000000)
      vmap_table: (offset=0x00000000)
    QuickMethodFrameInfo
      frame_size_in_bytes: 48
      core_spill_mask: 0x00004de0 (r5, r6, r7, r8, r10, r11, r14)
      fp_spill_mask: 0x00000000 
    CODE: (code_offset=0x0000101d size_offset=0x00001018 size=120)...
      0x0000101c: e92d4de0	push    {r5, r6, r7, r8, r10, r11, lr}
      0x00001020: f2ad0d14	subw    sp, sp, #20
      0x00001024: 9000    	str     r0, [sp, #0]
      0x00001026: f04f0c01	mov.w   r12, #1
      0x0000102a: f8cdc008	str.w   r12, [sp, #8]
      0x0000102e: f8d9c0c8	ldr.w   r12, [r9, #200]  ; top_handle_scope
      0x00001032: f8cdc004	str.w   r12, [sp, #4]
      0x00001036: f20d0c04	addw    r12, sp, #4
      0x0000103a: f8c9c0c8	str.w   r12, [r9, #200]
      0x0000103e: f8d0c008	ldr.w   r12, [r0, #8]
      0x00001042: f8cdc00c	str.w   r12, [sp, #12]
      0x00001046: f8c9d08c	str.w   sp, [r9, #140]
      0x0000104a: f04f0c00	mov.w   r12, #0
      0x0000104e: f8c9c090	str.w   r12, [r9, #144]
      0x00001052: 4648    	mov     r0, r9
      0x00001054: f8d0c194	ldr.w   r12, [r0, #404]
      0x00001058: 47e0    	blx     r12
      0x0000105a: 9004    	str     r0, [sp, #16]
      0x0000105c: f20d010c	addw    r1, sp, #12
      0x00001060: f8d90098	ldr.w   r0, [r9, #152]  ; jni_env
      0x00001064: f8ddc000	ldr.w   r12, [sp, #0]
      0x00001068: f8dcc028	ldr.w   r12, [r12, #40]
      0x0000106c: 47e0    	blx     r12
      0x0000106e: 9804    	ldr     r0, [sp, #16]
      0x00001070: 4649    	mov     r1, r9
      0x00001072: f8d1c19c	ldr.w   r12, [r1, #412]
      0x00001076: 47e0    	blx     r12
      0x00001078: f8d9c07c	ldr.w   r12, [r9, #124]  ; exception
      0x0000107c: f1bc0f00	cmp.w   r12, #0
      0x00001080: d103    	bne     +6 (0x0000108a)
      0x00001082: f20d0d14	addw    sp, sp, #20
      0x00001086: e8bd8de0	pop     {r5, r6, r7, r8, r10, r11, pc}
      0x0000108a: 4660    	mov     r0, r12
      0x0000108c: f8d9c234	ldr.w   r12, [r9, #564]  ; pDeliverException
      0x00001090: 47e0    	blx     r12
      0x00001092: be00    	        
  3: void com.android.hello.HelloActivity.onCreate(android.os.Bundle) (dex_method_idx=4)
    DEX CODE:
      0x0000: invoke-super {v0, v1}, void android.app.Activity.onCreate(android.os.Bundle) // method@1
      0x0003: invoke-static {}, void com.android.hello.HelloActivity.sayHello() // method@5
      0x0006: return-void
    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文件中既包含dex代码,又包启汇编代码,不知道是不是因为我手机是eng版本的。相关系统属性如下(Nexus4 android-5.1.1_r17(lmy48t):

[dalvik.vm.dex2oat-Xms]: [64m]
[dalvik.vm.dex2oat-Xmx]: [512m]
[dalvik.vm.dex2oat-filter]: [interpret-only]
[dalvik.vm.dexopt-flags]: [m=y]
[dalvik.vm.image-dex2oat-Xms]: [64m]
[dalvik.vm.image-dex2oat-Xmx]: [64m]
[dalvik.vm.image-dex2oat-filter]: [verify-none]

相关的参考文档,ART and Dalvik: https://source.android.com/devices/tech/dalvik/index.html

1. Bytecode Format: https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

2. .Dex Format: https://source.android.com/devices/tech/dalvik/dex-format.html

3. Instruction Formats: https://source.android.com/devices/tech/dalvik/instruction-formats.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注