1.引言
以前一直很好奇,啟動(dòng)一個(gè)新的Activity,為什么非要在清單文件里注冊,到底是哪里地方進(jìn)行了校驗(yàn),整個(gè)啟動(dòng)的流程是什么樣子的。如果想實(shí)現(xiàn)插件化機(jī)制,啟動(dòng)一個(gè)插件中新的Activity的話有什么其它方法去做到。這篇文章本來是想寫在Acytivity的啟動(dòng)流程分析之后的,但是里面確實(shí)涉及的類,邏輯很多,寫起來可能會(huì)有些漏缺,而且比較無聊,所以先寫一下android的hook技術(shù),先大概講一下Activity的啟動(dòng)流程,里面會(huì)涉及到一些進(jìn)程交互,
2.Activity大致啟動(dòng)流程
啟動(dòng)一個(gè)Activity大致會(huì)經(jīng)歷一下幾個(gè)方法:
- Activity.startActivity()
- Activity.startActivityForResult()
- Instrumentation.execStartActivity()
- ActivityManagerService.startActivity()
- ApplicationThread.scheduleLaunchActivity()
- ActivityThread.Handler.handleMessage()
具體方法本文就不詳細(xì)說了,免得篇幅太長,引用一張圖來表述整個(gè)的交互過程:

