基于 Core 文件的 Android 调试与分析套件

5,111 阅读14分钟

简介

2024 年初画了个饼,重构整套开源的离线内存文件分析套件,时间也刚好过去了半年,该项目仅对 Android 8 ~ Android 15arm64armx86_64x86riscv64 体系的 Core 文件进行支持,更早之前的 Android 版本,实在没有多余的精力去适配了,目前仍有许多功能并不完善,如 native unwind,asm disassembler,dwarf 等只能放到后面。arm64 目前用先采用 FP 堆栈恢复来过渡。

项目介绍

项目地址用途
OpenCoreAnalysisKit用于分析 Core 内存文件的工具箱,目前仅针对 AOSP, 核心项目仓库 core-parser 组成部分。
OpenLinuxAnalysisKit基于 crash-utility 项目解析内核插件集合,linux-parser 插件。
crash-android基于红帽子 crash-utility 项目派生的 android 分支,与 linux-parser 组合使用。
OpenCoreSDK用于用户态程序捕获自身进程 Core 文件的开发组件

其它项目

项目地址用途
GDB可用于调试分析C、C++程序,具备分析 Core 文件的能力,以及进行反编译等。
LLVM高版本的 Android 建议使用该项目替代 GDB 的功能。
crash-utility用于解析 linux vmcore 的工具,基于 GDB 构建的项目,支持插件开发。
jadxJava 字节码反编译工具,分析逆向 APK 程序时可选用配合 Dex 脱壳。
ghidra美国国家安全局(NSA)开源的逆向工程工具套件, 可作为 IDA 使用的替代品。
frida一个强大的、跨平台的动态代码插桩框架

技术体系结构

OpenCoreAnalysizer.jpg

项目编译

OpenCoreAnalysisKit 不支持在 Window 上使用,Window 群体可 ndk 编译 Android 端上使用的程序,该项目编译依赖 cmakeclangndk,需要确保安装了如下版本以及以上。

项目建议版本地址
cmake3.21.1+cmake.org/download
clang12.0.0+github.com/llvm/llvm-p…
ndkr22+github.com/android/ndk…
$ git clone https://github.com/Penguin38/OpenCoreAnalysisKit.git
$ cd OpenCoreAnalysisKit
$
$ export BUILD_TYPE="Debug"                                     (可选)
$ export SUPPORT_CLANG_VERSIONS="10 11 12 13 14 15 16 17 18"    (可选)
$ ./script/build.sh
$
$ export BUILD_TYPE="Debug"                                     (可选)
$ export BUILD_ANDROID_ABIS="arm64-v8a armeabi-v7a x86_64 x86"  (可选)
$ export BUILD_ANDROID_PLATFORM="android-30" (Android 低版本调节,可选)
$ export ANDROID_NDK_HOME=<NDK_DIR>           (编译 Android 版本,必选)
$ ./script/build_android.sh
PlatformPath
Emulatoroutput/aosp/<BUILD_TYPE>/android/x86_64/bin/core-parser
output/aosp/<BUILD_TYPE>/android/x86/bin/core-parser
Phoneoutput/aosp/<BUILD_TYPE>/android/arm64-v8a/bin/core-parser
output/aosp/<BUILD_TYPE>/android/armeabi-v7a/bin/core-parser
Ubuntuoutput/aosp/<BUILD_TYPE>/linux/bin/core-parser
MacOSoutput/aosp/<BUILD_TYPE>/macos/bin/core-parser

OpenLinuxAnalysisKit 采用 gcc 进行独立编译,但插件依赖运行版本应建立在 crash-utility 8.0.4+,过早的版本可能不支持。

$ git clone https://github.com/Penguin38/OpenLinuxAnalysisKit.git
$ cd OpenLinuxAnalysisKit
$ ./build.sh

编译后的产物都在 output 目录下。该目录下有 arm64x86_64、两个子目录,使用 arm64 下的 linux-parser.so 插件即可。

