吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2015|回复: 18
上一主题 下一主题
收起左侧

[Android 原创] 初阶技能:Android应用运行异常如何丰富线索

  [复制链接]
跳转到指定楼层
楼主
iofomo 发表于 2024-9-8 16:22 回帖奖励
本帖最后由 iofomo 于 2024-9-8 16:28 编辑

在我们容器虚拟化产品开发过程中,时常会遇到某些应用无法启动或运行时异常崩溃的问题;让应用行为信息丰富,则能还原应用异常发生过程,对我们快速分析问题至关重要。

在这里需要用到以下几个开源工具:

01. 举个栗子

我们在做应用容器时,尝尝会遇到应用进程崩溃,通过adb抓取的日志,却只有很少的几行日志,没有其他任何信息,因此通过一些简单的方式让应用行为的痕迹还原,是我们第一步要做的事情。

9402  9402 D AndroidRuntime: Shutting down VM
9402  9402 I Process : Sending signal. PID: 9402 SIG: 9

02. 打印退出痕迹

通过拦截native函数,打印Java层的异常退出:

import android.os;

public class Process {

    /**
     * Returns the identifier of this process, which can be used with
     * {@link #killProcess} and {@link #sendSignal}.
     */
        public static final native void sendSignal(int pid, int signal);

    /**
     * @hide
     * Private impl for avoiding a log message...  DO NOT USE without doing
     * your own log, or the Android Illuminati will find you some night and
     * beat you up.
     */
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
        public static final native void sendSignalQuiet(int pid, int signal);

}

public class Runtime {
    private static native void nativeExit(int code);
}

通过拦截以下函数,打印Native层的异常退出:

void exit(int status);
int kill(pid_t pid, int sig);

打印调用栈:

// 非 JNI 环境获取从当前线程获取 JNIEnv(AttachCurrentThread)
void jni_thread_dump(JNIEnv* env) {
    jclass jcls = env->FindClass("java/lang/Thread");
    if (!jcls) return;
    jmethodID jm = env->GetStaticMethodID(jcls, "dumpStack", "()V");
    if (!jm) return;
    env->CallStaticVoidMethod(jcls, jm);
    env->ExceptionClear();
}

输出结果:

5511  5511 W System.err: java.lang.Exception: Stack trace
5511  5511 W System.err:    at java.lang.Thread.dumpStack(Thread.java:1527)
5511  5511 W System.err:    at android.os.Process.sendSignal(Native Method)
5511  5511 W System.err:    at android.os.Process.killProcess(Process.java:1295)
5511  5511 W System.err:    at com.android.internal.os.RuntimeInit$KillApplicationHandler.uncaughtException(RuntimeInit.java:207)
5511  5511 W System.err:    at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)
5511  5511 W System.err:    at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)
5511  5511 W System.err:    at java.lang.Thread.dispatchUncaughtException(Thread.java:2211)

03. 追踪Throwable异常调用栈

在我们常用的try ... catch语句中,异常时打印调用栈是常规操作,在这里可以拦截并打印栈日志信息。

import java.lang;

public class Throwable implements Serializable {

        private static native StackTraceElement[] nativeGetStackTrace(Object stackState);

    private static native Object nativeFillInStackTrace();

}

04. 打印VM异常调用栈

package dalvik.system;

/**
 * Provides a limited interface to the Dalvik VM stack. This class is mostly
 * used for implementing security checks.
 */
public final class VMStack {
    /**
     * Retrieves the stack trace from the specified thread.
     *
     * @Param t
     *      thread of interest
     * @Return an array of stack trace elements, or null if the thread
     *      doesn't have a stack trace (e.g. because it exited)
     */
    native public static StackTraceElement[] getThreadStackTrace(Thread t);

    /**
     * Retrieves an annotated stack trace from the specified thread.
     *
     * @param t
     *      thread of interest
     * @return an array of annotated stack frames, or null if the thread
     *      doesn't have a stack trace (e.g. because it exited)
     */
    native public static AnnotatedStackTraceElement[] getAnnotatedThreadStackTrace(Thread t);