從上圖我們可以看出整個(gè)通信過程是涉及到2次Binder通信過程的,APP進(jìn)程和system_server進(jìn)程分別作為了一次client和server端。APP進(jìn)程也就是我們自己的應(yīng)用進(jìn)程,system_server進(jìn)程是系統(tǒng)進(jìn)程,javaframework框架的核心載體,里面運(yùn)行了大量的系統(tǒng)服務(wù),比如這里提供ApplicationThreadProxy),ActivityManagerService,結(jié)合圖,啟動(dòng)流程大致如下:
- APP進(jìn)程入口是ActivityThread,初始化時(shí)便會(huì)創(chuàng)建一個(gè)Binder線程(具體是指ApplicationThread,Binder的服務(wù)端,用于接收系統(tǒng)服務(wù)AMS發(fā)送來的事件),ApplicationThread主要有兩個(gè)作用:
- 作為Binder服務(wù)端接受system_server中的client端ApplicationThreadProxy發(fā)送過來的消息,對應(yīng)上面的方法是ActivityManagerService------>ApplicationThread,
- 作為消息的中轉(zhuǎn)站,將msg通過handler傳遞到ActivityThread中,也就是傳遞到上述的最后一個(gè)方法中。
- system_server進(jìn)程中的ActivityManagerService作為Binder的服務(wù)端,接受ActivityManagerProxy作為client端發(fā)送過來的消息,也就是Instrumentation.execStartActivity()---->ActivityManagerService.startActivity()。
這里主要看下Instrumentation.execStartActivity這個(gè)方法,比較關(guān)鍵,跳過去可能有的朋友比較模糊,主要代碼如下:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
....
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;
}
這里的contextThread也就是上面講的ApplicationThread對象,主要看下面ActivityManager.getService(),返回的是一個(gè)IActivityManager接口類型對象,繼續(xù)看:
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);//注意這一行
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
這里我用的API25,API26及以上,實(shí)現(xiàn)的代碼不太一樣,廢棄了ActivityManagerProxy,改用了AIDL來實(shí)現(xiàn)通信,為了讓大家伙更理解Binder,這里就用之前的API了,邏輯應(yīng)該很清晰通過ServiceManager拿到IBinder對象,再在本地進(jìn)行查找,如果不在同一個(gè)進(jìn)程,就返回ActivityManagerProxy代理對象,所以很清晰,Instrumentation.execStartActivity()實(shí)際上最后就調(diào)用到了ActivityManagerProxy中。
3.Hook實(shí)現(xiàn)
3.1 hook實(shí)現(xiàn)思路
咳咳?。∥覀兓氐秸},上面只是鋪墊,我們的主題是hook,怎么啟動(dòng)一個(gè)沒注冊的Activity呢,先將下思路,既然最終檢查是在AMS中,那我們可以在之前做一些騷操作,來個(gè)貍貓換太子,具體思路如下:
- 直接startActivity()開啟一個(gè)未注冊的TargetActivity
- 既然在AMS之前,消息是從ActivityManagerProxy中發(fā)出去的,我們可以動(dòng)態(tài)代理生成一個(gè)類(不熟悉動(dòng)態(tài)代理的朋友只能自行g(shù)oogle了~),代理ActivityManagerProxy對象,攔截其中的startActivity()方法,拿到其中的intent參數(shù)對象。我們把真正的intent替換成我們一個(gè)已經(jīng)注冊過的ProxyActivity,先把AMS的check這一關(guān)給過了,再把真正的intent當(dāng)做對象存在ProxyActivity的intent中。
- 既然intent替換了,AMS是過了,那肯定得給它搞回來,要不然不就直接打開了ProxyActivity?我們知道最好消息時(shí)回到了ActivityThread.Handler.handleMessage()中的,嘿嘿,熟悉Handler機(jī)制的朋友應(yīng)該知道,在這之前是會(huì)先走dispatchMessage方法的,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
是分別會(huì)先后執(zhí)行handleCallback(msg)--->mCallback.handleMessage(msg)--->handleMessage(msg),而Activity中正好是最好一個(gè),那我們可以hook一下這個(gè)mCallback,讓我們在最后消息執(zhí)行時(shí),把我們的intent給替換回去
3.2 上代碼!!
public class HookActivityUtils {
private static final String TAG = "HookActivityUtils";
private volatile static HookActivityUtils sHookActivityUtils;
public static HookActivityUtils getInstance(){
if (sHookActivityUtils==null){
synchronized (HookActivityUtils.class){
if (sHookActivityUtils==null){
sHookActivityUtils = new HookActivityUtils();
}
}
}
return sHookActivityUtils;
}
private HookActivityUtils(){
}
public void hooks(Context mContext){
Object object;
try {
//尋找hook點(diǎn),最好是靜態(tài)或者單例,不容易發(fā)生改變,因?yàn)槭庆o態(tài),所以傳入null即可
//因?yàn)榘姹静町?,所以要分開處理
if (Build.VERSION.SDK_INT>=26){
Field iActivityManagerSingleton = ActivityManager.class.getDeclaredField("IActivityManagerSingleton");
iActivityManagerSingleton.setAccessible(true);
object = iActivityManagerSingleton.get(null);
}else{
Field gDefault = Class.forName("android.app.ActivityManagerNative").getDeclaredField("gDefault");
gDefault.setAccessible(true);
object = gDefault.get(null);
}
//獲取單例對象,實(shí)現(xiàn)IActivityManager接口的實(shí)現(xiàn)類
Field mFieldInstance = Class.forName("android.util.Singleton").getDeclaredField("mInstance");
mFieldInstance.setAccessible(true);
Object mInstance = mFieldInstance.get(object);
//尋找到hook點(diǎn)后,新建一個(gè)代理對象
ActivityManagerDelegate managerDelegate = new ActivityManagerDelegate(mInstance,mContext);
Class<?> aClass = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(aClass.getClassLoader(), new Class<?>[]{aClass}, managerDelegate);
//替換動(dòng)態(tài)代理對象
mFieldInstance.set(object,proxy);
} catch (Exception mE) {
mE.printStackTrace();
}
}
public void hookHanlder(){
try {
Class<?> aClass = Class.forName("android.app.ActivityThread");
Method currentActivityThread = aClass.getDeclaredMethod("currentActivityThread");
currentActivityThread.setAccessible(true);
//ActivityThread 本身對象
Object invoke = currentActivityThread.invoke(null);
Field mH = aClass.getDeclaredField("mH");
mH.setAccessible(true);
//獲取handler對象
Object handler = mH.get(invoke);
//獲取handler中的mCallback
Field mCallback = Handler.class.getDeclaredField("mCallback");
mCallback.setAccessible(true);
mCallback.set(handler,new HookCallBack((Handler) handler));
} catch (Exception mE) {
mE.printStackTrace();
}
}
}
主要也就是對應(yīng)的兩個(gè)方法,一個(gè)通過反射拿到實(shí)現(xiàn)IActivityManager接口的對象,并生成一個(gè)代理此對象的代理對象,另外一個(gè)是反射拿到ActivityThread中的mH Handler對象,然后傳入一個(gè)實(shí)現(xiàn)Handler.callback接口的對象,這樣Handler中的mcallback就不為空了,也就達(dá)到了我們的目的
然后是我們的代理對象:
public class ActivityManagerDelegate implements InvocationHandler {
private static final String TAG = "ActivityManagerDelegate";
private Object mObject;
private Context mContext;
public ActivityManagerDelegate(Object mObject,Context mContext) {
this.mObject = mObject;
this.mContext = mContext;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("startActivity")){
//攔截方法
Log.e(TAG,"i got you");
Intent intent =null;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent){
intent = (Intent) args[i];
//找到了intent參數(shù)
Intent mIntent = new Intent();
ComponentName componentName = new ComponentName(mContext,ProxyActivity.class);
//將真正的intent帶上,后續(xù)替換
mIntent.setComponent(componentName);
mIntent.putExtra("realObj",intent);
//修改為已注冊Activity的intent,先讓AMS檢查通過
args[i] = mIntent;
}
}
}
return method.invoke(mObject,args);
}
}
我們攔截startActivity,然后將ProxyActivity的ComponentName傳遞進(jìn)去,貍貓換太子,同時(shí)將真正的intent帶過去,接下來就是處理消息了:
public class HookCallBack implements Handler.Callback {
private static final String TAG = "HookCallBack";
private Handler mHandler;
public HookCallBack(Handler mHandler) {
this.mHandler = mHandler;
}
@Override
public boolean handleMessage(Message msg) {
if (msg.what==100){
handleHookMsg(msg);
}
mHandler.handleMessage(msg);
return false;
}
private void handleHookMsg(Message mMsg) {
Object obj = mMsg.obj;
try {
Field intent = obj.getClass().getDeclaredField("intent");
//這時(shí)候拿出之前存進(jìn)來真正的intent
intent.setAccessible(true);
Intent proxyIntent = (Intent) intent.get(obj);
Intent realIntent = proxyIntent.getParcelableExtra("realObj");
proxyIntent.setComponent(realIntent.getComponent());
} catch (Exception mE) {
mE.printStackTrace();
}
}
}
什么?為什么要攔截msg.what等于100的消息?
private class H extends Handler {
....
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
....
}
這下明白了吧,攔截到這個(gè)消息后,把事先存進(jìn)去的intent的Component再set回去就完美了~~
主頁面MainActivity:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HookActivityUtils.getInstance().hooks(this);
HookActivityUtils.getInstance().hookHanlder();
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,TargetActivity.class);
startActivity(intent);
}
});
}
}
這里我們打開的是TargetActivity,但是清單文件中并沒有聲明:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ProxyActivity"></activity>
</application>
這樣的話,就實(shí)現(xiàn)打開一個(gè)未注冊的Activity了,是不是也是挺easy的,在實(shí)現(xiàn)插件化機(jī)制的時(shí)候,要打開插件中的activity的話,因?yàn)闆]有在原宿主中的清單文件注冊,是無法直接調(diào)轉(zhuǎn)的,這時(shí)候我們這個(gè)代理activity就可以起很大的作用了。
喜歡請點(diǎn)擊+關(guān)注哦
