Android 插件化系列文章目录
【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )
【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )
【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “宿主“ 应用中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )
【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )
文章目录
- Android 插件化系列文章目录
- 前言
- 一、Hook 点分析
- 二、查看 Instrumentation 源码
- 三、分析 Instrumentation.execStartActivity 方法
- 四、分析 ActivityManager 中的源码
- 五、博客资源
前言
在 【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements ) 博客中介绍了从 " 插件包 " APK 文件中获取 Element[] dexElements 流程 ;
在博客 【Android 插件化】Hook 插件化框架 ( 通过反射获取 “宿主“ 应用中的 Element[] dexElements ) 介绍了从 " 宿主 " 应用中获取 Element[] dexElements 流程 ;
在博客 【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 ) 中 , 将上述从 " 插件包 " APK 文件中获取 Element[] dexElements 和 从 " 宿主 " 应用中获取 Element[] dexElements 获取的两个数组进行了合并 ;
在博客 【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 ) 中验证了上述加载的插件包 Dex 字节码是否加载成功 , 并通过反射调用了插件包中的方法 ;
上面一组博客将插件包数据加载到了内存中 , 下面开始进行 Hook Activity 的操作 ,
一、Hook 点分析
在
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
两篇博客中 , 分析了 Activity 的启动流程 ,
在当前应用主进程中 , 在 Activity 中执行 startActivity 方法 , 然后调用 Instrumentation 中的 execStartActivity 方法 ;
然后在 ActivityManagerService 进程中 , 执行后续操作 ; ( 具体细节看上面的两篇博客 )
这里只需要研究 Activity 中执行 startActivity 方法的情况 , 因为插件化只涉及在 " 宿主 " 应用中 , 启动 " 插件 " 包中的 Activity 类 , 只需要考虑这一种情况即可 ;
Activity 的所有的生命周期函数 , 都与 Instrumentation 相关 , Instrumentation 会处理 Activity 实例化等操作 ;
二、查看 Instrumentation 源码
查看 Instrumentation 相关代码 , 双 Shift 搜索界面中 , 选中 " Include non-project items " 选项 , 当前的编译版本是 28 , 因此这里选择 API 28 中的 Instrumentation.java 源码 ;
三、分析 Instrumentation.execStartActivity 方法
启动 Activity 时 , 调用的是下面的 Instrumentation.execStartActivity 方法 ;
代码语言:javascript复制public class Instrumentation {
/**
* Execute a startActivity call made by the application. The default
* implementation takes care of updating any active {@link ActivityMonitor}
* objects and dispatches this call to the system activity manager; you can
* override this to watch for the application to start an activity, and
* modify what happens when it does.
*
* <p>This method returns an {@link ActivityResult} object, which you can
* use when intercepting application calls to avoid performing the start
* activity action but still return the result the application is
* expecting. To do this, override this method to catch the call to start
* activity so that it returns a new ActivityResult containing the results
* you would like the application to see, and don't call up to the super
* class. Note that an application is only expecting a result if
* <var>requestCode</var> is >= 0.
*
* <p>This method throws {@link android.content.ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
*
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
* is being started.
* @param token Internal token identifying to the system who is starting
* the activity; may be null.
* @param target Which activity is performing the start (and thus receiving
* any result); may be null if this call is not being made
* from an activity.
* @param intent The actual Intent to start.
* @param requestCode Identifier for this request's result; less than zero
* if the caller is not expecting a result.
* @param options Addition options.
*
* @return To force the return of a particular result, return an
* ActivityResult object containing the desired data; otherwise
* return null. The default implementation always returns null.
*
* @throws android.content.ActivityNotFoundException
*
* @see Activity#startActivity(Intent)
* @see Activity#startActivityForResult(Intent, int)
* @see Activity#startActivityFromChild
*
* {@hide}
*/
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;
}
}
在上述方法最后 , 调用了 AMS 的 startActivity 方法 , ActivityManager.getService().startActivity() 方法最终是 ActivityManagerService 执行的 ;
由于当前主线程与 ActivityManagerService 不再同一个进程中 , 因此需要使用 Binder 进行调用 ;
代码语言:javascript复制 int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
四、分析 ActivityManager 中的源码
在 ActivityManager 中的 getService 方法 , 获取的
代码语言:javascript复制 /**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
IActivityManager 是 Binder 的 Proxy , Binder 下存在 Stub 和 Proxy 两个内部类 , Binder 生成的 Java 类时内部生成上述 Stub 和 Proxy 两个内部类 ;
反射的时候 , 反射
代码语言:javascript复制final IActivityManager am = IActivityManager.Stub.asInterface(b);
对象 , 最终调用 startActivity 的是 IActivityManager , 使用占坑的 Activity 隐瞒 IActivityManager , 实际上启动我们从插件包中加载的 Activity ;
Hook 点就是 android.app.ActivityManager
的 private static final Singleton<IActivityManager> IActivityManagerSingleton
成员 ;
五、博客资源
博客资源 :
- GitHub : https://github.com/han1202012/Plugin_Hook