【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )

简介: 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )

文章目录

Android 插件化系列文章目录

前言

一、Activity 任务栈相关源码

1、任务栈管理者 ActivityStackSupervisor

2、任务栈 ActivityStack

3、Activity 启动涉及到的组件

二、Activity 进程相关源码

1、Instrumentation 源码分析

三、博客资源

前言

上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 ) 使用了静态代理 , hook 了 Activity 的启动过程 ;


在 hook Android 的内部流程时 , 注意版本兼容 , 不同的 Android 版本底层源码实现机制可能有区别 , 需要使用不同的 hook 兼容方式 ;


hook 本身实现起来很简单 , 但是其 与底层源码耦合性太高 , 在 Android 8.0 8.08.0 可以 hook 住的方法 , 在 Android 10.0 10.010.0 可能就无法使用了 ;


Hook 插件化框架的 难点是版本兼容 , 需要逐个手动兼容 Android 低版本到最新版本 , 一旦系统更新 , 或者某厂商 ROM 更新 , 都要进行兼容测试以及改进 ;


如果 Android 高版本禁止反射 @hide 方法 , 可以在 调用链上找到一个非隐藏的方法 , 总能 hook 住 ; 极端情况下 , 使用 动态字节码技术 , 在运行时修改字节码数据 , 删除 @hide 注解 ;


插件化模块选择 : 一般的业务逻辑不建议使用插件化 ; 功能比较单一 , 业务逻辑更新比较频繁 , 并且很重要的模块 , 使用插件化实现 ;



插件化框架主要是 通过 hook 修改 Instrumentation , 以及 劫持 ActivityManagerService ;



源码分析的大忌就是死磕每一行源码的细节 , 只看自己能看懂的 , 每个方法最多看 2 22 层 , 不要偏离主线 ;

现在的源码参考资料很多 , 参考别人已经分析完毕的源码经验 , 可以节省很多时间 ;






一、Activity 任务栈相关源码


基于 Android 28 2828 源码 , 分析 Activity 的启动过程 ; ( Android 27 2727 , 28 2828 , 29 2929 中 Android 启动源码都进行了不同程度的改进 , 3 33 个版本的源码是不同的 )




1、任务栈管理者 ActivityStackSupervisor


Activity 任务栈 : ActivityStack ; Activity 启动后 , 都加入到 ActivityStack ( 任务栈 ) 中 ;


任务栈管理者 : ActivityStack 由 ActivityStackSupervisor 来管理 , ActivityStackSupervisor 中有两个数组 , 分别是


mHomeStack : Launcher 应用使用的任务栈 ;

mFocusedStack : 当前聚焦的任务栈 , 可以接收输入 , 或启动下一个 Activity ;

public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
        RecentTasks.Callbacks {
    /** The stack containing the launcher app. Assumed to always be attached to
     * Display.DEFAULT_DISPLAY. */
    ActivityStack mHomeStack;
    /** The stack currently receiving input or launching the next activity. */
    ActivityStack mFocusedStack;
}


源码地址 : frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java



2、任务栈 ActivityStack


ActivityStack ( 任务栈 ) 中存在两个集合 :


ArrayList<TaskRecord> mTaskHistory : 之前运行的 ( 可能仍在运行 ) 的 Activity 的历史记录 , 每个 TaskRecord 都包含了 1 11 个 ActivityRecord 集合 ;

ArrayList<ActivityRecord> mLRUActivities : 当前正在运行的 Activity 列表 , 按照最近最少使用算法 LRU 机制进行排序 , 列表中第一个 Activity 是最近最少使用的 ;

ActivityRecord 就是 Activity 的信息 , 注意不是 Activity 的实例对象 , 是历史任务栈中的一个条目 , 可以代表一个 Activity ;


TaskRecord 中 维护了 1 11 个 ArrayList<ActivityRecord> , 用于保存 ActivityRecord ;