使用指南

分别划分为内核态与用户态解析,传统的内核态解析已经有诸多工具来完成,如高通有 ramparser 工具,MTK 也有类似的 DB 解析工具来获得大量的内核各模块的数据信息,也有一些厂商自制的解析工具,然而用户态的解析,如用于非 C、C++ 的高级语言所直接编写的程序的解析器却少之又少。

内核态

对于 ramdump,vmcore 文件,一般分析可用 T32、crash-utility 工具,本文主要以 crash-utility 项目插件来展开介绍,插件开发可参考《Crash 插件开发指南》。如需详解 crash-utility 项目,可安排一期文章专门介绍。

$ crash-android vmcore vmlinux

向 crash 环境下添加 zram 相关的符号文件,用于插件解压交换出去的内存。

crash-android> mod -s zram zram.ko
crash-android> mod -s zsmalloc zsmalloc.ko

linux-parser 插件添加到 crash 环境下,目前 lp 插件支持的功能有 corezramshmembinderpage_owner,以后会陆续补充,目前主要用于替代 gcore 项目使用。

crash-android> extend <TO_PATH>/OpenLinuxAnalysisKit/output/arm64/linux-parser.so
crash-android> lp help
core        zram        shmem       binder      
meminfo     page_owner  dmabuf      help

我们可以用 lp binder 来查看 binder 信息,目前有哪些线程正在等待响应。

crash-android> lp binder -a | grep out
    outgoing transaction 363543018: 0xffffff8170b4a000 from 7417:7417 to 1709:1728 code 18 flags 12 pri SCHED_NORMAL:120 r1
    outgoing transaction 363543023: 0xffffff806b8a9c00 from 7335:7335 to 1709:2599 code 28 flags 12 pri SCHED_NORMAL:120 r1
    outgoing transaction 363542714: 0xffffff8196ae6400 from 7310:7310 to 1709:3784 code 17 flags 12 pri SCHED_NORMAL:120 r1
    outgoing transaction 363538861: 0xffffff808380de00 from 7203:7203 to 1709:3251 code 6 flags 12 pri SCHED_NORMAL:120 r1

当我们需要访问某个虚拟地址时,发现该内存页表已经被压缩到 zram 时,那么此时可以用 lp zram 命令完成内存的读取。

crash-android> rd 12c00000
rd: WARNING: only the lzo compressor is supported
rd: invalid user virtual address: 12c00000  type: "64-bit UVADDR"

crash-android> vtop 12c00000
VIRTUAL     PHYSICAL        
12c00000    (not mapped)

PAGE DIRECTORY: ffffff80c96dd000
   PGD: ffffff80c96dd000 => 8000001096de003
   PMD: ffffff80c96de4b0 => 8000001096df003
   PTE: ffffff80c96df000 => 4670a00

  PTE                    SWAP                  OFFSET
4670a00  /first_stage_ramdisk/dev/block/zram0  288522

      VMA           START       END     FLAGS FILE
ffffff80a0028658   12c00000   52c00000 100073 

SWAP: /first_stage_ramdisk/dev/block/zram0  OFFSET: 288522

同理的,若是共享内存 shmem 的页表被交换到 zram 上,则用 lp shmem 来完成内存读取。

crash-android> lp zram -r 12c00000 -e 12c00050
        12c00000:  00000000705d1828 000000000000000a  (.]p............
        12c00010:  0000000000000000 0000000000000000  ................
        12c00020:  0000000000000000 0000000000000000  ................
        12c00030:  0000000000000000 0000000000000000  ................
        12c00040:  0000000000000000 0000000000000000  ................

用户态 Core 提取

例如从 binder 信息看到 1709 进程卡在 binder call 上,此时我们需要用户态详细信息,这时可以使用 lp core 来完成 Core 的转储,耗时取决于硬盘的能力。

crash-android> lp core -p 1709 --zram --shmem -f 0x18
Saved [1709.core].

