接下来的系列文章会学习DroidPlugin对于Android四大组件的处理方式,我们且看它如何采用Hook技术坑蒙拐骗把系统玩弄于股掌之中,最终赋予Activity,Service等组件生命周期,完成借尸还魂的。
首先,在学DroidPlugin对于Activity组件的处理方式。首先得先简单了解Activity的启动流程,这里推荐
老罗的博文:http://blog.csdn.net/luoshengyang/article/details/6685853
在这个图中,ActivityManagerService和ActivityStack位于同一个进程中,而ApplicationThread和ActivityThread位于另一个进程中。其中,ActivityManagerService是负责管理Activity的生命周期的,ActivityManagerService还借助ActivityStack是来把所有的Activity按照后进先出的顺序放在一个堆栈中;对于每一个应用程序来说,都有一个ActivityThread来表示应用程序的主进程,而每一个ActivityThread都包含有一个ApplicationThread实例,而ApplicationThread也不是一个Thread,是一个Binder,主要用于应用进程和ActivityManagerService进程间通信的
private class ApplicationThread extends ApplicationThreadNative
......
......
public abstract class ApplicationThreadNative extends Binder
implements IApplicationThread {
下面简要介绍一下启动的过程:
Step 1. 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口;
Step 2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;
Step 3. ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;
Step 4. ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;
Step 5. 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;
Step 6. ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;
Step 7. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。
App进程与AMS进程的通信过程如图所示:
通过上文的分析,我们已经对Activity的启动过程简单理解了,最近一段时间要做插件化开发,后续有时间在去细细琢磨老罗的文章
对与『必须在AndroidManifest.xml中显示声明使用的Activity』这个问题,上文给出了思路——瞒天过海;我们可以在AndroidManifest.xml里面声明一个替身Activity,然后在合适的时候把这个假的替换成我们真正需要启动的Activity就OK了。
先从App进程调用startActivity;然后通过IPC调用进入系统进程system_server,完成Activity管理以及一些校检工作,最后又回到了APP进程完成真正的Activioty对象创建。
由于这个检验过程是在AMS进程完成的,我们对system_server进程里面的操作无能为力,只有在我们APP进程里面执行的过程才是有可能被Hook掉的,也就是第一步和第三步;具体应该怎么办呢?
既然需要一个显式声明的Activity,那就声明一个!可以在第一步假装启动一个已经在AndroidManifest.xml里面声明过的替身Activity,让这个Activity进入AMS进程接受检验;最后在第三步的时候换成我们真正需要启动的Activity;这样就成功欺骗了AMS进程,瞒天过海!
实战过程
具体来说,我们打算实现如下功能:在MainActivity中启动一个并没有在AndroidManifest.xml中声明的TargetActivity;按照上文分析,我们需要声明一个替身Activity,我们叫它StubActivity;
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.weishu.intercept_activity.app">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 替身Activity, 用来欺骗AMS -->
<activity android:name=".StubActivity" />
</application>
</manifest>
OK,那么我们在MainActivity中启动TargetActivity很简单,就是个startActivity调用的事:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = new Button(this);
button.setText("启动TargetActivity");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 启动目标Activity; 注意这个Activity是没有在AndroidManifest.xml中显式声明的
// 但是调用者并不需要知道, 就像一个普通的Activity一样
startActivity(new Intent(MainActivity.this, TargetActivity.class));
}
});
setContentView(button);
}
如果你直接这么运行,肯定会直接抛出ActivityNotFoundException然后直接退出;我们接下来要做的就是让这个调用成功启动TargetActivity。
由于AMS进程会对Activity做显式声明验证,因此在
启动Activity的控制权转移到AMS进程之前,我们需要想办法临时把TargetActivity替换成替StubActivity;
我们来看ContextImpl中的startActivity的代码
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}
最终调用了execStartActivity方法,我们继续看
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
我们应该Hook掉ActivityManagerNative对于startActivity方法的调用,替换掉交给AMS的intent对象,将里面的TargetActivity的暂时替换成已经声明好的替身StubActivity;
/**
* Hook AMS
* <p/>
* 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity"
* <p/>
* 进而骗过AMS
*/
public static void hookActivityManagerNative() throws ClassNotFoundException,
NoSuchMethodException, InvocationTargetException,
IllegalAccessException, NoSuchFieldException {
// 26public abstract class Singleton<T> {
// 27 private T mInstance;
// 29 protected abstract T create();
// 30
// 31 public final T get() {
// 32 synchronized (this) {
// 33 if (mInstance == null) {
// 34 mInstance = create();
// 35 }
// 36 return mInstance;
// 37 }
// 38 }
// 39}
// 40
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
Class<?> singleton = Class.forName("android.util.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象
Object rawIActivityManager = mInstanceField.get(gDefault);
// 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager));
mInstanceField.set(gDefault, proxy);
}
package com.weishu.intercept_activity.app.hook;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import android.content.ComponentName;
import android.content.Intent;
import android.util.Log;
import com.weishu.intercept_activity.app.StubActivity;
class IActivityManagerHandler implements InvocationHandler {
private static final String TAG = "IActivityManagerHandler";
Object mBase;
public IActivityManagerHandler(Object base) {
mBase = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
// 只拦截这个方法
// 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱
// API 23:
// public final Activity startActivityNow(Activity parent, String id,
// Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
// Activity.NonConfigurationInstances lastNonConfigurationInstances) {
// 找到参数里面的第一个Intent 对象
Intent raw;
int index = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
raw = (Intent) args[index];
Log.i(TAG,"---raw---"+raw.toString());
/以上是获取new Intent(MainActivity.this, TargetActivity.class)
Intent newIntent = new Intent();
// 替身Activity的包名, 也就是我们自己的包名
String stubPackage = "com.weishu.intercept_activity.app";
// 这里我们把启动的Activity临时替换为 StubActivity
ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
newIntent.setComponent(componentName);
// 把我们原始要启动的TargetActivity先存起来
newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw);
// 替换掉Intent, 达到欺骗AMS的目的
args[index] = newIntent;
Log.d(TAG, "hook success");
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
}
}
通过这个替换过程,在ActivityManagerNative的startActivity调用之后,system_server端收到Binder驱动的消息,开始执行ActivityManagerService里面真正的startActivity方法;这时候AMS看到的intent参数里面的组件已经是StubActivity了,因此可以成功绕过检查,这时候如果不做后面的Hook,直接调用
startActivity(new Intent(MainActivity.this, TargetActivity.class));
也不会出现上文的ActivityNotFoundException
现在我们的startActivity启动一个没有显式声明的Activity已经不会抛异常了,但是要真正正确地把TargetActivity启动起来,还有一些事情要做。其中最重要的一点是,我们用替身StubActivity临时换了TargetActivity,肯定需要在『合适的』时候替换回来;接下来我们就完成这个过程。
AMS进程转移到App进程也是通过Binder调用完成的,承载这个功能的Binder对象是IApplicationThread;在App进程它是Server端,在Server端接受Binder远程调用的是Binder线程池,Binder线程池通过Handler将消息转发给App的主线程;
那么,ActivityThread中的Handler类H是如何实现的呢?H的部分源码如下:
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
我们简单看一下Handler是如何处理接收到的Message的
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到H类仅仅重载了handleMessage方法;通过dispathMessage的消息分发过程得知,我们可以拦截这一过程:把这个H类的mCallback替换为我们的自定义实现,这样dispathMessage就会首先使用这个自定义的mCallback,然后看情况使用H重载的handleMessage。
这个Handler.Callback是一个接口,我们可以使用动态代理或者普通代理完成Hook,这里我们使用普通的静态代理方式;创建一个自定义的Callback类:
package com.weishu.intercept_activity.app.hook;
import java.lang.reflect.Field;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/* package */ class ActivityThreadHandlerCallback implements Handler.Callback {
Handler mBase;
public ActivityThreadHandlerCallback(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
// ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
// 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
case 100:
handleLaunchActivity(msg);
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
// 这里简单起见,直接取出TargetActivity;
Object obj = msg.obj;
// 根据源码:
// 这个对象是 ActivityClientRecord 类型
// 我们修改它的intent字段为我们原来保存的即可.
// switch (msg.what) {
// case LAUNCH_ACTIVITY: {
// Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
// final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
// r.packageInfo = getPackageInfoNoCheck(
// r.activityInfo.applicationInfo, r.compatInfo);
// handleLaunchActivity(r, null);
try {
// 把替身恢复成真身
Field intent = obj.getClass().getDeclaredField("intent");
intent.setAccessible(true);
Intent raw = (Intent) intent.get(obj);
Intent target = raw.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
raw.setComponent(target.getComponent());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
这个Callback类的使命很简单:把替身StubActivity恢复成真身TargetActivity;有了这个自定义的Callback之后我们需要把ActivityThread里面处理消息的Handler类H的的mCallback修改为自定义callback类的对象,看如下代码:
/**
* 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity
* <p/>
* 不然就真的启动替身了, 狸猫换太子...
* <p/>
* 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成
* H 会完成这个消息转发; 最终调用它的callback
*/
public static void hookActivityThreadHandler() throws Exception {
// 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
Object currentActivityThread = currentActivityThreadField.get(null);
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mHandler
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);
// 设置它的回调, 根据源码:
// 我们自己给他设置一个回调,就会替代之前的回调;
// public void dispatchMessage(Message msg) {
// if (msg.callback != null) {
// handleCallback(msg);
// } else {
// if (mCallback != null) {
// if (mCallback.handleMessage(msg)) {
// return;
// }
// }
// handleMessage(msg);
// }
// }
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH));
}
僵尸or活人?——能正确收到生命周期回调吗
实际上TargetActivity已经是一个有血有肉的Activity了:它具有自己正常的生命周期;可以运行Demo代码验证一下。
这个过程是如何完成的呢?我们以onDestroy为例简要分析一下:
从Activity的finish方法开始跟踪,最终会通过ActivityManagerNative到AMS然后接着通过ApplicationThread到ActivityThread,然后通过H转发消息到ActivityThread的handleDestroyActivity,接着这个方法把任务交给performDestroyActivity完成。
App进程与AMS交互几乎都是这么一种模式,几个角色 ActivityManagerNative, ApplicationThread, ActivityThread以及Handler类H分工明确,读者可以按照这几个角色的功能分析AMS的任何调用过程
最后总结下:
有一个Activity,但是没有在配置文件进行配置,如何启动它呢?通过查询源码得知通过Context\Activity启动一个Activity,最后都是调用了ActivityManagerNative.getDefault().startActivity的方法,先从App进程调用startActivity;然后通过IPC调用进入系统进程system_server,完成Activity管理以及一些校检工作,最后又回到了APP进程完成真正的Activioty对象创建。
由于这个检验过程是在AMS进程完成的,我们对system_server进程里面的操作无能为力,只有在我们APP进程里面执行的过程才是有可能被Hook掉的,
既然需要一个显式声明的Activity,那就声明一个!可以在第一步假装启动一个已经在AndroidManifest.xml里面声明过的替身Activity,让这个Activity进入AMS进程接受检验;最后在第三步的时候换成我们真正需要启动的Activity;这样就成功欺骗了AMS进程,瞒天过海!
通过hookActivityManagerNative方法,来修改startActivity方法,改变参数Intent,然后通过hookActivityThreadHandler,来修改Hanler中的mCallback属性,
、、、、、、、、、、、、、、、、、最后贴出代码、、、、、、、、、、、、、、、、、、、、
MainActivity
package com.weishu.intercept_activity.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import com.weishu.intercept_activity.app.hook.AMSHookHelper;
public class MainActivity extends Activity {
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
try {
AMSHookHelper.hookActivityManagerNative();
AMSHookHelper.hookActivityThreadHandler();
} catch (Throwable throwable) {
throw new RuntimeException("hook failed", throwable);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = new Button(this);
button.setText("启动TargetActivity");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 启动目标Activity; 注意这个Activity是没有在AndroidManifest.xml中显式声明的
// 但是调用者并不需要知道, 就像一个普通的Activity一样
startActivity(new Intent(MainActivity.this, TargetActivity.class));
}
});
setContentView(button);
}
}
StubActivity
package com.weishu.intercept_activity.app;
import android.app.Activity;
/**
* 原始的Activity; 这个Activity会在Manifest中注册;
* 当启动这个Activity的时候, 我们会把它拦截;然后跳转到TargetActivity
* <p/>
* 用到插件里面的话,那么就是在宿主程序里面注册了一堆空的Activity
* <p/>
* 如果希望启动插件的Activity; 由于插件Activity没有在主程序的Manifest中注册
* 因此直接启动肯定会问题(插件的Activity有可能在它自己的Manifest.xml 中注册
* 但是由于插件并不是一个真正安装的程序, Android系统并不知道这件事
* <p/>
* 我们可以通过分析Activity的启动机制, 可以在"合适的时候" 进行偷梁换柱,
* 虽然我们要启动TargetActivity; 但是我们在真正启动之前,暂时替换为RawActivity
* 这样,就能绕过AMS的验证,最后真正启动的时候,我们再替换回来,保证启动的是我们自己
* <p/>
* 这样我们就成为了一个真正的Activity, 生命周期由系统管理!
* Created by weishu on 16/1/7.
*/
public class StubActivity extends Activity {
// dummy, just stub
}
TargetActivity
package com.weishu.intercept_activity.app;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
/**
* 要注意的是,这个Activity并没有再Manifest中注册!!!
* <p/>
* 且看我们如何瞒天过海, 成功启动它.
* <p/>
*/
public class TargetActivity extends Activity {
private static final String TAG = "TargetActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called with " + "savedInstanceState = [" + savedInstanceState + "]");
TextView tv = new TextView(this);
tv.setText("TargetActivity 启动成功!!!");
setContentView(tv);
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause() called with " + "");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume() called with " + "");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop() called with " + "");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() called with " + "");
}
}
AMSHookHelper
package com.weishu.intercept_activity.app.hook;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import android.os.Handler;
/**
* @author weishu
* @date 16/3/21
*/
public class AMSHookHelper {
public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
/**
* Hook AMS
* <p/>
* 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity"
* <p/>
* 进而骗过AMS
*/
public static void hookActivityManagerNative() throws ClassNotFoundException,
NoSuchMethodException, InvocationTargetException,
IllegalAccessException, NoSuchFieldException {
// 26public abstract class Singleton<T> {
// 27 private T mInstance;
// 29 protected abstract T create();
// 30
// 31 public final T get() {
// 32 synchronized (this) {
// 33 if (mInstance == null) {
// 34 mInstance = create();
// 35 }
// 36 return mInstance;
// 37 }
// 38 }
// 39}
// 40
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
Class<?> singleton = Class.forName("android.util.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象
Object rawIActivityManager = mInstanceField.get(gDefault);
// 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager));
mInstanceField.set(gDefault, proxy);
}
/**
* 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity
* <p/>
* 不然就真的启动替身了, 狸猫换太子...
* <p/>
* 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成
* H 会完成这个消息转发; 最终调用它的callback
*/
public static void hookActivityThreadHandler() throws Exception {
// 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
Object currentActivityThread = currentActivityThreadField.get(null);
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mHandler
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);
// 设置它的回调, 根据源码:
// 我们自己给他设置一个回调,就会替代之前的回调;
// public void dispatchMessage(Message msg) {
// if (msg.callback != null) {
// handleCallback(msg);
// } else {
// if (mCallback != null) {
// if (mCallback.handleMessage(msg)) {
// return;
// }
// }
// handleMessage(msg);
// }
// }
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH));
}
}
IActivityManagerHandler
package com.weishu.intercept_activity.app.hook;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import android.content.ComponentName;
import android.content.Intent;
import android.util.Log;
import com.weishu.intercept_activity.app.StubActivity;
class IActivityManagerHandler implements InvocationHandler {
private static final String TAG = "IActivityManagerHandler";
Object mBase;
public IActivityManagerHandler(Object base) {
mBase = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
// 只拦截这个方法
// 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱
// API 23:
// public final Activity startActivityNow(Activity parent, String id,
// Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
// Activity.NonConfigurationInstances lastNonConfigurationInstances) {
// 找到参数里面的第一个Intent 对象
Intent raw;
int index = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
raw = (Intent) args[index];
Log.i(TAG,"---raw---"+raw.toString());
/以上是获取new Intent(MainActivity.this, TargetActivity.class)
Intent newIntent = new Intent();
// 替身Activity的包名, 也就是我们自己的包名
String stubPackage = "com.weishu.intercept_activity.app";
// 这里我们把启动的Activity临时替换为 StubActivity
ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
newIntent.setComponent(componentName);
// 把我们原始要启动的TargetActivity先存起来
newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw);
// 替换掉Intent, 达到欺骗AMS的目的
args[index] = newIntent;
Log.d(TAG, "hook success");
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
}
}
ActivityThreadHandlerCallback
package com.weishu.intercept_activity.app.hook;
import java.lang.reflect.Field;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/* package */ class ActivityThreadHandlerCallback implements Handler.Callback {
Handler mBase;
public ActivityThreadHandlerCallback(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
// ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
// 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
case 100:
handleLaunchActivity(msg);
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
// 这里简单起见,直接取出TargetActivity;
Object obj = msg.obj;
// 根据源码:
// 这个对象是 ActivityClientRecord 类型
// 我们修改它的intent字段为我们原来保存的即可.
// switch (msg.what) {
// case LAUNCH_ACTIVITY: {
// Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
// final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
// r.packageInfo = getPackageInfoNoCheck(
// r.activityInfo.applicationInfo, r.compatInfo);
// handleLaunchActivity(r, null);
try {
// 把替身恢复成真身
Field intent = obj.getClass().getDeclaredField("intent");
intent.setAccessible(true);
Intent raw = (Intent) intent.get(obj);
Intent target = raw.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
raw.setComponent(target.getComponent());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}