• 巧用寄存器定位android native崩溃问题


    巧用寄存器定位android native崩溃问题

    背景:

    由于android 一些崩溃在libc.so里边一些函数里边,对于问题调查不太方便,本文针对memcpy的一个崩溃,来阐述下此类问题应该如何调查与解决

    崩溃堆栈

    Bash
    signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x745fce8000
        x0  000000745fc382f0  x1  000000745fce7fc0  x2  ffffffff87a9b060  x3  000000745fcbceb0
        x4  00000073e7783070  x5  00000073e7757f60  x6  0000000000000000  x7  0000000000000000
        x8  0000000000000000  x9  0000000000000000  x10 0000000000000000  x11 0000000000000000
        x12 0000000000000000  x13 0000000000000000  x14 0000a237db54c6d0  x15 0000000000000010
        x16 00000071a79deca8  x17 0000007543d1fc80  x18 0000007165a80000  x19 000000733f897810
        x20 00000071885ad274  x21 00000071885ad27c  x22 00000071885ad278  x23 000000748f9930a0
        x24 000000000002b110  x25 0000000087b4ad80  x26 000000745fc382f0  x27 00000071885ae000
        x28 0000000000000000  x29 00000071885ad260
        sp  00000071885ad1b0  lr  000000717c9d5f30  pc  0000007543d1fc3c
    backtrace:
        #00 pc 000000000004ac3c  /apex/com.android.runtime/lib64/bionic/libc.so (__memcpy+300)
        ..............//其他方法的调用栈

    可以看的出来,崩溃在了__memcpy 这个函数里边,这个是标准库里边的,我们只能review 调用memcpy的地方是否出现问题,首先我们还要区分到底野指针还是越界.

    Arm 64 函数调用

    通过图中可以看出下来r0---r7 也就是x0-x7, 对应的x0就是memcpy的第一个参数,x1 的第二个参数

           

    void        *memcpy(void *__dst, const void *__src, size_t __n);

    __dst 对应的x0

    __src 对应的x1

    __n  对应的x2,对于arm64而言,size_t占用8个字节,因此是x2

    为了验证上边是不是如是否正确,接下来写了一段测试代码进行测试

    C++
    void memcpy_1(size_t a, void *dst, void *src, size_t size)
    {
        memcpy(dst, src, size);
    }
    int main()
    {
        memcpy_1(55,(void*)0x1234, (void*)0x4578, 11);
        return 0;
    }
     

    可以看的出来的,很简单的代码,按照我们的上边猜想memcpy_1(int a, void *dst, void *src, size_t size)的四个参数对应的 x0~x3,下边我就从汇编角度来看下具体

    Assembly language
      0x100001b40 <+24>: mov    x0, #0x37
        0x100001b44 <+28>: mov    x1, #0x1234
        0x100001b48 <+32>: mov    x2, #0x4578
        0x100001b4c <+36>: mov    x3, #0xb
    ->  0x100001b50 <+40>: bl     0x100001af0               ; memcpy_1 at main.cpp:326
     

    看的出来确实如我们预期的一样,那接下来看下memcpy的调用前的汇编

    Assembly language
    test`memcpy_1:
    ->  0x100001af0 <+0>:  sub    sp, sp, #0x30;给栈分配了48个字节
        0x100001af4 <+4>:  stp    x29, x30, [sp, #0x20] ;;保存寄存器值加载2个字节到x29,x30
        0x100001af8 <+8>:  add    x29, sp, #0x20
        0x100001afc <+12>: stur   x0, [x29, #-0x8];x0 临时存放到 [x29, #-0x8]
        0x100001b00 <+16>: str    x1, [sp, #0x10];x1 临时存放到 [sp, #0x10]
        0x100001b04 <+20>: str    x2, [sp, #0x8];x2 临时存放到 [sp, #0x8]
        0x100001b08 <+24>: str    x3, [sp]
        0x100001b0c <+28>: ldr    x0, [sp, #0x10]; 把[sp, #0x10] 存放到x0
        0x100001b10 <+32>: ldr    x1, [sp, #0x8]; 把[sp, #0x8] 存放到x1
        0x100001b14 <+36>: ldr    x2, [sp]; 把[sp] 存放到,放到x2中
        0x100001b18 <+40>: bl     0x100003c48               ; symbol stub for: memcpy
     

    汇编语言可以看出来它最终传递给memcpy的寄存器还是x0,x1,x2,这也满足了我们预期,那你可能会有疑问,memcpy 调用完回来,数据是怎么恢复的,那我们接下来看下对应的汇编

    Assembly language
        0x100001b1c <+52>: ldp    x29, x30, [sp, #0x20]; 与上边相对 恢复现场stp    x29, x30, [sp, #0x20] ;;加载2个字节到x29,x30
        0x100001b20 <+56>: add    sp, sp, #0x30; 把栈分配的48个字节跟减除,最终达到动态栈平衡

    总结:

    同过上边分析,我们明确的只知道x0,x1,x2对应的memcpy的3个参数,根据上边崩溃堆栈的x0,x1, x2的值如下:

    x0  000000745fc382f0  x1  000000745fce7fc0  x2  ffffffff87a9b060

    也就是

    void        *memcpy(void *__dst, const void *__src, size_t __n);

    __dst = 000000745fc382f0

    __src = 000000745fce7fc0

    __n = ffffffff87a9b060

    可以看的出来 拷贝字节长度__n 明显ffffffff87a9b060,这个值明显有问题,是个负数,所以我们要想解决这个崩溃,就要知道这个负值是怎么来的,可以在适当的地方加一些保护。

  • 相关阅读:
    华为机试 - 字符串筛选排序
    【Shell 脚本速成】07、Shell 流程控制——if 判断语句
    《HTML+CSS+JavaScript》之第18章 图片样式
    全球AI投资减少,生成式AI面临挑战;Command R + 成首个击败GPT-4的开源模型
    10_13C++
    MySQL8 间隙锁在11种情况下的锁持有情况分析
    Linux系统 -目录结构与配网
    android studio 打包 jar aar
    内容交付网络—CDN
    NLP实践!文本语法纠错模型实战,搭建你的贴身语法修改小助手 ⛵
  • 原文地址:https://blog.csdn.net/c553110519/article/details/126528748