基于安卓墓碑文件制作 FakeCore 原理

前言

这里墓碑指的是在 Android 下应用程序发生了 Native Crash 的情况下由系统自动生成的文件。具体的墓碑文件格式以及 Coredump 文件的核心部分可以翻阅之前的文章 《如何理解Native Crash问题》

原理

部件 依赖
ELF Header 唯一需要计算的参数 "Number of program headers"。
Program Headers 解析 memory map 获得进程所有 vma 数量,以及 Fake VMA 和 PT_NOTE 组成。
Thread Registers 解析 Crash thread registers 即可获得相应的内容。
AUXV 实际上是个 Fake AUXV,见具体内容。
Segment(PT_LOAD) 通过解析 memory near xx 获得相应的内存值,墓碑文件没有记录的内存地址则补 0 填充即可。

图中右侧为一个墓碑文件的基本内容,左侧是一个 Coredump 文件的基本组成,绝大部分信息均可来自墓碑文件,但要想 GDB、LLDB 上能够进行调试,加载符号表文件,我们需要 Fake AUXV 以及 Fake VMA 来得到一个 FakeCore 的文件。

FakeCore 文件格式

FAKE AUXV

像 GDB、LLDB 这种调试工具,要获得该进程依赖的动态链接库信息所依赖的基本参数。

数值 类型 描述 用途
3 AT_PHDR Program headers for program 找到执行程序的 Program Headers 首地址,这里我们让该地址指向 FAKE PHDR 即可
4 AT_PHENT Size of program header entry 遍历的步长,大小为 sizeof(Elfxx_Phdr)
5 AT_PHNUM Number of program headers 数量为 1 即可,具体原因见 FAKE PHDR。

FAKE PHDR

调试工具得到 AT_PHDR 地址下一步的目的是检索 Program headers 表得到 PT_DYNAMIC 的位置。

ini 复制代码
typedef struct {
    uint32_t   p_type;
    uint32_t   p_flags;
    Elf64_Off  p_offset;
    Elf64_Addr p_vaddr;
    Elf64_Addr p_paddr;
    uint64_t   p_filesz;
    uint64_t   p_memsz;
    uint64_t   p_align;
} Elf64_Phdr;
成员
p_type PT_DYNAMIC
p_flags PF_R | PF_W
p_vaddr 该地址指向 FAKE DYNAMIC 即可

FAKE DYNAMIC

调试工具得到 PT_DYNAMIC 地址下一步的目的是检索 DEBUG 地址,比如我们用 readelf -d 来解析 ELF 可执行文件,可以看到 DYNAMIC 上的内容,其中有一项即为 DEBUG,当前地址为 0x0,实际上程序真正运行时会生成一个真正的 DYNAMIC 内存,其中 DEBUG 地址指向 r_debug 地址。

yaml 复制代码
# llvm-readelf -d app_process64
Dynamic section at offset 0x30c0 contains 41 entries:
  Tag                Type              Name/Value
  0x0000000000000001 (NEEDED)          Shared library: [libandroid_runtime.so]
  0x0000000000000001 (NEEDED)          Shared library: [libbinder.so]
  0x0000000000000001 (NEEDED)          Shared library: [libcutils.so]
  0x0000000000000001 (NEEDED)          Shared library: [libhidlbase.so]
  0x0000000000000001 (NEEDED)          Shared library: [liblog.so]
  0x0000000000000001 (NEEDED)          Shared library: [libnativeloader.so]
  0x0000000000000001 (NEEDED)          Shared library: [libsigchain.so]
  0x0000000000000001 (NEEDED)          Shared library: [libutils.so]
  0x0000000000000001 (NEEDED)          Shared library: [libwilhelm.so]
  0x0000000000000001 (NEEDED)          Shared library: [libc++.so]
  0x0000000000000001 (NEEDED)          Shared library: [libc.so]
  0x0000000000000001 (NEEDED)          Shared library: [libm.so]
  0x0000000000000001 (NEEDED)          Shared library: [libdl.so]
  0x000000000000001e (FLAGS)           BIND_NOW 
  0x000000006ffffffb (FLAGS_1)         NOW PIE 
  0x0000000000000015 (DEBUG)           0x0
  0x0000000060000011 (ANDROID_RELA)    0xe38
  0x0000000060000012 (ANDROID_RELASZ)  18 (bytes)
  0x0000000000000009 (RELAENT)         24 (bytes)
  0x0000000000000024 (RELR)            0xe50
  0x0000000000000023 (RELRSZ)          24 (bytes)
  0x0000000000000025 (RELRENT)         8 (bytes)
  0x0000000000000017 (JMPREL)          0xe68
  0x0000000000000002 (PLTRELSZ)        1152 (bytes)
  0x0000000000000003 (PLTGOT)          0x3360
  0x0000000000000014 (PLTREL)          RELA
  0x0000000000000006 (SYMTAB)          0x310
  0x000000000000000b (SYMENT)          24 (bytes)
  0x0000000000000005 (STRTAB)          0x8a4
  0x000000000000000a (STRSZ)           1427 (bytes)
  0x000000006ffffef5 (GNU_HASH)        0x888
  0x0000000000000020 (PREINIT_ARRAY)   0x3000
  0x0000000000000021 (PREINIT_ARRAYSZ) 16 (bytes)
  0x0000000000000019 (INIT_ARRAY)      0x3010
  0x000000000000001b (INIT_ARRAYSZ)    16 (bytes)
  0x000000000000001a (FINI_ARRAY)      0x3020
  0x000000000000001c (FINI_ARRAYSZ)    16 (bytes)
  0x000000006ffffff0 (VERSYM)          0x7c0
  0x000000006ffffffe (VERNEED)         0x824
  0x000000006fffffff (VERNEEDNUM)      3
  0x0000000000000000 (NULL)            0x0