    /**
     * Retrieves a partial stack trace from the specified thread into
     * the provided array.
     *
     * @param t
     *      thread of interest
     * @param stackTraceElements
     *      preallocated array for use when only the top of stack is
     *      desired. Unused elements will be filled with null values.
     * @return the number of elements filled
     */
    native public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements);
}

输出:

9402  9402 W stack  : android.app.LoadedApk.makeApplication(LoadedApk.java:1554)
9402  9402 W stack  : android.app.ActivityThread.handleBindApplication(ActivityThread.java:8522)
9402  9402 W stack  : android.app.ActivityThread.access$2800(ActivityThread.java:311)
9402  9402 W stack  : android.app.ActivityThread$H.handleMessage(ActivityThread.java:2889)
9402  9402 W stack  : android.os.Handler.dispatchMessage(Handler.java:117)
9402  9402 W stack  : android.os.Looper.loopOnce(Looper.java:205)
9402  9402 W stack  : android.os.Looper.loop(Looper.java:293)
9402  9402 W stack  : android.app.ActivityThread.loopProcess(ActivityThread.java:9934)
9402  9402 W stack  : android.app.ActivityThread.main(ActivityThread.java:9923)
9402  9402 W stack  : java.lang.reflect.Method.invoke(Native Method)
9402  9402 W stack  : com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
9402  9402 W stack  : com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)
9402  9402 W stack  : ohos.abilityshell.HarmonyLoader.tryLoadHarmony(HarmonyLoader.java:130)
9402  9402 W stack  : ohos.abilityshell.HarmonyApplication.tryLoadHarmony(HarmonyApplication.java:673)
9402  9402 W stack  : ohos.abilityshell.HarmonyApplication.attachBaseContext(HarmonyApplication.java:168)
9402  9402 W stack  : com.demo.app.DemoApp.attachBaseContext(SourceFile:1)
9402  9402 W stack  : android.app.Application.attach(Application.java:338)
9402  9402 W stack  : android.app.Instrumentation.newApplication(Instrumentation.java:1191)
9402  9402 W stack  : android.app.LoadedApk.makeApplication(LoadedApk.java:1546)
9402  9402 W stack  : android.app.ActivityThread.handleBindApplication(ActivityThread.java:8522)
9402  9402 W stack  : android.app.ActivityThread.access$2800(ActivityThread.java:311)
9402  9402 W stack  : android.app.ActivityThread$H.handleMessage(ActivityThread.java:2889)
9402  9402 W stack  : android.os.Handler.dispatchMessage(Handler.java:117)
9402  9402 W stack  : android.os.Looper.loopOnce(Looper.java:205)
9402  9402 W stack  : android.os.Looper.loop(Looper.java:293)
9402  9402 W stack  : android.app.ActivityThread.loopProcess(ActivityThread.java:9934)
9402  9402 W stack  : android.app.ActivityThread.main(ActivityThread.java:9923)
9402  9402 W stack  : java.lang.reflect.Method.invoke(Native Method)
9402  9402 W stack  : com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
9402  9402 W stack  : com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)

05. 打印异常信息

Android框架默认会为每个应用进程设置一个全局的异常Handler,我们可以替换掉输出打印更多内容。在这里要考虑应用自身也会设置的情况。

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
          // TODO
    }
});

06. 打印异常传送

AndroidAMS服务中增加了一个应用向服务提交异常的接口,我们通过android.reflect.Proxy方式代{过}{滤}理IActivityManager的实例,打印输出。

package android.app;

// @source code: /frameworks/base/core/java/android/app/IActivityManager.aidl
interface IActivityManager {
        void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo);
}