class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
        implements StackWindowListener {
    /**
     * The back history of all previous (and possibly still
     * running) activities.  It contains #TaskRecord objects.
     */
    private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
    /**
     * List of running activities, sorted by recent usage.
     * The first entry in the list is the least recently used.
     * It contains HistoryRecord objects.
     */
    final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>();
}


3、Activity 启动涉及到的组件


ActivityThread : 应用主线程 , 每个应用都是从该主线程的 main 函数开始的 ;


/frameworks/base/core/java/android/app/ActivityThread.java

Instrumentation : 每个 Activity 都持有该类对象 , 档调用 startActivity 启动其它 Activity 时 , 就会调用 Instrumentation 进行先关操作 ; ActivityThread 控制 Activity 也是通过该类进行 ; 一个应用中只有一个 Instrumentation 实例对象 ;


/frameworks/base/core/java/android/app/Instrumentation.java





二、Activity 进程相关源码



1、Instrumentation 源码分析


在上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 ) 一、分析 Activity 启动源码 章节中分析到 , 在 Activity 中调用 startActivity , 最终调用的是 Instrumentation 的 execStartActivity 方法 ;


在 Instrumentation 中的 newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) 方法 , 用于创建 Activity 实例 , 其中使用了 (Activity)clazz.newInstance() 创建 Activity 示例 ,


public Activity newActivity(Class<?> clazz, Context context, 
            IBinder token, Application application, Intent intent, ActivityInfo info, 
            CharSequence title, Activity parent, String id,
            Object lastNonConfigurationInstance) throws InstantiationException, 
            IllegalAccessException {
        Activity activity = (Activity)clazz.newInstance();
        ActivityThread aThread = null;
        // Activity.attach expects a non-null Application Object.
        if (application == null) {
            application = new Application();
        }
        activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,
                info, title, parent, id,
                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                new Configuration(), null /* referrer */, null /* voiceInteractor */,
                null /* window */, null /* activityConfigCallback */);
        return activity;
    }


在另外一个重载的 Activity newActivity(ClassLoader cl, String className, Intent intent) 方法中 , 通过指定 类加载器 ClassLoader , Activity 的全类名 , 也可以创建 Activity 实例对象 ;



Hook 劫持 Activity newActivity(ClassLoader cl, String className, Intent intent) 方法 , 传入插件包的类加载器 , 和插件包的类名 , 此时就可以初始化带上下文的 Activity ,


public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        String pkg = intent != null && intent.getComponent() != null
                ? intent.getComponent().getPackageName() : null;
        return getFactory(pkg).instantiateActivity(cl, className, intent);
    }



public class Instrumentation {
    public Activity newActivity(Class<?> clazz, Context context, 
            IBinder token, Application application, Intent intent, ActivityInfo info, 
            CharSequence title, Activity parent, String id,
            Object lastNonConfigurationInstance) throws InstantiationException, 
            IllegalAccessException {
        Activity activity = (Activity)clazz.newInstance();
        ActivityThread aThread = null;
        // Activity.attach expects a non-null Application Object.
        if (application == null) {
            application = new Application();
        }
        activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,
                info, title, parent, id,
                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                new Configuration(), null /* referrer */, null /* voiceInteractor */,
                null /* window */, null /* activityConfigCallback */);
        return activity;
    }
    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        String pkg = intent != null && intent.getComponent() != null
                ? intent.getComponent().getPackageName() : null;
        return getFactory(pkg).instantiateActivity(cl, className, intent);
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        result = am.onStartActivity(intent);
                    }
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
}



三、博客资源


博客资源 :


GitHub : https://github.com/han1202012/Plugin_Hook


目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
241 4
|
2月前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
1月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
33 8
|
2月前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
57 1
|
7月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
148 13
|
6月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
6月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
205 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
5月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
6月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
189 1
|
6月前
|
小程序 Linux
【编程小实验】利用Linux fork()与文件I/O:父进程与子进程协同实现高效cp命令(前半文件与后半文件并行复制)
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
137 2