arduino 复制代码
typedef struct elf64_dynamic {
    uint64_t      d_type;
    uint64_t      d_val;
} Elf64_dynamic;

struct r_debug {
    int r_version;
    struct link_map *r_map;
};
成员
d_type DT_DEBUG
d_val FAKE_DYNAMIC 地址 + sizeof(Elf64_dynamic)
r_version 1 (SYSV) 标准
r_map 该地址指向 FAKE LINK MAP 即可

该部分才是 GDB、LLDB 找到进程依赖动态库的链表,而我们不需要全部链接库,因为墓碑文件本身记录的信息非常有限,只要 Crash thread backtrace 中出现的链接库即可,如下才是我们需要的。

sql 复制代码
backtrace:
      #00 pc 00000000000edcc8  /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
      #01 pc 0000000000018a4c  /system/lib64/libutils.so (android::Looper::pollInner(int)+188)
      #02 pc 000000000001892c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+124)
      #03 pc 000000000018d03c  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) 
      #04 pc 0000000000217094  /system/framework/arm64/boot-framework.oat (art_jni_trampoline+116)
... ...
arduino 复制代码
struct link_map {
    uint64_t      l_addr;
    char *        l_name;
    uint64_t      l_ld;
    struct link_map *l_next, *l_prev;
};
成员
l_addr 动态链接库的加载地址,可从 memory map 中获得。
l_name 文件路径的字符串均在 FAKE STRTAB 即可。
l_ld 关乎 GDB、LLDB 能否正常解析堆栈的核心参数,DYNAMIC 虚拟地址 + 加载地址
l_next 下一个 link_map 地址,我们可以像数组一样堆叠即可。
l_prev 上一个 link_map 地址。

关于 l_ld 的值来源,依赖源 ELF 文件解析 DYNAMIC 获得虚拟地址。

FAKE STRTAB

此部分存放所有字符串,如前面 link_map 中的动态链接库地址。

FakeCore 实验效果

例如在手机中主动 kill -11 一个进程来获得相应的 tombstone 文件。

shell 复制代码
# kill -11 `pidof com.android.settings`

# ./data/tomb2core --input=/data/tombstones/tombstone_17 --output=/data/tombstones/tombstone_17.core
less 复制代码
// 注意制作 link_map 要在头部塞入一个 fake,因为 GDB 会认为第一个是进程的执行文件给过滤掉了。
art-parser> map
0x2002000 [0x2000000, 0x2001000) [fake] [*] 
0x2002028 [0x7bcdf53000, 0x7bce0b4000) [/apex/com.android.art/lib64/libart.so] [empty]
0x2002050 [0x7c6d6cb000, 0x7c6d719000) [/apex/com.android.runtime/lib64/bionic/libc.so] [empty]
0x2002078 [0x71c98000, 0x71eae000) [/system/framework/arm64/boot-framework.oat] [empty]
0x20020a0 [0x7189e000, 0x7193a000) [/system/framework/arm64/boot.oat] [empty]
0x20020c8 [0x7c6100a000, 0x7c610e9000) [/system/lib64/libandroid_runtime.so] [empty]
0x20020f0 [0x7c5fb5d000, 0x7c5fb6c000) [/system/lib64/libutils.so] [empty]
bash 复制代码
(gdb) core-file tombstone_17.core