// @source code: /frameworks/base/core/java/android/app/ApplicationErrorReport.java
public class ApplicationErrorReport implements Parcelable {
    public static class CrashInfo {
        /**
         * Dump a CrashInfo instance to a Printer.
         */
        public void dump(Printer pw, String prefix) {
            pw.println(prefix + "exceptionHandlerClassName: " + exceptionHandlerClassName);
            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
            pw.println(prefix + "exceptionMessage: " + exceptionMessage);
            pw.println(prefix + "throwFileName: " + throwFileName);
            pw.println(prefix + "throwClassName: " + throwClassName);
            pw.println(prefix + "throwMethodName: " + throwMethodName);
            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
            pw.println(prefix + "stackTrace: " + stackTrace);
        }
    }
    public static class ParcelableCrashInfo extends CrashInfo implements Parcelable {

    }
}

如输出结果为:

9402  9402 W Crash   : exceptionClassName: java.lang.IllegalStateException
9402  9402 W Crash   : exceptionMessage: failed to attach Application, errorCode=30, errorInfo=bms service error, code is 8519969. 8519797
9402  9402 W Crash   : throwFileName: HarmonyLoader.java
9402  9402 W Crash   : throwClassName: ohos.abilityshell.HarmonyLoader
9402  9402 W Crash   : throwMethodName: tryLoadHarmony
9402  9402 W Crash   : throwLineNumber: 130
9402  9402 W Crash   : stackTrace: java.lang.RuntimeException: Unable to instantiate application com.demo.app.DemoApp package com.demo.app: java.lang.IllegalStateException: failed to attach Application, errorCode=30, errorInfo=bms service error, code is 8519969.
9402  9402 W Crash   :  at android.app.LoadedApk.makeApplication(LoadedApk.java:1554)
9402  9402 W Crash   :  at android.app.ActivityThread.handleBindApplication(ActivityThread.java:8522)
9402  9402 W Crash   :  at android.app.ActivityThread.access$2800(ActivityThread.java:311)
9402  9402 W Crash   :  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2889)
9402  9402 W Crash   :  at android.os.Handler.dispatchMessage(Handler.java:117)
9402  9402 W Crash   :  at android.os.Looper.loopOnce(Looper.java:205)
9402  9402 W Crash   :  at android.os.Looper.loop(Looper.java:293)
9402  9402 W Crash   :  at android.app.ActivityThread.loopProcess(ActivityThread.java:9934)
9402  9402 W Crash   :  at android.app.ActivityThread.main(ActivityThread.java:9923)
9402  9402 W Crash   :  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
9402  9402 W Crash   :  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)
9402  9402 W Crash   : Caused by: java.lang.IllegalStateException: fail

07. 打印Binder通信信息

可以打印应用访问了那些系统服务。可以通过Binderceptor项目来完成。

9402  9402 W Trace   : Binder::tgt: 0x3887b52, 3, : android.content.pm.IPackageManager
9402  9402 W Trace   : Binder::tgt: 0xe45f2c2, 2, : android.os.IServiceManager
9402  9402 W Trace   : Binder::tgt: 0x76c2822, 56, : com.huawei.android.view.IHwWindowManager
9402  9417 W Trace   : Binder::tgt: 0x3887b52, 20, : android.content.pm.IPackageManager
9402  9402 W Trace   : Binder::tgt: 0x7db76a2, 30, : android.net.IConnectivityManager
9402  9402 W Trace   : Binder::tgt: 0x3887b52, 20, : android.content.pm.IPackageManager
9402  9402 W Trace   : Binder::tgt: 0x3887b52, 9, : android.content.pm.IPackageManager
9402  9402 W Trace   : Binder::tgt: 0x3887b52, 95, : android.content.pm.IPackageManager
9402  9402 W Trace   : Binder::tgt: 0x3887b52, 3, : android.content.pm.IPackageManager
......

binder codeaidl接口函数的对应关系:

