资讯详情

Native Crash的一切

作者:

本文由作者授权发表。

正文从下面开始。


1

常见的Native Crash类型

SIGSEGV

SEGV_MAPERR

地址不在 /proc/self/maps 映射中

SEGV_ACCERR

没有访问权限

SEGV_MTESERR

MTE特有类型

SIGABRT

程序主动退出,常见调用函数abort(),raise()等

SIGILL

ILL_ILLOPC

非法操作码(opcode)

ILL_ILLOPN

非法操作数(operand)

ILL_ILLADR

非法寻址

ILL_ILLTRP

非法trap,如_builtintrap()主动崩溃

ILL_PRVOPC

非法特权操作码(privileged opcode)

ILL_PRVREG

非法特权寄存器(privileged register)

ILL_COPROC

协处理器错误

ILL_BADSTK

内堆栈错误

SIGBUS

BUS_ADRALN

访问地址不对齐

BUS_ADRERR

访问不存在的物理地址

BUS_OBJERR

特定对象的硬件错误

SIGFPE

FPE_INTDIV

整数除以0

FPE_INTOVF

整数溢出

FPE_FLTDIV

浮点数除以0

FPE_FLTOVF

浮点数上溢(overflow)

FPE_FLTUND

浮点数下溢(underflow)

FPE_FLTRES

浮点数结果不准确

FPE_FLTINV

无效浮点操作

FPE_FLTSUB

越界

2

Android日志

当程序发生时 Native Crash 错误,Android 输出日志 log crash buffer 所以我们通过了

adb logcat -b crash 抓取相应的错误报告,日志本身可以提供有限的信息,只是错误的堆栈和当前线程的寄存器信息。

--------- beginning of crash

06-07 01:53:32.465 12027 12027 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

06-07 01:53:32.465 12027 12027 F DEBUG : Revision: '0'

06-07 01:53:32.466 12027 12027 F DEBUG : ABI: 'arm64'

06-07 01:53:32.466 12027 12027 F DEBUG : Timestamp: 2022-06-07 01:53:32.033409857 0800

06-07 01:53:32.466 12027 12027 F DEBUG : Process uptime: 0s

06-07 01:53:32.466 12027 12027 F DEBUG : Cmdline: mediaserver64

06-07 0:53:32.466 12027 12027 F DEBUG   : pid: 1139, tid: 11981, name: NPDecoder  >>> mediaserver64 <<<

06-07 01:53:32.466 12027 12027 F DEBUG   : uid: 1013

06-07 01:53:32.466 12027 12027 F DEBUG   : tagged_addr_ctrl: 0000000000000001

06-07 01:53:32.466 12027 12027 F DEBUG   : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

06-07 01:53:32.466 12027 12027 F DEBUG   :     x0  79748c5e568e2ddc  x1  0000007ca13c3618  x2  0000000000000000  x3  0000007ca1291000

06-07 01:53:32.466 12027 12027 F DEBUG   :     x4  0000000001909705  x5  0000000000000000  x6  0000007c02d88808  x7  b60625655bf0252f

06-07 01:53:32.467 12027 12027 F DEBUG   :     x8  0000000000000080  x9  0000007ca126fed7  x10 0000000000000006  x11 0000007bfd0a81fc

06-07 01:53:32.467 12027 12027 F DEBUG   :     x12 9ef8a95ca9649dbe  x13 e44782d5ac38720e  x14 0000007bfd0a8030  x15 0000001e56307b5c

06-07 01:53:32.467 12027 12027 F DEBUG   :     x16 0000007c95dfdb70  x17 0000007c9844f118  x18 0000007bfaa28000  x19 b400007c13c246d0

06-07 01:53:32.467 12027 12027 F DEBUG   :     x20 0000007c02d88730  x21 b400007c13c67c00  x22 0000000000000415  x23 0000007c02d89000

06-07 01:53:32.467 12027 12027 F DEBUG   :     x24 0000000000000002  x25 b400007c13c246d0  x26 b400007c13c67c00  x27 0000007c02d89000

06-07 01:53:32.467 12027 12027 F DEBUG   :     x28 0000007ca13c2c28  x29 0000007c02d886f0

06-07 01:53:32.467 12027 12027 F DEBUG   :     lr  0000007c02d886f0  sp  0000007c02d886d0  pc  0000007c02d886f0  pst 0000000080001000

06-07 01:53:32.467 12027 12027 F DEBUG   : backtrace:

06-07 01:53:32.467 12027 12027 F DEBUG   :       #00 pc 00000000000f86f0  [anon:stack_and_tls:11981]