(gdb) info sharedlibrary 
warning: Could not load shared library symbols for 6 libraries, e.g. /apex/com.android.art/lib64/libart.so.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
From                To                  Syms Read   Shared Object Library
                                        No          /apex/com.android.art/lib64/libart.so
                                        No          /apex/com.android.runtime/lib64/bionic/libc.so
                                        No          /system/framework/arm64/boot-framework.oat
                                        No          /system/framework/arm64/boot.oat
                                        No          /system/lib64/libandroid_runtime.so
                                        No          /system/lib64/libutils.so
bash 复制代码
(gdb) set sysroot symbols/
Reading symbols from symbols/apex/com.android.art/lib64/libart.so...
Reading symbols from symbols/apex/com.android.runtime/lib64/bionic/libc.so...
Reading symbols from symbols/system/framework/arm64/boot-framework.oat...
Reading symbols from symbols/system/framework/arm64/boot.oat...
Reading symbols from symbols/system/lib64/libandroid_runtime.so...
Reading symbols from symbols/system/lib64/libutils.so...
rust 复制代码
(gdb) bt
#0  __epoll_pwait () at /bionic/libc/syscalls-arm64/gen/syscalls-arm64.S:2370
#1  0x0000007c5fb75a50 in android::Looper::pollInner (this=<optimized out>, timeoutMillis=19008) at system/core/libutils/Looper.cpp:249
#2  0x0000007c5fb75930 in android::Looper::pollOnce (this=0xb400007bd2ad4100, timeoutMillis=19008, outFd=0x0, outEvents=0x0, outData=0x0) at system/core/libutils/Looper.cpp:217
#3  0x0000007c61197040 in android::Looper::pollOnce (this=0xfffffffffffffffc, timeoutMillis=<optimized out>) at system/core/libutils/include/utils/Looper.h:270
#4  android::NativeMessageQueue::pollOnce (env=0xb400007bd2a8ce00, pollObj=<optimized out>, timeoutMillis=<optimized out>, this=<optimized out>) at frameworks/base/core/jni/android_os_MessageQueue.cpp:125
#5  android::android_os_MessageQueue_nativePollOnce (env=0xb400007bd2a8ce00, obj=<optimized out>, ptr=-5476376615066225088, timeoutMillis=-811281936) at frameworks/base/core/jni/android_os_MessageQueue.cpp:225
#6  0x0000000071eaf098 in android::os::MessageQueue::nativePollOnce (this=...)
   from symbols/system/framework/arm64/boot-framework.oat
#7  0x00000000722bab8c in android::os::MessageQueue::next (this=...) at android/os/MessageQueue.java:341
#8  0x00000000722b82c4 in android::os::Looper::loopOnce (me=..., ident=<optimized out>, thresholdOverride=<optimized out>) at android/os/Looper.java:176
#9  0x00000000722b81d0 in android::os::Looper::loop ()
   from symbols/system/framework/arm64/boot-framework.oat
#10 0x0000000072041448 in android::app::ActivityThread::main (args=...) at android/app/ActivityThread.java:8667
#11 0x0000007bce163c84 in art_quick_invoke_static_stub () at art/runtime/arch/arm64/quick_entrypoints_arm64.S:688
#12 0x0000007bce1a6b44 in art::ArtMethod::Invoke (this=0x710e6b58, self=0xb400007bd2a42c00, args=0x16, args_size=<optimized out>, result=0x7fcfa4d7b0, shorty=0x7bcd4e00ca "") at art/runtime/art_method.cc:425
#13 0x0000007bce59e4e8 in art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*) [clone .__uniq.245181933781456475607640333933569312899] (soa=..., method=0x710e6b58, arg_array=0x7fcfa4d820, result=0x7fcfa4d7b0, shorty=0x7bcd4e00ca "") at art/runtime/reflection.cc:458
#14 art::(anonymous namespace)::InvokeMethodImpl(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArtMethod*, art::ObjPtr<art::mirror::Object>, art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object> >, char const**, art::JValue*) [clone .__uniq.245181933781456475607640333933569312899] (soa=..., m=0x710e6b58, np_method=0x710e6b58, receiver=..., objects=..., result=0x7fcfa4d7b0,
    shorty=<optimized out>) at art/runtime/reflection.cc:493
