Hook 替换启动的 Activity

由于特殊需求,需要在应用启动的时候动态替换启动的 Activity,参考插件化原理,在 Application 阶段进行 hook ,从而拿到 AcitvityThreadmH,然后在启动 activity 的时候进行替换

设备 Android 8.1,其他 Android 版本可能需要兼容

实例

应用包含两个界面,SplashActivity 和 MainActivity。在桌面点击应用图标,启动 SplashActivity。打印启动流程

1
2
3
4
5
6
7
8
9
E/xxxxxx: hookActivityThreadHHanlder: activityThread=android.app.ActivityThread@eaa28fb
E/xxxxxx: hookActivityThreadHHanlder: mh=Handler (android.app.ActivityThread$H) {4121d18}
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-103ms what=100 obj=ActivityRecord{dfa7556 token=android.os.BinderProxy@9e859d7 {pub.hanks.testhooksplashactivity/pub.hanks.testhooksplashactivity.SplashActivity}} target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: obj=ActivityRecord{dfa7556 token=android.os.BinderProxy@9e859d7 {pub.hanks.testhooksplashactivity/pub.hanks.testhooksplashactivity.SplashActivity}}
W/xxxxxx: SplashActivity.onCreate=pub.hanks.testhooksplashactivity.SplashActivity@56a8fa9
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-51ms what=140 arg1=5 target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-127ms what=149 obj=android.os.BinderProxy@9e859d7 target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-1ms what=1010 target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-10ms what=132 target=android.app.ActivityThread$H }

在handler处理消息过程中,判断是 LAUNCH_ACTIVITY(100) 并且是 SplashActivity,替换成 MainActivity,此处注意 Android P上开始,启动 activity 变成了 EXECUTE_TRANSACTION(159)

1
2
3
4
5
6
7
8
9
// Application
public class HookApplication extends Application {

@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
HookHelper.hookActivityThreadHHanlder();
}
}

找到 activityThread 的 mH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// hook 并替换
public static void hookActivityThreadHHanlder() {
try {
// 获取 activityThread
Class<?> aClass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThread = aClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThread.setAccessible(true);
Object activityThread = sCurrentActivityThread.get(aClass);

Log.e(TAG, "hookActivityThreadHHanlder: activityThread=" + activityThread);

//获取 mH
Field mHField = aClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mh = (Handler) mHField.get(activityThread);

Log.e(TAG, "hookActivityThreadHHanlder: mh=" + mh);

Class<?> handlerClass = Class.forName("android.os.Handler");
Field mCallbackField = handlerClass.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mh, new android.os.Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
// 处理启动 activity 的事件
handleLaunchActivityMessage(msg);
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

找到对应的 Message,然后进行替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

public static final int LAUNCH_ACTIVITY = 100;
public static final int EXECUTE_TRANSACTION = 159;

private static void handleLaunchActivityMessage(Message msg) {
Log.e(TAG, "hookActivityThreadHHanlder: msg=" + msg);
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Object obj = msg.obj;
Log.e(TAG, "hookActivityThreadHHanlder: obj=" + obj);
try {
Field intentField = HackHelper.getField(obj.getClass(), "intent");
intentField.setAccessible(true);
Intent intent = (Intent) intentField.get(obj);
if ("pub.hanks.testhooksplashactivity.SplashActivity".equals(intent.getComponent().getClassName())) {
Log.e(TAG, "hookActivityThreadHHanlder: 原始 intent =" + intent);
intent.setComponent(new ComponentName("pub.hanks.testhooksplashactivity", "pub.hanks.testhooksplashactivity.MainActivity"));
Log.e(TAG, "hookActivityThreadHHanlder: 替换后的 intent intentCopy=" + intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
break;

case EXECUTE_TRANSACTION: // 适配 androidP, HackHelper 突破 P 上反射限制
{
Object object = msg.obj;
Log.e(TAG, "hookActivityThreadHHanlder: 159 obj=" + object);
try {
//拿到ClientTransaction中的列表:mActivityCallbacks
Field mActivityCallbacksField = HackHelper.getField(object.getClass(), "mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List<Object> mActivityCallbacks = (List<Object>) mActivityCallbacksField.get(object);
//拿到LaunchActivityItem的实例对象.
String itemName = "android.app.servertransaction.LaunchActivityItem";
for (Object obj : mActivityCallbacks) {
Log.e(TAG, "hookActivityThreadHHanlder: mActivityCallbacks=" + obj);
if (itemName.equals(obj.getClass().getCanonicalName())) {
//拿到LaunchActivityItem中的mIntent.
Field mIntentField = HackHelper.getField(obj.getClass(), "mIntent");
mIntentField.setAccessible(true);
Intent intent = (Intent) mIntentField.get(obj);
if ("pub.hanks.testhooksplashactivity.SplashActivity".equals(intent.getComponent().getClassName())) {
Log.e(TAG, "hookActivityThreadHHanlder: 原始 intent =" + intent);
intent.setComponent(new ComponentName("pub.hanks.testhooksplashactivity", "pub.hanks.testhooksplashactivity.MainActivity"));
Log.e(TAG, "hookActivityThreadHHanlder: 替换后的 intent intentCopy=" + intent);
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
break;

default:
break;
}
}

hook 后成功替换了 SplashActivity, 直接启动了 MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
E/xxxxxx: hookActivityThreadHHanlder: activityThread=android.app.ActivityThread@eaa28fb
E/xxxxxx: hookActivityThreadHHanlder: mh=Handler (android.app.ActivityThread$H) {4121d18}
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-95ms what=140 arg1=5 target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-100ms what=100 obj=ActivityRecord{dfa7556 token=android.os.BinderProxy@9e859d7 {pub.hanks.testhooksplashactivity/pub.hanks.testhooksplashactivity.SplashActivity}} target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: obj=ActivityRecord{dfa7556 token=android.os.BinderProxy@9e859d7 {pub.hanks.testhooksplashactivity/pub.hanks.testhooksplashactivity.SplashActivity}}
E/xxxxxx: hookActivityThreadHHanlder: 原始 intent =Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=pub.hanks.testhooksplashactivity/.SplashActivity (has extras) }
E/xxxxxx: hookActivityThreadHHanlder: 替换后的 intent intentCopy=Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=pub.hanks.testhooksplashactivity/.MainActivity (has extras) }
W/xxxxxx: MainActivity.onCreate=pub.hanks.testhooksplashactivity.MainActivity@56a8fa9
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-26ms what=149 obj=android.os.BinderProxy@9e859d7 target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-2ms what=1010 target=android.app.ActivityThread$H }
E/xxxxxx: hookActivityThreadHHanlder: msg={ when=-9ms what=132 target=android.app.ActivityThread$H }

文章来自: https://hanks.pub