基于安卓墓碑文件制作 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 也是离线内存分析项目中的组成部分。

相关推荐
uwvwko2 小时前
BUUCTF——web刷题第一页题解
android·前端·数据库·php·web·ctf
fzxwl2 小时前
隆重推荐(Android 和 iOS)UI 自动化工具—Maestro
android·ui·ios
LittleLoveBoy4 小时前
踩坑:uiautomatorviewer.bat 打不开
android
居然是阿宋5 小时前
Android核心系统服务:AMS、WMS、PMS 与 system_server 进程解析
android
CGG927 小时前
【单例模式】
android·java·单例模式
kp000008 小时前
PHP弱类型安全漏洞解析与防范指南
android·开发语言·安全·web安全·php·漏洞
编程乐学(Arfan开发工程师)13 小时前
06、基础入门-SpringBoot-依赖管理特性
android·spring boot·后端
androidwork13 小时前
使用 Kotlin 和 Jetpack Compose 开发 Wear OS 应用的完整指南
android·kotlin
繁依Fanyi14 小时前
Animaster:一次由 CodeBuddy 主导的 CSS 动画编辑器诞生记
android·前端·css·编辑器·codebuddy首席试玩官
奔跑吧 android17 小时前
【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
android·bluetooth·bt·aosp13·storagemodule