#15 art::InvokeMethod<(art::PointerSize)8> (soa=..., javaMethod=<optimized out>, javaReceiver=<optimized out>, javaArgs=<optimized out>, num_frames=<optimized out>) at art/runtime/reflection.cc:772
#16 0x0000007bce5165b4 in art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) [clone .__uniq.165753521025965369065708152063621506277] (env=<optimized out>, javaMethod=<optimized out>,
    javaReceiver=<optimized out>, javaArgs=<optimized out>) at art/runtime/native/java_lang_reflect_Method.cc:86
#17 0x000000007194057c in java::lang::Class::getDeclaredMethodInternal (this=...)
   from symbols/system/framework/arm64/boot.oat
#18 0x0000000072495388 in com::android::internal::os::RuntimeInit$MethodAndArgsCaller::run (this=...) at com/android/internal/os/RuntimeInit.java:565
#19 0x000000007249ff78 in com::android::internal::os::ZygoteInit::main (argv=...) at com/android/internal/os/ZygoteInit.java:1081
#20 0x0000007bce163c84 in art_quick_invoke_static_stub () at art/runtime/arch/arm64/quick_entrypoints_arm64.S:688
#21 0x0000007bce1a6b44 in art::ArtMethod::Invoke (this=0x7116f378, self=0xb400007bd2a42c00, args=0x16, args_size=<optimized out>, result=0x7fcfa4dc48, shorty=0x7bcba45177 "") at art/runtime/art_method.cc:425
#22 0x0000007bce59ed6c in art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*) [clone .__uniq.245181933781456475607640333933569312899] (soa=..., method=0x7116f378, arg_array=0x7fcfa4dbd0, result=0x7fcfa4dc48, shorty=0x7bcba45177 "") at art/runtime/reflection.cc:458
#23 art::InvokeWithVarArgs<art::ArtMethod*> (soa=..., obj=0x0, method=0x7116f378, args=...) at art/runtime/reflection.cc:550
#24 0x0000007bce59f344 in art::InvokeWithVarArgs<_jmethodID*> (soa=..., obj=0x0, mid=<optimized out>, args=...) at art/runtime/reflection.cc:565
#25 0x0000007bce4690f8 in art::JNI<true>::CallStaticVoidMethodV (env=<optimized out>, mid=0x7116f378, args=...) at art/runtime/jni/jni_internal.cc:1963
#26 0x0000007c610eacac in _JNIEnv::CallStaticVoidMethod (this=<optimized out>, clazz=<optimized out>, methodID=<optimized out>) at libnativehelper/include_jni/jni.h:779
#27 0x0000007c610f7438 in android::AndroidRuntime::start (this=0x7fcfa4e020, className=0x55d055a394 "", options=..., zygote=true) at frameworks/base/core/jni/AndroidRuntime.cpp:1350
#28 0x00000055d055b590 in ?? ()

这样许多计算问题以及反编译问题都可在 GDB、LLDB 上完成,节省许多计算量,其中 tomb2core 也是离线内存分析项目中的组成部分。

相关推荐
福柯柯14 分钟前
Android ContentProvider的使用
android·contenprovider
不想迷路的小男孩14 分钟前
Android Studio 中Palette跟Component Tree面板消失怎么恢复正常
android·ide·android studio
餐桌上的王子16 分钟前
Android 构建可管理生命周期的应用(一)
android
菠萝加点糖20 分钟前
Android Camera2 + OpenGL离屏渲染示例
android·opengl·camera
用户20187928316730 分钟前
🌟 童话:四大Context徽章诞生记
android
yzpyzp38 分钟前
Android studio在点击运行按钮时执行过程中输出的compileDebugKotlin 这个任务是由gradle执行的吗
android·gradle·android studio
aningxiaoxixi1 小时前
安卓之service
android
TeleostNaCl2 小时前
Android 应用开发 | 一种限制拷贝速率解决因 IO 过高导致系统卡顿的方法
android·经验分享
用户2018792831672 小时前
📜 童话:FileProvider之魔法快递公司的秘密
android
vocal5 小时前
【我的安卓第一课】Android 多线程与异步通信机制(1)
android