文章目录
Android 插件化系列文章目录
前言
一、分析 Activity 启动源码
1、源码分析
2、涉及到的 Activity 相关代码
二、Hook Activity 启动过程
1、分析相关 类 / 成员 / 方法 结构
2、反射获取 Activity 中的 Instrumentation mInstrumentation 成员字段
3、获取 Activity 中的 Instrumentation mInstrumentation 成员字段值
4、设置 Activity 中的 Instrumentation mInstrumentation 成员字段值
5、代理类开发
三、完整代码示例
1、主界面代码示例
2、代理类代码示例
3、跳转的界面
4、执行结果
四、博客资源
前言
上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 ) 简要介绍了 Hook 实现思路 , 以及使用静态代理实现了 Hook 按钮点击事件 ;
在本博客中使用 Hook 技术进行 Hook 住 Activity 启动过程 ;
一、分析 Activity 启动源码
1、源码分析
在 " 宿主 " 模块中 , 启动 " 插件 " 模块 , 调用的是 startActivity 方法 ;
如果要 Hook Activity 的启动过程 , 必须熟悉启动 Activity 的源码 , 下面开始分析 调用 startActivity 方法的源码逻辑 ; 在 Activity 中启动另外一个 Activity , 调用 void startActivity(Intent intent) 方法 , @Override public void startActivity(Intent intent) { this.startActivity(intent, null); }
在该 void startActivity(Intent intent) 方法中主要调用 void startActivity(Intent intent, @Nullable Bundle options) 重载方法 ;
@Override public void startActivity(Intent intent, @Nullable Bundle options) { if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN) && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) { if (TextUtils.equals(getPackageName(), intent.resolveActivity(getPackageManager()).getPackageName())) { // Apply Autofill restore mechanism on the started activity by startActivity() final IBinder token = mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN); // Remove restore ability from current activity mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN); mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY); // Put restore token intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token); intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true); } } if (options != null) { startActivityForResult(intent, -1, options); } else { // Note we want to go through this call for compatibility with // applications that may have overridden the method. startActivityForResult(intent, -1); } }
在 void startActivity(Intent intent, @Nullable Bundle options) 中 , 最终调用了 void startActivityForResult(@RequiresPermission Intent intent, int requestCode) 方法 启动 Activity ;
void startActivityForResult(@RequiresPermission Intent intent, int requestCode) 方法最终也是调用 void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) 重载方法 , 最后一个参数设置为 null ;
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) { startActivityForResult(intent, requestCode, null); } public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } if (requestCode >= 0) { // If this start is requesting a result, we can avoid making // the activity visible until the result is received. Setting // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the // activity hidden during this time, to avoid flickering. // This can only be done when a result is requested because // that guarantees we will get information back when the // activity is finished, no matter what happens to it. mStartedActivity = true; } cancelInputsAndStartExitTransition(options); // TODO Consider clearing/flushing other event sources and events for child windows. } else { if (options != null) { mParent.startActivityFromChild(this, intent, requestCode, options); } else { // Note we want to go through this method for compatibility with // existing applications that may have overridden it. mParent.startActivityFromChild(this, intent, requestCode); } } }
在 void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) 方法中 , 调用了 Instrumentation mInstrumentation 成员的 execStartActivity 方法 , 启动 Activity ;
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
通过 Hook 该 execStartActivity 方法 , 使用该方法启动的 Activity 有完整的上下文环境 ;
2、涉及到的 Activity 相关代码
Activity 相关完整代码 :
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { @Override public void startActivity(Intent intent) { this.startActivity(intent, null); } @Override public void startActivity(Intent intent, @Nullable Bundle options) { if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN) && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) { if (TextUtils.equals(getPackageName(), intent.resolveActivity(getPackageManager()).getPackageName())) { // Apply Autofill restore mechanism on the started activity by startActivity() final IBinder token = mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN); // Remove restore ability from current activity mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN); mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY); // Put restore token intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token); intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true); } } if (options != null) { startActivityForResult(intent, -1, options); } else { // Note we want to go through this call for compatibility with // applications that may have overridden the method. startActivityForResult(intent, -1); } } public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) { startActivityForResult(intent, requestCode, null); } public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } if (requestCode >= 0) { // If this start is requesting a result, we can avoid making // the activity visible until the result is received. Setting // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the // activity hidden during this time, to avoid flickering. // This can only be done when a result is requested because // that guarantees we will get information back when the // activity is finished, no matter what happens to it. mStartedActivity = true; } cancelInputsAndStartExitTransition(options); // TODO Consider clearing/flushing other event sources and events for child windows. } else { if (options != null) { mParent.startActivityFromChild(this, intent, requestCode, options); } else { // Note we want to go through this method for compatibility with // existing applications that may have overridden it. mParent.startActivityFromChild(this, intent, requestCode); } } } }
二、Hook Activity 启动过程
1、分析相关 类 / 成员 / 方法 结构
要 Hook 的方法是 Instrumentation 的 execStartActivity 方法 ;
public class Instrumentation { @UnsupportedAppUsage public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { } }
Activity 中维护了 Instrumentation mInstrumentation 成员变量 ;
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called. @UnsupportedAppUsage private Instrumentation mInstrumentation; }
2、反射获取 Activity 中的 Instrumentation mInstrumentation 成员字段
首先 , 任何反射操作 , 都要获取其字节码文件 , 作为反射的入口 , 这里先获取 Activity 字节码对象 , 直接通过 Activity.class 获取即可 ;
// 1. 获取 Activity 字节码文件 // 字节码文件是所有反射操作的入口 Class<?> clazz = Activity.class;
然后 , 获取 Activity 中的 Instrumentation mInstrumentation 成员 Field 字段 ;
// 2. 获取 Activity 的 Instrumentation mInstrumentation 成员 Field 字段 Field mInstrumentation_Field = null; try { mInstrumentation_Field = clazz.getDeclaredField("mInstrumentation"); } catch (NoSuchFieldException e) { e.printStackTrace(); }
最后 , 设置 Field mInstrumentation 字段的可访问性 , 只要是调用反射方法 , 或者访问反射的成员字段 , 第一件事就是设置可访问性 ;
正常可访问的方法或字段 , 绝对不会使用反射获取 , 既然使用了反射 , 那么设置可访问性是标配操作 ;
// 3. 设置 Field mInstrumentation 字段的可访问性
mInstrumentation_Field.setAccessible(true);
// 3. 设置 Field mInstrumentation 字段的可访问性 mInstrumentation_Field.setAccessible(true);
本步骤完整代码示例 :
// 1. 获取 Activity 字节码文件 // 字节码文件是所有反射操作的入口 Class<?> clazz = Activity.class; // 2. 获取 Activity 的 Instrumentation mInstrumentation 成员 Field 字段 Field mInstrumentation_Field = null; try { mInstrumentation_Field = clazz.getDeclaredField("mInstrumentation"); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 3. 设置 Field mInstrumentation 字段的可访问性 mInstrumentation_Field.setAccessible(true);
3、获取 Activity 中的 Instrumentation mInstrumentation 成员字段值
获取 Activity 的 Instrumentation mInstrumentation 成员对象值 , 该成员值就是需要被代理的目标对象 ;
代理者 需要 持有 被代理的目标对象 ;
获取该成员的意义是 , 创建 Instrumentation 代理时, 需要将原始的 Instrumentation 传入代理对象中 ;
// 4. 获取 Activity 的 Instrumentation mInstrumentation 成员对象值 // 获取该成员的意义是 , 创建 Instrumentation 代理时, 需要将原始的 Instrumentation 传入代理对象中 Instrumentation mInstrumentation = null; try { mInstrumentation = (Instrumentation) mInstrumentation_Field.get(this); } catch (IllegalAccessException e) { e.printStackTrace(); }
4、设置 Activity 中的 Instrumentation mInstrumentation 成员字段值
设置 Activity 中的 Instrumentation mInstrumentation 成员字段值 , 将 Activity 的 Instrumentation mInstrumentation 成员变量 设置为自己定义的 Instrumentation 代理对象 ;
此处使用的是静态代理 ;
// 5. 将 Activity 的 Instrumentation mInstrumentation 成员变量 // 设置为自己定义的 Instrumentation 代理对象 try { mInstrumentation_Field.set(this, new InstrumentationProxy(mInstrumentation)); } catch (IllegalAccessException e) { e.printStackTrace(); }
5、代理类开发
被代理的目标对象是 Activity 中的 Instrumentation mInstrumentation 成员变量 ;
代理类中需要持有上述成员变量 , 通过反射获取该成员 , 并设置给代理者 ;
在代理类中 , 继承 Instrumentation 类 , 这是因为还需要通过反射 , 将代理类设置给 Activity 的 Instrumentation mInstrumentation 成员 , 以达到偷梁换柱的目的 , 档 Activity 调用 Instrumentation mInstrumentation 成员时 , 其实调用的是开发者开发的代理类 ;
在 Android 界面跳转时 , 会自动回调 Activity 中的 Instrumentation mInstrumentation 成员的 execStartActivity 方法 ;
实际上调用的是代理类的 execStartActivity 方法 ;
在代理类 execStartActivity 方法中 , 首先调用持有的 Activity 中原本的 Instrumentation mInstrumentation 成员的 execStartActivity 方法 , 然后在该方法的前面 , 后面 , 可以添加自己的业务逻辑 , 该方法的执行参数也可以进行修改 ;
这样就成功将自己的业务逻辑注入到了 Activity 启动过程中 ;
代码示例 :
package com.example.plugin_hook; import android.app.Activity; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class InstrumentationProxy extends Instrumentation { private static final String TAG = "InstrumentationProxy"; /** * Activity 中原本的 Instrumentation mInstrumentation 成员 * 从构造函数中进行初始化 */ final Instrumentation orginalInstrumentation; public InstrumentationProxy(Instrumentation orginalInstrumentation) { this.orginalInstrumentation = orginalInstrumentation; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { Log.i(TAG, "注入的 Hook 前执行的业务逻辑"); // 1. 反射执行 Instrumentation orginalInstrumentation 成员的 execStartActivity 方法 Method execStartActivity_Method = null; try { execStartActivity_Method = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 2. 设置方法可访问性 execStartActivity_Method.setAccessible(true); // 3. 执行 Instrumentation orginalInstrumentation 的 execStartActivity 方法 // 使用 Object 类型对象接收反射方法执行结果 ActivityResult activityResult = null; try { activityResult = (ActivityResult) execStartActivity_Method.invoke(orginalInstrumentation, who, contextThread, token, target, intent, requestCode, options); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } Log.i(TAG, "注入的 Hook 后执行的业务逻辑"); return activityResult; } }
三、完整代码示例
1、主界面代码示例
主界面代码示例 :
package com.example.plugin_hook; import androidx.appcompat.app.AppCompatActivity; import android.app.Activity; import android.app.Instrumentation; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取按钮 , 并未按钮组件设置点击事件 Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i(TAG, "Button OnClickListener onClick"); startActivity(new Intent(MainActivity.this, MainActivity2.class)); } }); hookOnClick(button); hookStartActivity(); } @Override public void startActivity(Intent intent) { super.startActivity(intent); } /** * hook Button 组件的 getListenerInfo 方法 * @param view */ private void hookOnClick(View view){ // 获取 View 的 getListenerInfo 方法 Method getListenerInfo = null; try { getListenerInfo = View.class.getDeclaredMethod("getListenerInfo"); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 getListenerInfo.setAccessible(true); // 执行 View view 对象的 getListenerInfo 方法 Object mListenerInfo = null; try { mListenerInfo = getListenerInfo.invoke(view); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // 反射获取 OnClickListener 成员 // ① 先根据全类名获取 ListenerInfo 字节码 Class<?> clazz = null; try { clazz = Class.forName("android.view.View$ListenerInfo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // ② 获取 android.view.View.ListenerInfo 中的 mOnClickListener 成员 Field field = null; try { field = clazz.getField("mOnClickListener"); } catch (NoSuchFieldException e) { e.printStackTrace(); } // ③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 field.setAccessible(true); // ④ 获取 mOnClickListener 成员变量 View.OnClickListener mOnClickListener = null; try { mOnClickListener = (View.OnClickListener) field.get(mListenerInfo); } catch (IllegalAccessException e) { e.printStackTrace(); } // ⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员 // 其中 ListenerInfo 成员 是 try { View.OnClickListener finalMOnClickListener = mOnClickListener; field.set(mListenerInfo, new View.OnClickListener(){ @Override public void onClick(View v) { Log.i(TAG, "Hook Before"); finalMOnClickListener.onClick(view); Log.i(TAG, "Hook After"); } }); } catch (IllegalAccessException e) { e.printStackTrace(); } } /** * Hook Activity 界面启动过程 * 钩住 Instrumentation 的 execStartActivity 方法 * 向该方法中注入自定义的业务逻辑 */ private void hookStartActivity(){ // 1. 获取 Activity 字节码文件 // 字节码文件是所有反射操作的入口 Class<?> clazz = Activity.class; // 2. 获取 Activity 的 Instrumentation mInstrumentation 成员 Field 字段 Field mInstrumentation_Field = null; try { mInstrumentation_Field = clazz.getDeclaredField("mInstrumentation"); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 3. 设置 Field mInstrumentation 字段的可访问性 mInstrumentation_Field.setAccessible(true); // 4. 获取 Activity 的 Instrumentation mInstrumentation 成员对象值 // 获取该成员的意义是 , 创建 Instrumentation 代理时, 需要将原始的 Instrumentation 传入代理对象中 Instrumentation mInstrumentation = null; try { mInstrumentation = (Instrumentation) mInstrumentation_Field.get(this); } catch (IllegalAccessException e) { e.printStackTrace(); } // 5. 将 Activity 的 Instrumentation mInstrumentation 成员变量 // 设置为自己定义的 Instrumentation 代理对象 try { mInstrumentation_Field.set(this, new InstrumentationProxy(mInstrumentation)); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
2、代理类代码示例
代理类代码示例 :
package com.example.plugin_hook; import android.app.Activity; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class InstrumentationProxy extends Instrumentation { private static final String TAG = "InstrumentationProxy"; /** * Activity 中原本的 Instrumentation mInstrumentation 成员 * 从构造函数中进行初始化 */ final Instrumentation orginalInstrumentation; public InstrumentationProxy(Instrumentation orginalInstrumentation) { this.orginalInstrumentation = orginalInstrumentation; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { Log.i(TAG, "注入的 Hook 前执行的业务逻辑"); // 1. 反射执行 Instrumentation orginalInstrumentation 成员的 execStartActivity 方法 Method execStartActivity_Method = null; try { execStartActivity_Method = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 2. 设置方法可访问性 execStartActivity_Method.setAccessible(true); // 3. 执行 Instrumentation orginalInstrumentation 的 execStartActivity 方法 // 使用 Object 类型对象接收反射方法执行结果 ActivityResult activityResult = null; try { activityResult = (ActivityResult) execStartActivity_Method.invoke(orginalInstrumentation, who, contextThread, token, target, intent, requestCode, options); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } Log.i(TAG, "注入的 Hook 后执行的业务逻辑"); return activityResult; } }
3、跳转的界面
跳转的界面 :
package com.example.plugin_hook; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); } }
4、执行结果
按钮点击事件 , 在调用按钮事件的前后 , 注入的业务逻辑 , 分别打印 Hook Before 和 Hook After ;
Activity 启动过程 , Hook 住了 Activity 的 Instrumentation mInstrumentation 成员的 execStartActivity 方法 , 在调用该方法的 前后 , 各注入的业务逻辑中打印 注入的 Hook 前执行的业务逻辑 和 注入的 Hook 后执行的业务逻辑 ;
I/MainActivity: Hook Before I/MainActivity: Button OnClickListener onClick I/InstrumentationProxy: 注入的 Hook 前执行的业务逻辑 I/InstrumentationProxy: 注入的 Hook 后执行的业务逻辑 I/MainActivity: Hook After
四、博客资源
博客资源 :
GitHub : https://github.com/han1202012/Plugin_Hook