Android知识总结
一、启动插件的四大组件
1.1、宿主启动插件的Activity
Activity 是需要在清单文件中注册的,显然,插件的 Activity 没有在宿主的清单文件中注册,那我们如何来启动它呢?
这里我们就需要使用 Hook 技术,来绕开系统的检测。可能有些同学不知道 Hook 是什么,所以我们先简单的介绍下 Hook 技术。
正常情况下对象A调用对象B,对象B处理后将数据返回给对象A,如下图:
而加入Hook后的流程就变成了下图形式:
Hook可以是一个方法或者一个对象,它就像一个钩子一样挂在对象B上面,当对象A调用对象B之前,Hook可以做一些处理,起到“欺上瞒下”的作用。而对象B就是我们常说的Hook点。为了保证Hook的稳定性,Hook点一般选择容易找到并且不易变化的对象,如静态变量和单例。
那么思路就来了,首先我们在宿主里面创建一个 ProxyActivity 继承自 Activity,并且在清单中注册。当启动插件Activity 的时候,在系统检测前,找到一个Hook点,然后通过 Hook 将插件 Activity 替换成 ProxyActivity,等到检测完了后,再找一个Hook点,使用 Hook 将它们换回来,这样就实现了插件 Activity 的启动。思路是不是非常的简单。
通过 动态代理
和 反射
实现 Hook。
查找Hook点的原则:
- 尽量静态变量或者单例对象。
- 尽量 Hook public 的对象和方法。
从 HOOK 一直找到
静态的变量
或者直接拿到类的对象
,才说明 HOOK 成功。
1.2、Activity启动流程
首先我们来看一张 Activity 启动流程的简单示意图,如下:
通过这张图我们可以确定 Hook 点的大致位置。
- 1、在进入 AMS 之前,找到一个 Hook 点,用来将插件 Activity 替换为 ProxyActivity。
- 2、从 AMS 出来后,再找一个 Hook 点,用来将 ProxyActivity 替换为插件 Activity。
1.3、Activity启动流程
详情见:
Launch 启动 Activity
Activity 启动流程(一)
Activity 启动流程(二)
二、HOOK 点的选择
我们在项目中一般通过 startActivity(new Intent(this,PluginActivity.class));
启动 PluginActivity,如果我想换成启动 ProxyActivity,调用方法 startActivity(new Intent(this,ProxyActivity.class));
这样就可以了。是不是已经知道答案了!!!没错,我们只要找到能够修改 Intent
的地方,就可以作为 Hook 点,从这儿也可以看出 Hook 点并不是唯一的。
- 一般我们选择
Intent
作为 HOOK 点。
2.1、进入 AMS 之前HOOK 点
API30 源码
// android/app/Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 这儿就是我们的 Hook 点,替换传入 startActivity 方法中的 intent 参数
try {
intent.migrateExtraStreamToClipData(who);
intent.prepareToLeaveProcess(who);
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getBasePackageName(), who.getAttributionTag(), 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;
}
API 28 源码
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
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;
}
既然 Hook 点找到了,那我们怎么修改呢?
答案是动态代理,所以我们要生成一个代理对象,显然,我们要代理的是 ActivityTaskManager.getService()
返回的对象。
API 30 源码
// android/app/ActivityTaskManager.java
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton IActivityTaskManagerSingleton =
new Singleton() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
API 28 源码
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
桶过上面的代码,我们知道 IActivityManager 是调用的 Singleton 里面的 get 方法,所以下面我们再看下Singleton 是怎么样的。
// android/util/Singleton
public abstract class Singleton {
@UnsupportedAppUsage
public Singleton() {
}
@UnsupportedAppUsage
private T mInstance;
protected abstract T create();
@UnsupportedAppUsage
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
可以看出,IActivityManagerSingleton.get() 返回的实际上就是 mInstance 对象。所以接下来我们要替换的就是这个对象。
2.2、从 AMS 出来后HOOK点
替换回去会调用 Handler 的 handleMessage方法,所以下面我们看下 Handler 的源码。
public void handleMessage(Message msg) {
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
当 mCallback != null 时,首先会执行mCallback.handleMessage(msg),再执行 handleMessage(msg),所以我们可以将 mCallback 作为 Hook 点,创建它。ok,现在问题就只剩一个了,就是找到含有 intent 的对象,没办法,只能接着看源码。
API 26 源码
// android/app/ActivityThread.java
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
} break;
}
}
static final class ActivityClientRecord {
Intent intent;
}
可以看到,在 ActivityClientRecord 类中,刚好就有个 intent,而且这个类的对象,我们也可以获取到,就是msg.obj。
API 30源码
// android/app/ActivityThread.H.java
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
break;
//android/app/servertransaction/ClientTransaction .java
public class ClientTransaction implements Parcelable, ObjectPoolItem {
/** A list of individual callbacks to a client. */
@UnsupportedAppUsage
private List mActivityCallbacks;
}
//android/app/servertransaction/TransactionExecutor.java
public class TransactionExecutor {
public void execute(ClientTransaction transaction) {
executeCallbacks(transaction);
executeLifecycleState(transaction);
}
public void executeCallbacks(ClientTransaction transaction) {
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
item.getPostExecutionState());
if (closestPreExecutionState != UNDEFINED) {
cycleToPath(r, closestPreExecutionState, transaction);
}
//HOOK 点
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
}
}
}
//android/app/servertransaction/LaunchActivityItem.java
public class LaunchActivityItem extends ClientTransactionItem {
@UnsupportedAppUsage
private Intent mIntent;
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
//HOOK 点
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
在LaunchActivityItem
中我们就找到Intent
了,可以在此利用 HOOK 技术进行替换。
创建 mCallback 替换系统的Intent,运用拿到 msg 从而可以拿到 msg.obj; 然后在替换 intent
--> ClientTransaction == msg.obj
--> private List mActivityCallbacks;
--> ClientTransactionItem的子类
--> private Intent mIntent;
--> LaunchActivityItem 对象
--> private List mActivityCallbacks;
--> ClientTransaction == msg.obj
三、源码
- 核心代码
public class HookUtil {
private static final String TARGET_INTENT = "target_intent";
public static void hookAMSAidl(){
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
hookIActivityTaskManager();
}else{
hookIActivityManager();
}
}
public static void hookIActivityTaskManager(){
try{
Field singletonField = null;
Class actvityManager = Class.forName("android.app.ActivityTaskManager");
singletonField = actvityManager.getDeclaredField("IActivityTaskManagerSingleton");
singletonField.setAccessible(true);
Object singleton = singletonField.get(null);
//拿IActivityManager对象
Class singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//原始的IActivityTaskManager
final Object IActivityTaskManager = mInstanceField.get(singleton);
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
, new Class[]{Class.forName("android.app.IActivityTaskManager")}
, new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// Log.i(TAG, "invoke: " + method.getName());
//偷梁换柱
//真正要启动的activity目标
Intent raw = null;
int index = -1;
if ("startActivity".equals(method.getName())) {
for (int i = 0; i < args.length; i++) {
if(args[i] instanceof Intent){
raw = (Intent)args[i];
index = i;
}
}
//代替的Intent
Intent newIntent = new Intent();
newIntent.setComponent(new ComponentName("com.example.designdemo", StubActivity.class.getName()));
newIntent.putExtra(TARGET_INTENT,raw);
args[index] = newIntent;
}
return method.invoke(IActivityTaskManager, args);
}
});
// 7. IActivityManagerProxy 融入到framework
mInstanceField.set(singleton, proxy);
}catch (Exception e){
e.printStackTrace();
}
}
public static void hookIActivityManager() {
try {
// 获取 singleton 对象
Field singletonField = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // 小于8.0
Class clazz = Class.forName("android.app.ActivityManagerNative");
singletonField = clazz.getDeclaredField("gDefault");
} else {
Class clazz = Class.forName("android.app.ActivityManager");
singletonField = clazz.getDeclaredField("IActivityManagerSingleton");
}
singletonField.setAccessible(true);
Object singleton = singletonField.get(null);
// 获取 系统的 IActivityManager 对象
Class singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
final Object mInstance = mInstanceField.get(singleton);
Class iActivityManagerClass = Class.forName("android.app.IActivityManager");
// 创建动态代理对象
Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{iActivityManagerClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// do something
// Intent的修改 -- 过滤
/**
* IActivityManager类的方法
* startActivity(whoThread, who.getBasePackageName(), intent,
* intent.resolveTypeIfNeeded(who.getContentResolver()),
* token, target != null ? target.mEmbeddedID : null,
* requestCode, 0, null, options)
*/
// 过滤
if ("startActivity".equals(method.getName())) {
int index = -1;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
// 启动插件的
Intent intent = (Intent) args[index];
Intent proxyIntent = new Intent();
proxyIntent.setComponent(new ComponentName("com.example.designdemo", StubActivity.class.getName()));
proxyIntent.putExtra(TARGET_INTENT, intent);
args[index] = proxyIntent;
}
// args method需要的参数 --- 不改变原有的执行流程
// mInstance 系统的 IActivityManager 对象
return method.invoke(mInstance, args);
}
});
// ActivityManager.getService() 替换成 proxyInstance
mInstanceField.set(singleton, proxyInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void hookHandler() {
try {
// 获取 ActivityThread 类的 Class 对象
Class clazz = Class.forName("android.app.ActivityThread");
// 获取 ActivityThread 对象
Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
activityThreadField.setAccessible(true);
Object activityThread = activityThreadField.get(null);
// 获取 mH 对象
Field mHField = clazz.getDeclaredField("mH");
mHField.setAccessible(true);
final Handler mH = (Handler) mHField.get(activityThread);
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
// 创建的 callback
Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
// 通过msg 可以拿到 Intent,可以换回执行插件的Intent
// 找到 Intent的方便替换的地方 --- 在这个类里面 ActivityClientRecord --- Intent intent 非静态
switch (msg.what) {
case 100: //API 26 以下
try {
// msg.obj == ActivityClientRecord
Field intentField = msg.obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
// 启动代理Intent
Intent proxyIntent = (Intent) intentField.get(msg.obj);
// 启动插件的 Intent
Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
if (intent != null) {
intentField.set(msg.obj, intent);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
case 159: //API 30
try {
// msg.obj == ClientTransaction
// 获取 mActivityCallbacks 对象
Field mActivityCallbacksField = msg.obj.getClass()
.getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
for (int i = 0; i < mActivityCallbacks.size(); i++) {
if (mActivityCallbacks.get(i).getClass().getName()
.equals("android.app.servertransaction.LaunchActivityItem")) {
Object launchActivityItem = mActivityCallbacks.get(i);
// 获取启动代理的 Intent
Field mIntentField = launchActivityItem.getClass()
.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);
// 目标 intent 替换 proxyIntent
Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
if (intent != null) {
mIntentField.set(launchActivityItem, intent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
// 必须 return false
return false;
}
};
// 替换系统的 callBack
mCallbackField.set(mH, callback);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 初始化工具类
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LoadUtil.loadClass(this);
HookUtil.hookAMS();
HookUtil.hookHandler();
}
}
- 启动Activity
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.enjoy.plugin",
"com.enjoy.plugin.MainActivity"));
startActivity(intent);