public static void printStub(String clsName) {
        android.util.Log.w(TAG, ">>>>>> " + clsName);
        try {
            Class<?> nmClassStub = Class.findClass(clsName + "$Stub");
            Method[] mm = nmClassStub.getMethods();

            ArrayList<TRANSACTION_Item> list = new ArrayList<TRANSACTION_Item>();
            for (Method m : mm) {
                if (TextUtils.equals(m.getName(), "asBinder")) continue;
                try {
                    funcName = m.toGenericString();
                    funcName = funcName.replace("abstract ", "");
                    funcName = funcName.replace(clsName + ".", "");

                    fieldName = "TRANSACTION_" + m.getName();
                    Field f = nmClassStub.getDeclaredField(fieldName);
                    f.setAccessible(true);
                    int val = f.getInt(null);
                    // android.util.Log.w(TAG, fieldName + " " + val);
                    list.add(new TRANSACTION_Item(val, funcName, fieldName));
                } catch (Exception e) {
                    android.util.Log.e(TAG, e.toString());
                }
            }
            Collections.sort(list, new TRANSACTION_Comparator());
        } catch (Exception e) {
            android.util.Log.e(TAG, e.toString());
        }
        android.util.Log.w(TAG, "<<<<<< " + clsName);
}

排序后可以得到输出的结果:

package android.content.pm;

interface IPackageManager {
    public void checkPackageStartable(java.lang.String,int) throws android.os.RemoteException;// 1
    public boolean isPackageAvailable(java.lang.String,int) throws android.os.RemoteException;// 2
    public android.content.pm.PackageInfo getPackageInfo(java.lang.String,int,int) throws android.os.RemoteException;// 3
    public android.content.pm.PackageInfo getPackageInfoVersioned(android.content.pm.VersionedPackage,int,int) throws android.os.RemoteException;// 4
}

08. 打印JNI调用轨迹

通过拦截JNIEnv层提供调用Java层方法的函数,打印调用信息。

env->FindClass(...);
env->GetMethodID(...);
env->GetStaticMethodID(...);
env->RegisterNatives(...);

09. 打印访问文件行为

通过拦截open系列函数(为什么是系列,因为libc.so经过多年迭代,衍生出来很多函数,所以说Android的兼容性让广大开发者开发Demo容易,做稳定则难上加难),打印文件访问记录。

int open(const char *fileName, int flags, ...);
int open2(const char *fileName, int flags, ...);
int _open(const char *fileName, int flags, ...);
int openat(int dirfd, const char *fileName, int flags, ...);
int _openat(int dirfd, const char *fileName, int flags, ...);
...

至此,通过以上基本的方法,可以快速的增加应用运行轨迹信息。

若还不够,则需要针对应用内部调试,动态库加载的进阶方法深一步剖析,后面会给大家整理进阶技能。

免费评分

参与人数 12威望 +1 吾爱币 +29 热心值 +12 收起 理由
lingyun011 + 1 + 1 热心回复!
jaffa + 1 谢谢@Thanks!
Qiaoyuexuan + 1 + 1 用心讨论,共获提升!
allspark + 1 + 1 用心讨论,共获提升!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hwh425 + 1 用心讨论,共获提升!
fs000x + 1 + 1 用心讨论,共获提升!
woaihaopojie + 1 + 1 感谢分享!
Abner1001 + 1 + 1 又学一招,感谢楼主分享
debug_cat + 1 + 1 我很赞同!
melooon + 1 + 1 我很赞同!
helian147 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
qiong0205 发表于 2024-9-9 12:47
您的回帖是对楼主莫大的鼓励和支持,请不要发布纯表情、连续相同内容等无意义内容的帖子。

免费评分

参与人数 1吾爱币 -1 收起 理由
林伊轩 -1 请勿灌水,提高回帖质量是每位会员应尽的义务!

查看全部评分

沙发
justwz 发表于 2024-9-8 20:25
3#
梦幻恋尘 发表于 2024-9-8 22:27
4#
nan5201314 发表于 2024-9-9 00:16
又学一招
5#
Rwl6688 发表于 2024-9-9 07:14
赞赞赞赞
6#
liehuo2012 发表于 2024-9-9 08:42
谢谢分享,又学了一招
7#
naicha528 发表于 2024-9-9 09:25
谢谢分享,受教了
8#
shiyu1 发表于 2024-9-9 10:39
很有帮助,感谢
10#
马洋洋 发表于 2024-9-9 13:08
感谢大佬分享哈
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-1 09:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表