当只有日志堆栈不能进行更详细的分析时,我们还需要程序的部分内存信息以及寄存器信息,而Android 的错误机制会相应的会生成一份 tombstone 文件保存到 /data/tombstones/tombstone_xx ,对于没有 Root 权限的机器则可以通过 adb bugreport 抓取出 tombstone 文件。

3

Tombstone 

tombstone 文件保存的信息有错误程序的体系结构,通俗的说 arm、arm64 等,发生时间点,程序名,错误类型,错误程序的进程号、线程号,错误现场寄存器,堆栈和部分寄存器地址附近的内存信息,程序内存映射表 /proc/self/maps ,FD 信息以及发生错误时该程序输出的日志。

ABI: 'arm64'   【 arm64的程序 】

Timestamp: 2022-06-07 01:53:32.033409857+0800  【 发生错误的时间戳 】

Process uptime: 0s

Cmdline: mediaserver64   【 程序名 】

pid: 1139, tid: 11981, name: NPDecoder  >>> mediaserver64 <<<  【 进程号、线程号 】

uid: 1013

signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

【 错误类型是 SIGSEGV,子类是 SEGV_ACCERR,错误地址0x7c02d886f0 】

SIGSEGV 也是我们最常见的 Native Crash 类型,大部分时候我们称其为段错误,而错误意思是在 PC=0x7c02d886f0 上发生拒绝访问的段错误。

x0  79748c5e568e2ddc  x1  0000007ca13c3618  x2  0000000000000000  x3  0000007ca1291000

x4  0000000001909705  x5  0000000000000000  x6  0000007c02d88808  x7  b60625655bf0252f

x8  0000000000000080  x9  0000007ca126fed7  x10 0000000000000006  x11 0000007bfd0a81fc

x12 9ef8a95ca9649dbe  x13 e44782d5ac38720e  x14 0000007bfd0a8030  x15 0000001e56307b5c

x16 0000007c95dfdb70  x17 0000007c9844f118  x18 0000007bfaa28000  x19 b400007c13c246d0

x20 0000007c02d88730  x21 b400007c13c67c00  x22 0000000000000415  x23 0000007c02d89000

x24 0000000000000002  x25 b400007c13c246d0  x26 b400007c13c67c00  x27 0000007c02d89000

x28 0000007ca13c2c28  x29 0000007c02d886f0

lr  0000007c02d886f0  sp  0000007c02d886d0  pc  0000007c02d886f0  pst 0000000080001000

backtrace:

#00 pc 00000000000f86f0  [anon:stack_and_tls:11981]   【PC刚好落在线程栈地址上】

这种情况很少见,虽然它只有的一条堆栈,并不代表程序是从这里开始运行,出现的这种情况仅仅 unwind 无法正确的回溯。

我们是可以通过栈地址空间内存进行恢复调用栈,用户态主线程栈(红色部分)结构如下:

而线程栈位于 mmap segmemt上,我们可以在 /proc/self/maps 上找到该线程栈的地址空间范围。

0000007c'02c90000-0000007c'02d8bfff rw-         0     fc000  [anon:stack_and_tls:11981]

大多数 arm64 的 Linux Android 程序,它的线程调用栈结构样例如下:

 

//  bionic/libc/arch-arm64/bionic/__bionic_clone.S#include <private/bionic_asm.h>

// pid_t __bionic_clone(int flags, void* child_stack, pid_t* parent_tid, void* tls, pid_t* child_tid, int (*fn)(void*), void* arg);