镜像解包

Ramdump 中转出用户态进程的 Core 文件,存在着诸多缺页问题,对于匿名页的缺失我们是没有办法的修补的,但是对文件页表的缺失,我们可以从机器super.img,或是符号文件中获得原数据,解包 super.img 用到的工具有 simg2imglpunpack,这些工具都能在 AOSP 的代码获得。

$ simg2img super.img raw_super.img
$ mkdir out
$ lpunpack raw_super.img out

完成这一步后 out 目录下,会有对应的 system.imgvendor.img 以及其它,按需进行挂载。接下来挂载 system.img 并解包 apex 镜像。

$ mkdir root
$ sudo mount -t erofs out/system_a.img root -o loop

成功挂载 system.img 后,会看到 root/system/apex/ 目录下有 apexcapex 的压缩包文件。

12.png

// erofs 只读文件系统,需要将 apex 拷贝出去解压
$ mkdir apex
$ cp root/system/apex/* apex/

// apex
$ unzip -o apex/com.android.foo.apex -d apex/com.android.foo
$ sudo mount -t ext4 -o ro apex/com.android.foo/apex_payload.img apex/com.android.foo

// capex
$ unzip -o apex/com.android.foo.capex -d apex/com.android.foo
$ unzip -o apex/com.android.foo/original_apex -d apex/com.android.foo
$ sudo mount -t ext4 -o ro apex/com.android.foo/apex_payload.img apex/com.android.foo

镜像解包过程比较繁琐单一,可以编写相应的自动化脚本来完成这个过程。

用户态

假如用户态的数据,仅需关注 Native 堆栈,那么你该使用 GDBLLDB 来解析,若需要最完整的数据分析,从 Ramdump 中提取的 Core 文件则需要进行一些文件页的修补,如 JavaKotlin 的解析依赖 .dex.odex.apkvdex.jar 等文件,由于文件页在内存上并非常驻,因此文件页在 Ramdump 中并不完整的。

如何加载 Core 文件

$ core-parser -c 1709.core

应用开发者在 ROOT 的手机上可用如下方法获得目标进程的 Core 文件并进行加载。

# ./data/local/tmp/core-parser -p 1 -m arm64

Core 文件页修复

加载 Core 后,可以先查看 link_map 信息,相当于 GDBinfo sharedlibrary 命令,该命令主要是确认 Core 的调试入口信息是否完整,一般情况是执行文件的 PHDR 段的缺失。

core-parser> map
LINKMAP       REGION                   FLAGS NAME
WARN: Not found debug. You can try command exec.

我们可以通过 auxv 命令查看执行文件名。

core-parser> auxv | grep EXEC
    1f         AT_EXECFN  0x7ff0aacfde /system/bin/app_process64
加载执行文件
core-parser> exec app_process64
Mmap segment [5b1754f000, 5b17551000) app_process64 [0]
Mmap segment [5b17551000, 5b17552000) app_process64 [2000]

core-parser> map
NUM LINKMAP       REGION                   FLAGS  L_ADDR         NAME
  1 0x74ef15f0e0  [5b1754f000, 5b17551000)  r--   5b1754f000  /system/bin/app_process64 [*]
  2 0x74f0403200  [    ???   ,    ???    )  ---   74f02d2000  /system/bin/linker64
  3 0x74ef15f338  [    ???   ,    ???    )  ---   74f02d1000  [vdso]
  4 0x74ef15f590  [74e8969000, 74e8a28000)  r--   74e8969000  /system/lib64/libandroid_runtime.so [*]
  5 0x74ef15f7e8  [74d6244000, 74d6294000)  r--   74d6244000  /system/lib64/libbinder.so [*]
  6 0x74ef15fa40  [74d63d5000, 74d63de000)  r--   74d63d5000  /system/lib64/libcutils.so [*]
加载 so、dex 文件
core-parser> sysroot ./apex:./root
Mmap segment [74e8969000, 74e8a28000) ./root/system/lib64/libandroid_runtime.so [0]
Mmap segment [74e8a28000, 74e8b7e000) ./root/system/lib64/libandroid_runtime.so [bf000]
Read symbols[1726] (/system/lib64/libandroid_runtime.so)
... ...
Mmap segment [7432bd0000, 7432cf8000) ./apex/com.android.art/javalib/apache-xml.jar [0]
Mmap segment [74307bf000, 7431153000) ./root/system/framework/framework.jar [1a81000]
Mmap segment [7432cf8000, 7433200000) ./apex/com.android.art/javalib/core-oj.jar [0]
测试是否修补完整

可以使用 pclasstopbt 等指令进行测试,验证功能是否正常。

core-parser> class java.lang.String -f
[0x705f0f78]
public final class java.lang.String extends java.lang.Object {
  // Object instance fields:
    [0x00c] private int hash
    [0x008] private final int count

  // extends java.lang.Object
    [0x004] private transient int shadow$_monitor_
    [0x000] private transient java.lang.Class shadow$_klass_
}
core-parser>
保存修复后的 Core 文件
core-parser> fake core -r -o /tmp/1709.raw.core
FakeCore: saved [/tmp/1709.raw.core]

这个修复后的 Core 文件可用 GDB、LLDB 来解析 C、C++ 的堆栈及其它。

(lldb) bt
* thread #1, stop reason = signal SIGSTOP
  * frame #0: 0x00000074e414e2a0
    frame #1: 0x000000743376de64 libart.so`void art::Monitor::Lock<(art::LockReason)1>(art::Thread*) + 1300
    frame #2: 0x0000007433774330 libart.so`art::Monitor::MonitorEnter(art::Thread*, art::ObjPtr<art::mirror::Object>, bool) + 720
    frame #3: 0x000000743393a6a8 libart.so`artLockObjectFromCode + 40
    frame #4: 0x0000007433410e28 libart.so`art_quick_lock_object_no_inline + 56

Android 相关调试指令

具备打印 JavaKotlin 对象,查看类数据结构,还原 Java 堆栈,堆栈锁信息,解析堆栈其它调用信息,字节码反编译,统计分析,检索对象,转出 Hprof 文件等能力。

打印 Java、Kotlin 对象

该指令能够完成展示所有成员变量以及内存布局,检索该对象被谁持有。

core-parser> p 0x12c00098 -b -r 1
Size: 0x28
Object Name: java.util.HashMap
    [0x24] final float loadFactor = 1061158912.000000
    [0x20] int threshold = 0
    [0x1c] transient int size = 0
    [0x18] transient int modCount = 0
    [0x14] transient java.util.HashMap$Node[] table = 0x0
    [0x10] transient java.util.Set entrySet = 0x0
  // extends java.util.AbstractMap
    [0x0c] transient java.util.Collection values = 0x0
    [0x08] transient java.util.Set keySet = 0x0
  // extends java.lang.Object
    [0x04] private transient int shadow$_monitor_ = 0
    [0x00] private transient java.lang.Class shadow$_klass_ = 0x706235d8
Reference:
  --> 0x12c00088 java.util.HashSet
Binary:
12c00098: 00000000706235d8  0000000000000000  .5bp............
12c000a8: 0000000000000000  0000000000000000  ................
12c000b8: 3f40000000000000  00000000705d1828  ......@?(.]p....
查看 Java、Kotlin 类的结构

输出该类的继承、实现关系,含有的静态变量以及成员变量的偏移地址,该类的所有成员函数的 ArtMethod 地址。

core-parser> class java.util.HashMap
[0x706235d8]
public class java.util.HashMap extends java.util.AbstractMap {
  // Implements:
    java.util.Map
    java.lang.Cloneable
    java.io.Serializable

  // Class static fields:
    [0x23c] final static float DEFAULT_LOAD_FACTOR = 1061158912.000000
    [0x238] final static int UNTREEIFY_THRESHOLD = 0x6
    [0x234] final static int TREEIFY_THRESHOLD = 0x8
    [0x230] final static int MIN_TREEIFY_CAPACITY = 0x40
    [0x22c] final static int MAXIMUM_CAPACITY = 0x40000000
    [0x228] final static int DEFAULT_INITIAL_CAPACITY = 0x10
    [0x220] private final static long serialVersionUID = 0x507dac1c31660d1

  // Object instance fields:
    [0x024] final float loadFactor
    [0x020] int threshold
    [0x01c] transient int size
    [0x018] transient int modCount
    [0x014] transient java.util.HashMap$Node[] table
    [0x010] transient java.util.Set entrySet

  // extends java.util.AbstractMap
    [0x00c] transient java.util.Collection values
    [0x008] transient java.util.Set keySet

  // extends java.lang.Object
    [0x004] private transient int shadow$_monitor_
    [0x000] private transient java.lang.Class shadow$_klass_

  // Methods:
    [0x7073db88] public void java.util.HashMap.<init>()
    [0x7073dba8] public void java.util.HashMap.<init>(int)
    ... ...
}
ArtMethod 函数反编译
core-parser> method 0x7073dee8 --dex-dump --oat-dump
public boolean java.util.HashMap.isEmpty() [dex_method_idx=17884]
DEX CODE:
  0x7432ec2b28: 1052 168b                | iget v0, v1, Ljava/util/HashMap;.size:I // field@5771
  0x7432ec2b2c: 0039 0004                | if-nez v0, 0x7432ec2b34 //+4
  0x7432ec2b30: 1012                     | const/4 v0, #+1
  0x7432ec2b32: 0228                     | goto 0x7432ec2b36 //+2
  0x7432ec2b34: 0012                     | const/4 v0, #+0
  0x7432ec2b36: 000f                     | return v0
OAT CODE:
  [0x70b52fe0, 0x70b52ff0]
  0x70b52fe0: b9401c20 | ldr w0, [x1, #0x1c]
  0x70b52fe4: 7100001f | cmp w0, #0
  0x70b52fe8: 1a9f17e0 | cset w0, eq
  0x70b52fec: d65f03c0 | ret 
查看线程

* 的意思是当前 core-parser 调试环境下的线程号,当我们进行 btframe 时默认线程,可用 thread <Tid> 进行切换当前调试线程。

core-parser> t -a
 ID   TID    STATUS                          ADDRESS              NAME
*1    1709   Blocked                         0xb4000074eed63c00   "main" 
 2    1719   Runnable                        0xb40000741b800000   "Signal Catcher" 
 3    1720   Native                          0xb4000074eed65800   "perfetto_hprof_listener" 
 4    1723   Native                          0xb40000741b811800   "Jit thread pool worker thread 0" 
 5    1722   WaitingInMainDebuggerLoop       0xb40000740d8e1c00   "ADB-JDWP Connection Control Thread" 
 6    1724   WaitingForTaskProcessor         0xb40000740d8e0000   "HeapTaskDaemon" 
... ...
 ---  11865  NotAttachJVM
 ---  19153  NotAttachJVM
查看 Java 堆栈

该堆栈输出格式与 anr_trace.txttombstone 类似,两者的结合。

core-parser> bt
"main" sysTid=1709 Blocked
  | group="main" daemon=0 prio=5 target=0x0 uncaught_exception=0x0
  | tid=1 sCount=0 flags=0 obj=0x72beb5d8 self=0xb4000074eed63c00 env=0xb4000074eeddb200
  | stack=0x7ff02ae000-0x7ff02b0000 stackSize=0x7ff000 handle=0x74f04094f8
  | mutexes=0xb4000074eed64390 held=
  x0  0xb40000740d97643c  x1  0x0000000000000080  x2  0x0000000000000007  x3  0x0000000000000000  
  x4  0x0000000000000000  x5  0x0000000000000000  x6  0x0000000000000000  x7  0x000000009ad91228  
  x8  0x0000000000000062  x9  0x0000000000000000  x10 0x0000000000000032  x11 0x0000000000000033  
  x12 0x0000000000000003  x13 0x0000000000000033  x14 0x000000000000001c  x15 0x000001c7ddd068bc  
  x16 0x0000007433a0f770  x17 0x00000074e414e280  x18 0x00000074efda2000  x19 0xb40000740d976428  
  x20 0xb4000074eed63c00  x21 0xb40000740d97643c  x22 0x00000074332a718b  x23 0x00000074332afcde  
  x24 0x0000007433c15000  x25 0x0000007ff0aa8430  x26 0x0000000000000003  x27 0x00000000fffffffe  
  x28 0x00000074eefd0000  fp  0x0000007ff0aa8470  
  lr  0x000000743347e804  sp  0x0000007ff0aa8400  pc  0x00000074e414e2a0  pst 0x0000000060001000  
  Native: #0  00000074e414e2a0  syscall+0x20
  Native: #1  000000743347e800  art::Mutex::ExclusiveLock(art::Thread*)+0x140
  Native: #2  000000743376de60  void art::Monitor::Lock<(art::LockReason)1>(art::Thread*)+0x510
  Native: #3  000000743377432c  art::Monitor::MonitorEnter(art::Thread*, art::ObjPtr<art::mirror::Object>, bool)+0x2cc
  Native: #4  000000743393a6a4  artLockObjectFromCode+0x24
  JavaKt: #00  000000742032affa  com.android.server.alarm.AlarmManagerService.setImpl
  - waiting to lock <0x136c1c78> (a java.lang.Object) held by thread sysTid=1959
  JavaKt: #01  0000007420320720  com.android.server.alarm.AlarmManagerService$5.set
  JavaKt: #02  00000074324841b0  android.app.AlarmManager.setImpl
  JavaKt: #03  00000074324840ba  android.app.AlarmManager.setImpl
  JavaKt: #04  0000007432483e7c  android.app.AlarmManager.set
  JavaKt: #05  000000742061c958  com.android.server.job.controllers.TimeController.updateAlarmWithListenerLocked
  JavaKt: #06  000000742061c094  com.android.server.job.controllers.TimeController.checkExpiredDeadlinesAndResetAlarm
  JavaKt: #07  000000742061c792  com.android.server.job.controllers.TimeController.maybeStopTrackingJobLocked
  JavaKt: #08  00000074205fad0e  com.android.server.job.JobSchedulerService.stopTrackingJobLocked
  JavaKt: #09  00000074205fe31c  com.android.server.job.JobSchedulerService.onJobCompletedLocked
  JavaKt: #10  0000007420600c96  com.android.server.job.JobServiceContext.closeAndCleanupJobLocked
  JavaKt: #11  0000007420601234  com.android.server.job.JobServiceContext.handleFinishedLocked
  JavaKt: #12  0000007420600e4c  com.android.server.job.JobServiceContext.doCallbackLocked
  JavaKt: #13  0000007420600f44  com.android.server.job.JobServiceContext.doJobFinished
  JavaKt: #14  00000074205ffc9c  com.android.server.job.JobServiceContext$JobCallback.jobFinished
  JavaKt: #15  000000743256ee36  android.app.job.JobServiceEngine$JobHandler.handleMessage
  JavaKt: #16  0000007431e985a2  android.os.Handler.dispatchMessage
  JavaKt: #17  0000007431ebc42c  android.os.Looper.loopOnce
  JavaKt: #18  0000007431ebcb4e  android.os.Looper.loop
  JavaKt: #19  00000074202bfeb8  com.android.server.SystemServer.run
  JavaKt: #20  00000074202bf88a  com.android.server.SystemServer.main
  JavaKt: #21  0000000000000000  java.lang.reflect.Method.invoke
  JavaKt: #22  0000007430bc695a  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
  JavaKt: #23  0000007430bcca64  com.android.internal.os.ZygoteInit.main
分析堆栈特定帧

frame 指令可以展示指定的 JavaKtNative 帧的信息,选项类型为 -j-n 区分。

core-parser> f 2 -j
  JavaKt: #02  00000074324841b0  android.app.AlarmManager.setImpl(int, long, long, long, int, android.app.PendingIntent, android.app.AlarmManager$OnAlarmListener, java.lang.String, java.util.concurrent.Executor, android.os.WorkSource, android.app.AlarmManager$AlarmClockInfo)
  {
      Location: /system/framework/framework.jar
      art::ArtMethod: 0x713f29d8
      dex_pc_ptr: 0x74324841b0
      quick_frame: 0x7ff0aa8a20
      frame_pc: 0x7216bff8
      method_header: 0x7216bd1c

      DEX CODE:
      0x74324841a8: 1208 0022                | move-object/from16 v18, v34
      0x74324841ac: 1308 0023                | move-object/from16 v19, v35
      0x74324841b0: 0f78 27ec 0005           | invoke-interface/range {v5 .. v19}, void android.app.IAlarmManager.set(java.lang.String, int, long, long, long, int, android.app.PendingIntent, android.app.IAlarmListener, java.lang.String, android.os.WorkSource, android.app.AlarmManager$AlarmClockInfo) // method@10220
      {
          v0 = 0x1b66d328    v1 = w24    v2 = 0x138da128    v3 = 0x090732b6    
          v4 = 0x00000000    v5 = 0x1b66d328    v6 = 0x708a5618    v7 = w25    
          v8 = 0x090732b6    v9 = 0x00000000    v10 = w27    v11 = x27/hi    
          v12 = w28    v13 = x28/hi    v14 = w29    v15 = 0x00000000    
          v16 = w23    v17 = 0x9ad91228    v18 = 0x1a800620    v19 = 0x00000000    
          v20 = w23    v21 = w24    v22 = w25    v23 = w26    
          v24 = x26/hi    v25 = w27    v26 = x27/hi    v27 = w28    
          v28 = x28/hi    v29 = w29    v30 = 0x00000000    v31 = 0x138da128    
          v32 = 0x9ad91228    v33 = w22    v34 = 0x1a800620    v35 = 0x00000000
      }

      OAT CODE:
      0x7216bfd4: bd0043ff | str s31, [sp, #0x40]
      0x7216bfd8: f9402fe4 | ldr x4, [sp, #0x58]
      0x7216bfdc: b90037f7 | str w23, [sp, #0x34]
      0x7216bfe0: b9400020 | ldr w0, [x1]
      0x7216bfe4: d0ff9671 | adrp x17, 0x71439000
      0x7216bfe8: 91010231 | add x17, x17, #0x40
      0x7216bfec: f9404000 | ldr x0, [x0, #0x80]
      0x7216bff0: f9403800 | ldr x0, [x0, #0x70]
      0x7216bff4: f9400c1e | ldr x30, [x0, #0x18]
      0x7216bff8: d63f03c0 | blr x30
      0x7216bffc: 1400000b | b 0x7216c028
      0x7216c000: b9409a61 | ldr w1, [x19, #0x98]
      {
          x22 = 0x000000001a800638    x23 = 0x00000000138db6f0    x24 = 0x000000001b662de8    x25 = 0x0000000000000002    
          x26 = 0x00000000090732b6    x27 = 0xffffffffffffffff    x28 = 0x0000000000000000    fp = 0x0000000000000000    
          lr = 0x000000007216bffc
      }
  }
统计分析

类似 hprof 文件在 Android Stdio 上解析,查看各类型的 top 对象。

core-parser> top 5 -d -n --app
Address       Allocations       ShallowSize       NativeSize     ClassName
TOTAL             2233407        133558996          24992325
------------------------------------------------------------
0x70d36350             53             2438          19869347     android.graphics.Bitmap
0x70d3c110           2695            45815           2695000     android.os.BinderProxy
0x9ad4d358            509            18324            254500     com.android.server.wm.ActivityRecord$Token
0x70d8bca8            471            15072            241152     android.view.SurfaceControl$Transaction
0x9ad5ec88            453            18120            226500     com.android.server.wm.WindowContainer$RemoteToken
提取 Hprof 文件

time 是个耗时统计指令,可有可无,如该 Core 共扫描了 5011368 消耗了 4 多秒,当然还可以加 --quick 的选项来提高一倍速,但有 hprof 无法在 Android Stdio 上打开的风险。

core-parser> time hprof /tmp/1709.hprof
hprof: heap dump first prepare...
hprof: heap dump "/tmp/1709.hprof" starting...
hprof: heap dump completed, scan objects (5011368).
hprof: saved [/tmp/1709.hprof].

Total time: 4.128580 (seconds)

hprof.png

Dex 文件脱壳

dex 命令可以查看安卓应用程序加载了哪些 dex 文件,并用其命令进行脱壳。

core-parser> dex
NUM DEXCACHE    REGION                   FLAGS NAME
  1 0x6f38e0d8  [7b069c053000, 7b069c561000)  r--  /data/dalvik-cache/x86_64/system@framework@boot.vdex [*]
 ... ...
 17 0x12d92238  [7b06874f6000, 7b0687733000)  r--  /data/app/penguin.opencore.tester-KIK9ndXUSAmLHpFOvWQPHA==/oat/x86_64/base.vdex [*]
 18 0x12d93ef0  [7b06874f6000, 7b0687733000)  r--  /data/app/penguin.opencore.tester-KIK9ndXUSAmLHpFOvWQPHA==/oat/x86_64/base.vdex [*]
 19 0x12d232f0  [7b06874f6000, 7b0687733000)  r--  /data/app/penguin.opencore.tester-KIK9ndXUSAmLHpFOvWQPHA==/oat/x86_64/base.vdex [*]
core-parser> dex --app -d vdex
Saved [vdex/base.vdex_0x32b9f22b].
Saved [vdex/base.vdex_0x4edd148b].
Saved [vdex/base.vdex_0xc65c568b].
core-parser> sh jadx -Pdex-input.verify-checksum=no vdex/base.vdex_0x32b9f22b
INFO  - loading ...
INFO  - processing ...
INFO  - done
core-parser> sh jadx -Pdex-input.verify-checksum=no vdex/base.vdex_0x4edd148b
INFO  - loading ...
INFO  - processing ...
INFO  - done
core-parser> sh jadx vdex/base.vdex_0xc65c568b
INFO  - loading ...
INFO  - processing ...
INFO  - done
core-parser>

Inkedopencore_LI.jpg

厂商适配

OpenCoreAnalysisKit 项目以 Android Stdio 上能下载到的 Google APIs 模拟器镜像为基础实现的项目。若对 ART 相关代码改动较少的地方,一般都能兼容到,改动较大需厂商修改所需的类型结构的成员偏移地址即可,后续会支持符号表解析。

systemimage.png

技术交流群

本文仅介绍常用的能力,对于其它功能、插件开发等,由于篇幅有限不在此介绍了,建了相关技术交流群(进群可以私信我),有问题可在群里进行讨论,欢迎各路神仙加入这个比较冷门的话题。Topic:Android 软件逆向分析、Android 稳定性、Linux 稳定性、Core 文件调试与分析、内存、虚拟机器等。笔者不才诸多研究不深入,不可能回答上太多的问题。这段时间经常熬夜爆肝更新,项目累计码了约 3 万行代码,得歇会。希望这个开源项目能够帮到你。