You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判定Rooted Android设备中APP的异常关闭原因?

解决思路与实操方案

一、用原生信号钩子捕获崩溃场景

由于你拥有注入目标进程的SO源码,可以在SO初始化阶段挂接信号处理函数,捕获Android原生崩溃常见的信号(如SIGSEGV、SIGABRT、SIGILL),直接将堆栈信息写入本地文件(规避logcat缓冲丢失问题):

#include <signal.h>
#include <sys/ucontext.h>
#include <stdio.h>
#include <execinfo.h>
#include <stdlib.h>

// 保存原信号处理函数,可选恢复用
struct sigaction orig_segv, orig_abrt, orig_ill;

void crash_handler(int sig, siginfo_t *info, void *context) {
    FILE *fp = fopen("/data/local/tmp/crash_dump.txt", "a");
    if (!fp) goto restore;

    fprintf(fp, "=== Caught Signal: %d ===\n", sig);
    // 打印调用堆栈
    void *callstack[128];
    int frame_count = backtrace(callstack, 128);
    char **symbols = backtrace_symbols(callstack, frame_count);
    if (symbols) {
        for (int i = 0; i < frame_count; i++) {
            fprintf(fp, "%s\n", symbols[i]);
        }
        free(symbols);
    }
    fflush(fp);
    fclose(fp);

restore:
    // 恢复原信号处理,让系统继续执行默认逻辑
    switch (sig) {
        case SIGSEGV: sigaction(sig, &orig_segv, NULL); break;
        case SIGABRT: sigaction(sig, &orig_abrt, NULL); break;
        case SIGILL: sigaction(sig, &orig_ill, NULL); break;
    }
    raise(sig);
}

// 在JNI_OnLoad中注册钩子
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = crash_handler;
    sa.sa_flags = SA_SIGINFO;

    // 保存原处理函数并替换
    sigaction(SIGSEGV, &sa, &orig_segv);
    sigaction(SIGABRT, &sa, &orig_abrt);
    sigaction(SIGILL, &sa, &orig_ill);

    return JNI_VERSION_1_6;
}

注意:选择/data/local/tmp这类无需额外权限的路径写入日志,确保SO被正确注入并执行JNI_OnLoad。

二、替换日志输出方式,避免缓冲丢失

logcat存在输出缓冲,进程崩溃时未刷新的日志会丢失,改用直接写入文件的自定义日志:

#include <stdio.h>
#include <stdarg.h>

void so_log(const char *tag, const char *fmt, ...) {
    FILE *fp = fopen("/data/local/tmp/so_runtime_log.txt", "a");
    if (!fp) return;

    va_list args;
    va_start(args, fmt);
    fprintf(fp, "[%s] ", tag);
    vfprintf(fp, fmt, args);
    fprintf(fp, "\n");
    fflush(fp); // 强制刷新缓冲区
    fclose(fp);
}

将SO中所有__android_log_print调用替换为该函数,确保每一条日志都立即落地。

三、Root设备下的进程退出追踪

  1. strace系统调用追踪:
    附加到目标进程,记录所有系统调用:

    strace -p <目标进程PID> -o /data/local/tmp/strace_output.txt
    

    进程退出后,查看日志末尾的系统调用,若出现exit_group说明主动退出,若出现kill或信号相关调用则为崩溃。

  2. 内核日志排查:
    执行以下命令查看内核层面的进程异常记录:

    dmesg | grep <进程名/PID>
    

    部分内存违规、权限问题会被内核记录,且不会生成tombstone。

  3. 退出码验证:
    进程退出后,执行ps -o exit_code <PID>(需进程刚退出时查询),若退出码为128+信号值(如139对应SIGSEGV),则说明进程被信号杀死。

四、SO问题定位细化

  • 给SO中所有关键函数添加进入/退出日志,记录参数、返回值,逐步缩小崩溃范围;
  • 检查SO中的内存操作:重点排查野指针、数组越界、空指针解引用,这类问题可能直接导致进程被内核终止,无tombstone生成;
  • 验证依赖兼容性:用readelf -d libxxx.so查看SO依赖库,确保目标设备上的依赖版本与编译环境匹配,避免兼容性崩溃。

内容的提问来源于stack exchange,提问作者Alexey Starinsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:43:16