ENTRY_PRIVATE(__bionic_clone)
    # Push 'fn' and 'arg' onto the child stack.
    stp     x5, x6, [x1, #-16]!

    # Make the system call.
    mov     x8, __NR_clone
    svc     #0

    # Are we the child?
    cbz     x0, .L_bc_child

    # Set errno if something went wrong.
    cmn     x0, #(MAX_ERRNO + 1)
    cneg    x0, x0, hi
    b.hi    __set_errno_internal

    ret

.L_bc_child:
    # We're in the child now. Set the end of the frame record chain.
    mov     x29, #0
    # Setting x30 to 0 will make the unwinder stop at __start_thread.
    mov     x30, #0
    # Call __start_thread with the 'fn' and 'arg' we stored on the child stack.
    ldp     x0, x1, [sp], #16
    b       __start_thread
END(__bionic_clone)

NOTE_GNU_PROPERTY()

tombstone 会记录当前有效地址的寄存器附近内存信息,大小为0x100,这个可以修改文件

system/core/debuggerd/libdebuggerd/utility.cpp 中的宏定义 MEMORY_BYTES_TO_DUMP 参数

像这种一条堆栈的情况下,栈的内存信息配合下边的映射表可以帮助到我们对栈进行恢复。

memory near x1 (/system/lib64/libstagefright.so):

0000007ca13c35f0 0000000000000000 0000000000000000  ................

0000007ca13c3600 0000000000000000 0000000000000000  ................

0000007ca13c3610 0000000000000000 0000007ca132326c  ........l22.|...

0000007ca13c3620 0000007ca1324008 0000007ca10552e4  .@2.|....R..|...

0000007ca13c3630 0000007ca10552e8 0000007ca10552ec  .R..|....R..|...

0000007ca13c3640 0000007ca10552f4 0000007ca132402c  .R..|...,@2.|...

0000007ca13c3650 0000000000000000 0000000000000000  ................

0000007ca13c3660 0000000000000000 0000000000000000  ................

0000007ca13c3670 0000000000000000 0000007ca134ea84  ..........4.|...

0000007ca13c3680 0000007ca134ecec 0000007ca10552e4  ..4.|....R..|...

0000007ca13c3690 0000007ca10552e8 0000007ca10552ec  .R..|....R..|...

0000007ca13c36a0 0000007ca10552f4 0000007ca134ed10  .R..|.....4.|...

0000007ca13c36b0 0000000000000000 0000000000000000  ................

0000007ca13c36c0 0000000000000000 0000000000000000  ................

0000007ca13c36d0 0000000000000000 0000007ca135d02c  ........,.5.|...

0000007ca13c36e0 0000007ca135d4b8 0000007ca10552e4  ..5.|....R..|...

memory near x29 ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..    【SP = 0x0000007c02d886d0】

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o  

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…      【x29 = 0x0000007c02d886f0】

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

memory near lr ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

memory near sp ([anon:stack_and_tls:11981]):

0000007c02d886b0 0000000000000018 0000007caae98c58 ……..X…|…

0000007c02d886c0 0000007c02d886f0 0000007c95de6754 ….|…Tg..|…

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

memory near pc ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

memory map (1146 entries):

0000005f'fabc7000-0000005f'fabc7fff r--         0      1000  /system/bin/mediaserver64

0000005f'fabc8000-0000005f'fabc9fff r-x      1000      2000  /system/bin/mediaserver64

0000005f'fabca000-0000005f'fabcafff r--      3000      1000  /system/bin/mediaserver64

0000007b'e79a3000-0000007b'e7d93fff ---         0    3f1000

0000007c'a120e000-0000007c'a128efff r--         0     81000  /system/lib64/libstagefright.so

0000007c'a128f000-0000007c'a13c0fff r-x     81000    132000  /system/lib64/libstagefright.so

0000007c'a13c1000-0000007c'a13cffff r--    1b3000      f000  /system/lib64/libstagefright.so

0000007c'a13d0000-0000007c'a13d1fff rw-    1c1000      2000  /system/lib64/libstagefright.so

0000007c'a787c000-0000007c'a78f4fff r--         0     79000  /system/lib64/libmediaplayerservice.so

0000007c'a78f5000-0000007c'a79ecfff r-x     79000     f8000  /system/lib64/libmediaplayerservice.so

0000007c'a79ed000-0000007c'a79f8fff r--    171000      c000  /system/lib64/libmediaplayerservice.so

0000007c'a79f9000-0000007c'a79f9fff rw-    17c000      1000  /system/lib64/libmediaplayerservice.so

open files:

fd 0: /dev/null (unowned)

fd 1: /dev/null (unowned)

fd 2: /dev/null (unowned)

fd 3: socket:[62562] (unowned)

fd 4: /dev/binderfs/binder (unowned)

fd 5: /dev/binderfs/hwbinder (unowned)

fd 6: /sys/kernel/tracing/trace_marker (unowned)

fd 7: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

fd 8: /dmabuf: (unowned)

fd 9: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

fd 10: /storage/emulated/0/zapya/folder/华语音乐/IN-K&王忻辰&苏星婕 - 落日与晚风.mp3 (owned by unique_fd 0x7c13c7a498)

fd 11: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

...

4

Coredump

 

前面 tombstone 文件内容,可以了解到它的信息很有限,当我们需要更多的内存信息时,它无法满足我们,这时 coredump 就尤为重要了,它可以按我们配置抓取相应的内存信息,关于 core 的介绍,见:

https://man7.org/linux/man-pages/man5/core.5.html

 
    
# build/envsetup.sh# coredump_setup - enable core dumps globally for any process
#                  that has the core-file-size limit set correctly
#
# NOTE: You must call also coredump_enable for a specific process
#       if its core-file-size limit is not set already.
# NOTE: Core dumps are written to ramdisk; they will not survive a reboot!

function coredump_setup()
{
    echo "Getting root...";
    adb root;
    adb wait-for-device;

    echo "Remounting root partition read-write...";
    adb shell mount -w -o remount -t rootfs rootfs;
    sleep 1;
    adb wait-for-device;
    adb shell mkdir -p /cores;
    adb shell mount -t tmpfs tmpfs /cores;
    adb shell chmod 0777 /cores;

    echo "Granting SELinux permission to dump in /cores...";
    adb shell restorecon -R /cores;

    echo "Set core pattern.";
    adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern';

    echo "Done."
}

# coredump_enable - enable core dumps for the specified process
# $1 = PID of process (e.g., $(pid mediaserver))
#
# NOTE: coredump_setup must have been called as well for a core
#       dump to actually be generated.

function coredump_enable()
{
    local PID=$1;
    if [ -z "$PID" ]; then
        printf "Expecting a PID!\n";
        return;
    fi;
    echo "Setting core limit for $PID to infinite...";
    adb shell /system/bin/ulimit -P $PID -c unlimited
}

给 system_server 配置 coredump 参数,由于目标进程的 coredump 生成的目录受 selinux 权限限制,因此这种方法配置抓 coredump 的方法要注意目标进程对哪些目录文件有读写的 selinux 权限,再配置相应的目录。

 
    
adb wait-for-device
adb root
adb shell mkdir /data/cores
adb shell chmod 777 /data/cores
#adb shell setenforce 0
adb shell restorecon -R /data/cores
adb shell 'echo /data/cores/core.%e.%p > /proc/sys/kernel/core_pattern'
adb shell 'system/bin/ulimit -P `pidof system_server` -c unlimited'
#adb shell 'echo 2 > /proc/sys/fs/suid_dumpable'

注意:确定问题与selinux权限无关,可以通过adb shell setenforce 0关闭selinux权限

给 com.android.settings 配置抓取 coredump 的参数,由于前面的配置中 /data/cores 目录恢复 selinux 权限后如下:

drwxrwxrwx   2 root     root     u:object_r:system_data_file:s0          3452 2022-07-04 15:08 cores

我们知道 app 一定有权限对自身的 /data/data/$PACKAGE/ 目录下的文件具有读写权限,于是可以配置成如下参数:

 
    
adb wait-for-device
adb root
adb shell mkdir /data/data/com.android.settings/cores
adb shell chmod 777 /data/data/com.android.settings/cores
adb shell restorecon -R /data/data/com.android.settings/cores
adb shell 'echo /data/data/com.android.settings/cores/core.%e.%p > /proc/sys/kernel/core_pattern'
adb shell 'system/bin/ulimit -P `pidof com.android.settings` -c unlimited'
#adb shell 'echo 2 > /proc/sys/fs/suid_dumpable'

当我们在机器上验证对这个app进行kill -11模拟时

$ kill -11 `pidof com.android.settings`

$ ls /data/data/com.android.settings/cores/core.ndroid.settings.27946

coredump_filter 进程默认值是0x23,只抓取:私有匿名/共享匿名/私有大尺寸页,需要抓全部内存信息则 adb shell 'echo 0x27 > /proc/$PID/coredump_filter' 即可。

/proc/$PID/coredump_filter

bit0: 私有匿名

bit1: 共享匿名

bit2: 有底层文件的私有映射

bit3: 有底层文件共享映射

bit4: ELF头

bit5: 私有大尺寸页

bit6: 共享大尺寸页

core_pattern 控制生成 core 的文件名,以及输出的 core 的位置。例如:

adb shell 'echo /data/cores/core.%p > /proc/sys/kernel/core_pattern'

/proc/sys/kernel/core_pattern

%p: 添加pid

%u: 添加当前uid

%g: 添加当前gid

%s: 添加导致产生core的信号

%t: 添加core文件生成时的unix时间

%h: 添加主机名

%e: 添加命令名

%E: 可执行文件的路径名,用斜杠(’/’)替换为感叹号(’!’)。

当程序调用了 seteuid()/setegid() 改变进程的有效用户或组,则在默认情况下系统不会为这些进程生成 core,此时你可能需要调整 suid_dumpable 参数进入调试模式或安全模式下进行。

/proc/sys/fs/suid_dumpable

0:默认模式

1:调试模式

2:安全模式

core 文件也是ELF文件的一种,因此它的主体格式组成部分与 ELF 文件相

同,以案例讲解的 core 文件为例,它主要组成部分为 /proc/self/maps 下的VMA以及各个线程寄存器。其中寄存器信息存放在PT_NOTE,各VMA存放在 PT_LOAD,当被过滤掉的 VMA,它只有 Program Header 描述,没有对应的 segment。

 

注:MTK 平台的 MINIDUMP 也是 coredump 的一种,它所保存的内存信息有限,core 的分析可以使用 GDB、lldb 等调试工具,如何使用这些调试工具,这里就不一一介绍。

$ ~/work/debug/gdb_arm64/gdb-12.1/output/bin/aarch64-linux-gdb

当我们没有符号表时,仅加载 core-file 也是可以使用的。

(gdb) core-file PROCESS_MINIDUMP

当我们有相应的符号表时,即可加载符号表目录

(gdb) set solib-search-path symbols/

或者

(gdb) set sysroot symbols/

(gdb) info sharedlibrary

显示所有共享库的地址范围

(gdb) info registers

显示当前线程的当前帧寄存器信息

(gdb) info locals

显示当前帧的局部变量

(gdb) info thread

显示有哪些线程

(gdb) thread 2

切换到2号线程上

(gdb) bt

显示当前线程的堆栈

(gdb) thread apply all [command]

例如打印所有线程堆栈

(gdb) thread apply all bt

让所有线程做同样的命令

(gdb) frame

显示当前帧信息

(gdb) frame 3

切换到#3帧

(gdb) print 或 (gdb) p

打印变量

(gdb) ptype 'android::AHandler'

查看某个class或struct的数据结构

(gdb) ptype /o 'android::AHandler'

查看数据类型占多少字节

(gdb) set print pretty on

格式化输出

(gdb) set log on

保存gdb输出的结果

(gdb) x /gx 0x7c02d886f0

读取地址0x7c02d886f0内存内容,其中输出格式如下:

o(octal), x(hex), d(decimal),

u(unsigned decimal), t(binary),

f(float), a(address), i(instruction),

c(char), s(string),

z(hex, zero padded on the left).

(gdb) disassemble 0x0000007c95de6708

(gdb) disassemble 'android::AMessage::setTarget'

显示函数汇编信息

5

内存检测机制

 

在 Android 11 之后的 AOSP master 中,弃用了 arm64 上的平台开发 ASAN,改为使用 HWASAN,AddressSanitizer (ASAN) 是一种基于编译器的快速检测工具,用于检测内存错误。

Stack and heap buffer overflow/underflow

堆栈和堆缓冲区上溢/下溢

Heap use after free

使用已释放的内存

Stack use outside scope

超出栈范围

Double free/wild free

多次释放内存/错误释放

HWASan 仅适用于 Android 10 及更高版本,且只能用于 AArch64 硬件,具备 ASAN 同样的检测能力,在 Linux-4.14 版本以上支持 tagged-pointers才能使用。

编译Android版本时带入环境变量如下:

$ export SANITIZE_TARGET=hwaddress

跳过某个模块,在相应模块下的Android.bp文件下添加内容如下:

sanitize: {

hwaddress: false,

address: false,

},

Android.mk则添加内容如下:

LOCAL_NOSANITIZE := hwaddress

APP构建支持HWASAN则在Application.mk下添加内容如下:

APP_STL := c++_shared

APP_CFLAGS := -fsanitize=hwaddress -fno-omit-frame-pointer

APP_LDFLAGS := -fsanitize=hwaddress

最新 Android S 上引入的 ARM Memory Tagging Extension (MTE),MTE 的原理和 HWASan 类似,最大的区别在于 HWASan 需要重新编译,在所有内存访问前插桩相应的检测函数来实现,而 MTE 则在读写指令内部完成检测,完全由硬件支持。

更多内容转至掘金大佬-芦半山:

https://juejin.cn/post/6844904111570157575

https://juejin.cn/post/7013595058125406238

6

野指针的危害

当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称 Wild pointer (野指针),如果这个野指针所指向的内存被分配给其它指针,而这个野指针仍在使用,程序将难以预测。

 
    

#include <stdio.h>

class A {
public:
    virtual ~A() = default;
    virtual void foo() {
        printf("A:%ld\n", a);
    }
    long a;
};

class B {
public:
    virtual ~B() = default;
    virtual void foo() {
        printf("B:%ld\n", b);
    }
    long b;
};

int main(int /*argc*/, char** /*argv[]*/) {
    A *a = new A();
    A *a_bak = a;
    a->a = 1000L;
    printf("A ptr = %p\n", a);    delete a;   //  此时a指针已经被free掉,那么指针a_bak是个野指针
    B *b = new B();
    printf("B ptr = %p\n", b);
    b->b = 2000L;
    b->foo();
    a_bak->foo();   //  此处程序会运行怎么结果
    delete b;
    return 0;
}

以上程序会怎么输出,由于 B 与 A 的数据结构大小一致,运行在同一个线程,极大可能会分配刚释放的指针地址,因此这个程序最后指针 b 与指针 a_bak 是同一个。

# ./data/Tester64                                                                                                  

A ptr = 0xb400007690205010

B ptr = 0xb400007690205010

B:2000

B:2000

像以上这个结果,那程序如下改写,那么可怕的事情就会出现,下面这个程序会报什么错。

#include <stdio.h>

class A {
public:
    long bad;
    long a;
};

class B {
public:
    virtual ~B() = default;
    virtual void foo() {
        b = 2000L;
        printf("B:%ld\n", b);
    }
    long b;
};

int main(int /*argc*/, char** /*argv[]*/) {
    A *a = new A();
    A *a_bak = a;
    printf("A ptr = %p\n", a);
    delete a;
    B *b = new B();
    printf("B ptr = %p\n", b);

    a_bak->bad = 0x20L;
    b->foo();
    delete b;
    return 0;
}

上面这个程序在27行处,bad 会将 B 的虚函数表破坏,导致28和29行在虚函数表上寻找 foo 函数与析构函数地址时发段错误。

Timestamp: 2022-07-06 14:47:50.925654058+0800

Process uptime: 0s

Cmdline: ./data/Tester64

pid: 12652, tid: 12652, name: Tester64  >>> ./data/Tester64 <<<

uid: 0

tagged_addr_ctrl: 0000000000000001

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x28

Cause: null pointer dereference

x0  b40000762f005010  x1  b40000762f01b000  x2  0000000000000007  x3  ffffffffffffffff

x4  ffffffffffffffff  x5  0000000040100401  x6  b40000762f01b006  x7  3637303030303462

x8  0000000000000020  x9  a454ef76eb4317d3  x10 0000000000004001  x11 0000000000000000

x12 0000000000000000  x13 0000000000000002  x14 0000000000000010  x15 0000000000000010

x16 000000762f5a0c58  x17 000000762f5910d4  x18 000000763745c000  x19 b40000762f005010

x20 b40000762f005010  x21 0000007fe173d378  x22 0000000000000001  x23 0000000000000000

x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 0000000000000000

x28 0000000000000000  x29 0000007fe173d2e0

lr  0000005ac14600c8  sp  0000007fe173d2e0  pc  0000005ac14600d0  pst 0000000060001000

backtrace:

#00 pc 00000000000010d0  /data/Tester64 (main+124)

#01 pc 000000000008436c  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+100)

程序会出错还好,如果程序不会出错,仍继续运行,那么这个程序将变得很可怕,因为你不知道程序将会怎么运行,像下面这个程序,刻意改写控制程序往其它方向运行。

#include <stdio.h>

class A {
public:
    void *bad;
    long a;
};

class B {
public:
    virtual ~B() {
        printf("delete B\n");
    };
    virtual void foo() {
        b = 2000L;
        printf("B:%ld\n", b);
    }
    long b;
};

void func1() {
    printf("Hello !!\n");
}

void func2() {
    printf("GoGoGo !!\n");
}

int main(int /*argc*/, char** /*argv[]*/) {

    A *a = new A();
    A *a_bak = a;
    printf("A ptr = %p\n", a);
    delete a;
    B *b = new B();
    printf("B ptr = %p\n", b);

    long *data = new long[4] {0x0L, (long)func2, (long)func1, 0x0L};
    a_bak->bad = data;

    printf("Test .. \n");

    b->foo();
    delete b;

    printf("Done.\n");
    return 0;
}

# ./data/Tester64

A ptr = 0xb400007ce9605010

B ptr = 0xb400007ce9605010

Test ..

Hello !!

GoGoGo !!

Done.

7

数组越界的危害

比起前面的野指针,数组越界大多数情况都能被 HWASAN 等内存检测发现,当然前面的野指针情况也是能被检测发现的,数组越界往往会出现某个对象的内存前半部分被污染,而后半部分数据正常的情况。

#include <stdio.h>

class A {
public:
    long a = 0x55AA;
    long b = 0xDEAD;
};

int main(int /*argc*/, char** /*argv[]*/) {
    long *b = new long[2] {0x0L, 0x1L};
    A *a = new A();
    printf("A:%p\n", a);
    printf("B:%p\n", b);

    b[2] = 0xDEAD;
    printf("B2:%p\n", &b[2]);

    printf("0x%lx-0x%lx\n", a->a, a->b);
    return 0;
}

因为 b 的数据大小与对象 a 的数据大小相同,因此在这个程序刚启动分配指针地址时,基本会连在一块,因此 b[2] 越界行为将对象 a 的内容破坏,往往程序运行了很久,内存碎片化增加,就不知道 b[2] 越界操作会破坏哪个对象的内存。

# ./data/Tester64

A:0xb400007fa7c05020

B:0xb400007fa7c05010

B2:0xb400007fa7c05020

0xdead-0xdead

8

机器码翻译

本文案例中的 tombstone 文件 PC 跑飞,没有落在 text 段地址上,这里换一份 tombstone 方便说明,我们可以通过编译的方法生成对应的 ELF 文件,即可用 objdump 得到对应的汇编。

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x40

Cause: null pointer dereference

x0  0000000000000020  x1  b400007bf95f0e00  x2  63692f6863726165  x3  0000000000000008

x4  b400007bf95f0e74  x5  b400007bf8ea36f4  x6  656863732f676e69  x7  732f7269645f616d

x8  db4cf552ad46f717  x9  0000000000000001  x10 00000000000349e0  x11 0000007bc0000000

x12 0000000000000000  x13 0000000000034960  x14 b400007b58d7c4c0  x15 00000000374d2457

x16 0000007ca3543d50  x17 0000007ca35338ec  x18 0000007b38c74000  x19 0000000000000020

x20 0000000000000000  x21 0000007b548e9000  x22 0000007b548e9000  x23 b400007bf946ffd0

x24 0000007b548e76c0  x25 0000007b548e9000  x26 0000007b548e7b30  x27 0000007b548e7b18

x28 0000007b548e7a10  x29 0000007b548e7410

lr  0000007a8e4e7980  sp  0000007b548e73e0  pc  0000007a8e4d0e00  pst 0000000060001000

memory near pc (/apex/com.android.appsearch/lib64/libicing.so):

0000007a8e4d0de0 a90557f6f90023f7 9100c3fda9064ff4 .#…W…O……

0000007a8e4d0df0 f94016c8d53bd056 f81f83a8aa0003f3 V.;…@………

0000007a8e4d0e00 350001c839408008 91181c42b0fffde2 ..@9…5….B…

0000007a8e4d0e10 2a1f03e1910023e0 910023f452808ee3 .#…..…R.#..

0000007a8e4d0e20 910022809400d695 912b742190fffde1 ….."……!t+.

编写汇编文件 code.S,内容如下:

 
    

.inst 0xf90023f7
.inst 0xa90557f6
.inst 0xa9064ff4
.inst 0x9100c3fd
.inst 0xd53bd056
.inst 0xf94016c8
.inst 0xaa0003f3
.inst 0xf81f83a8.inst 0x39408008.inst 0x350001c8
.inst 0xb0fffde2
.inst 0x91181c42
.inst 0x910023e0
.inst 0x2a1f03e1
.inst 0x52808ee3
.inst 0x910023f4
.inst 0x9400d695
.inst 0x91002280
.inst 0x90fffde1
.inst 0x912b7421

编译:aarch64-linux-android-as code.S -o code.o

反汇编:aarch64-linux-android-objdump -d code.o

code.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <.text>:

0:        f90023f7         str        x23, [sp, #64]

4:        a90557f6         stp        x22, x21, [sp, #80]

8:        a9064ff4         stp        x20, x19, [sp, #96]

c:        9100c3fd         add        x29, sp, #0x30

10:        d53bd056         mrs        x22, tpidr_el0

14:        f94016c8         ldr        x8, [x22, #40]

18:        aa0003f3         mov        x19, x0

1c:        f81f83a8         stur        x8, [x29, #-8]

 20:        39408008         ldrb        w8, [x0, #32]

24:        350001c8         cbnz        w8, 5c <.text+0x5c>

28:        b0fffde2         adrp        x2, fffffffffffbd000 <.text+0xfffffffffffbd000>

2c:        91181c42         add        x2, x2, #0x607

30:        910023e0         add        x0, sp, #0x8

34:        2a1f03e1         mov        w1, wzr

38:        52808ee3         mov        w3, #0x477                         // #1143

3c:        910023f4         add        x20, sp, #0x8

40:        9400d695         bl        35a94 <.text+0x35a94>

44:        91002280         add        x0, x20, #0x8

48:        90fffde1         adrp        x1, fffffffffffbc000 <.text+0xfffffffffffbc000>

4c:        912b7421         add        x1, x1, #0xadd

由于x0=0x0000000000000020,因此在指令0x39408008( ldrb        w8, [x0, #32]),会发生读取内存0x40的段错误。

8

汇编翻译

有些时候我们可能需要手动修改 ELF 文件的机器码达到一些调试的目的,这时候我们就需要知道汇编对应的机器码,去查手册也比较麻烦,这时我们直接编写汇编文件即可。

编写汇编文件 code_asm.S,内容如下:

 
    
str   x30, [sp, #-16]!
str   x29, [sp, #-16]!

编译:aarch64-linux-android-as code_asm.S -o code_asm.o

反汇编:aarch64-linux-android-objdump -d code_asm.o

code_asm.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <.text>:

0:        f81f0ffe         str        x30, [sp, #-16]!

4:        f81f0ffd         str        x29, [sp, #-16]!

9

案例讲解

Timestamp: 2022-06-07 01:53:32.033409857+0800

Process uptime: 0s

Cmdline: mediaserver64

pid: 1139, tid: 11981, name: NPDecoder  >>> mediaserver64 <<<

uid: 1013

tagged_addr_ctrl: 0000000000000001

signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

x0  79748c5e568e2ddc  x1  0000007ca13c3618  x2  0000000000000000  x3  0000007ca1291000

x4  0000000001909705  x5  0000000000000000  x6  0000007c02d88808  x7  b60625655bf0252f

x8  0000000000000080  x9  0000007ca126fed7  x10 0000000000000006  x11 0000007bfd0a81fc

x12 9ef8a95ca9649dbe  x13 e44782d5ac38720e  x14 0000007bfd0a8030  x15 0000001e56307b5c

x16 0000007c95dfdb70  x17 0000007c9844f118  x18 0000007bfaa28000  x19 b400007c13c246d0

x20 0000007c02d88730  x21 b400007c13c67c00  x22 0000000000000415  x23 0000007c02d89000

x24 0000000000000002  x25 b400007c13c246d0  x26 b400007c13c67c00  x27 0000007c02d89000

x28 0000007ca13c2c28  x29 0000007c02d886f0

lr  0000007c02d886f0  sp  0000007c02d886d0  pc  0000007c02d886f0  pst 0000000080001000

backtrace:

#00 pc 00000000000f86f0  [anon:stack_and_tls:11981]

0000007c'02c90000-0000007c'02d8bfff rw-         0     fc000  [anon:stack_and_tls:11981]

memory near pc ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

code.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <.text>:

  0:        02d886f0         .inst        0x02d886f0 ; undefined

4:        0000007c         udf        #124

从 tombstone 的 backtrace 信息上看,很明显是PC跑飞了,跑到线程栈地址空间下,由于栈内存地址不可运行,因此被拒绝访问而出错,如果有x的权限那么也是错误,会发生 SIGILL 指令错误。

从 tombstone 中的 x29 和 SP 寄存器附近的内存中进行栈回溯,由于大小受限,往往无法回溯到栈底,无法完全证明程序就是经过这几个函数。

标签: dd70f120三社二极管模块kc120e3e精密电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台

memory near x29 ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..    【SP = 0x0000007c02d886d0】

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o  

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…      【x29 = 0x0000007c02d886f0】

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

这里我们通过 FP(x29) 回溯,如果这个 x29 是有效的,那么 x29 存放的是上一个 Caller 的 FP 寄存器,x29+0x8 的地址存放上一个 Caller 的 LR 寄存器。

0x0000007ca133f8e0 地址落在 /system/lib64/libstagefright.so 的 TEXT 段上。

0000007c'a120e000-0000007c'a128efff r--         0     81000  /system/lib64/libstagefright.so

 0000007c'a128f000-0000007c'a13c0fff r-x     81000    132000  /system/lib64/libstagefright.so

0000007c'a13c1000-0000007c'a13cffff r--    1b3000      f000  /system/lib64/libstagefright.so

0000007c'a13d0000-0000007c'a13d1fff rw-    1c1000      2000  /system/lib64/libstagefright.so

[14] .text             PROGBITS         0000000000081000  00081000

000000000012bed8  0000000000000000  AX       0     0     4096

Funcation_Offset  =  PC - TEXT_LOAD + .text_offset

(gdb) p /x 0x0000007ca133f8e0-0x7ca128f000+0x81000

$1 = 0x1318e0

(gdb) x /2i 0x1318e0-0x4

0x1318dc <_ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+200>:        bl        0x1ad390 <_ZN7android8AMessageC1EjRKNS_2spIKNS_8AHandlerEEE@plt>

0x1318e0 <_ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+204>:        eor        x8, x29, x25

(gdb) p _ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+200

$2 = (android::status_t (*)(struct android::MediaCodec * const, size_t, size_t, size_t, int64_t, uint32_t,

struct android::AString *)) 0x1318dc <android::MediaCodec::queueInputBuffer(unsigned long, unsigned long, unsigned long, long, unsigned int, android::AString*)+200>

因此0x0000007ca133f8e0所在的函数是android::MediaCodec::queueInputBuffer。

一般我们直接 PC 减去库文件的起始地址即可得到函数的偏移地址。

即:0x0000007ca133f8e0 - 0x0000007ca120e000