简介
2024 年初画了个饼,重构整套开源的离线内存文件分析套件,时间也刚好过去了半年,该项目仅对 Android 8 ~ Android 15
的arm64
、arm
、x86_64
、x86
、riscv64
体系的 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 构建的项目,支持插件开发。 |
jadx | Java 字节码反编译工具,分析逆向 APK 程序时可选用配合 Dex 脱壳。 |
ghidra | 美国国家安全局(NSA)开源的逆向工程工具套件, 可作为 IDA 使用的替代品。 |
frida | 一个强大的、跨平台的动态代码插桩框架 |
技术体系结构
项目编译
OpenCoreAnalysisKit 不支持在 Window 上使用,Window 群体可 ndk 编译 Android 端上使用的程序,该项目编译依赖 cmake
,clang
,ndk
,需要确保安装了如下版本以及以上。
项目 | 建议版本 | 地址 |
---|---|---|
cmake | 3.21.1+ | cmake.org/download |
clang | 12.0.0+ | github.com/llvm/llvm-p… |
ndk | r22+ | 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
Platform | Path |
---|---|
Emulator | output/aosp/<BUILD_TYPE>/android/x86_64/bin/core-parser output/aosp/<BUILD_TYPE>/android/x86/bin/core-parser |
Phone | output/aosp/<BUILD_TYPE>/android/arm64-v8a/bin/core-parser output/aosp/<BUILD_TYPE>/android/armeabi-v7a/bin/core-parser |
Ubuntu | output/aosp/<BUILD_TYPE>/linux/bin/core-parser |
MacOS | output/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
目录下。该目录下有 arm64
、x86_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
插件支持的功能有 core
,zram
,shmem
,binder
,page_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
用到的工具有 simg2img
、lpunpack
,这些工具都能在 AOSP 的代码获得。
$ simg2img super.img raw_super.img
$ mkdir out
$ lpunpack raw_super.img out
完成这一步后 out
目录下,会有对应的 system.img
,vendor.img
以及其它,按需进行挂载。接下来挂载 system.img
并解包 apex
镜像。
$ mkdir root
$ sudo mount -t erofs out/system_a.img root -o loop
成功挂载 system.img
后,会看到 root/system/apex/
目录下有 apex
、capex
的压缩包文件。
// 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 堆栈,那么你该使用 GDB
、LLDB
来解析,若需要最完整的数据分析,从 Ramdump 中提取的 Core 文件则需要进行一些文件页的修补,如 Java
,Kotlin
的解析依赖 .dex
、.odex
、.apk
、vdex
、.jar
等文件,由于文件页在内存上并非常驻,因此文件页在 Ramdump
中并不完整的。
如何加载 Core 文件
$ core-parser -c 1709.core
应用开发者在 ROOT 的手机上可用如下方法获得目标进程的 Core 文件并进行加载。
# ./data/local/tmp/core-parser -p 1 -m arm64
Core 文件页修复
加载 Core 后,可以先查看 link_map
信息,相当于 GDB
的 info 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]
测试是否修补完整
可以使用 p
、class
、top
、bt
等指令进行测试,验证功能是否正常。
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 相关调试指令
具备打印 Java
、Kotlin
对象,查看类数据结构,还原 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
调试环境下的线程号,当我们进行 bt
、frame
时默认线程,可用 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.txt
、tombstone
类似,两者的结合。
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
指令可以展示指定的 JavaKt
、Native
帧的信息,选项类型为 -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)
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>
厂商适配
OpenCoreAnalysisKit
项目以 Android Stdio
上能下载到的 Google APIs
模拟器镜像为基础实现的项目。若对 ART 相关代码改动较少的地方,一般都能兼容到,改动较大需厂商修改所需的类型结构的成员偏移地址即可,后续会支持符号表解析。
技术交流群
本文仅介绍常用的能力,对于其它功能、插件开发等,由于篇幅有限不在此介绍了,建了相关技术交流群(进群可以私信我
),有问题可在群里进行讨论,欢迎各路神仙加入这个比较冷门的话题。Topic:Android 软件逆向分析、Android 稳定性、Linux 稳定性、Core 文件调试与分析、内存、虚拟机器等。笔者不才诸多研究不深入,不可能回答上太多的问题。这段时间经常熬夜爆肝更新,项目累计码了约 3 万行代码,得歇会。希望这个开源项目能够帮到你。