如何判定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设备下的进程退出追踪
strace系统调用追踪:
附加到目标进程,记录所有系统调用:strace -p <目标进程PID> -o /data/local/tmp/strace_output.txt进程退出后,查看日志末尾的系统调用,若出现
exit_group说明主动退出,若出现kill或信号相关调用则为崩溃。内核日志排查:
执行以下命令查看内核层面的进程异常记录:dmesg | grep <进程名/PID>部分内存违规、权限问题会被内核记录,且不会生成tombstone。
退出码验证:
进程退出后,执行ps -o exit_code <PID>(需进程刚退出时查询),若退出码为128+信号值(如139对应SIGSEGV),则说明进程被信号杀死。
四、SO问题定位细化
- 给SO中所有关键函数添加进入/退出日志,记录参数、返回值,逐步缩小崩溃范围;
- 检查SO中的内存操作:重点排查野指针、数组越界、空指针解引用,这类问题可能直接导致进程被内核终止,无tombstone生成;
- 验证依赖兼容性:用
readelf -d libxxx.so查看SO依赖库,确保目标设备上的依赖版本与编译环境匹配,避免兼容性崩溃。
内容的提问来源于stack exchange,提问作者Alexey Starinsky
相关产品推荐
相关产品推荐

