日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

android 全局hook_【Hook】实现无清单启动Activity

發(fā)布時(shí)間:2024/7/23 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 全局hook_【Hook】实现无清单启动Activity 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

引子

Hook技術(shù)在android開(kāi)發(fā)領(lǐng)域算是一項(xiàng)黑科技,那么一個(gè)新的概念進(jìn)入視線,我們最關(guān)心的3個(gè)問(wèn)題就是,它是什么,有什么用,怎么用

本系列將由淺入深 手把手講解這三大問(wèn)題。

本文是第三篇, 高級(jí)篇。

前面兩篇Hook博文,寫了兩個(gè)demo,一個(gè)是hook入門,一個(gè)是略微復(fù)雜的Activity啟動(dòng)流程的hook。

那么玩點(diǎn)更高端的吧, 正常開(kāi)發(fā)中,所有 Activity都要在 AndroidManifest.xml中進(jìn)行注冊(cè),才可以正常跳轉(zhuǎn),通過(guò) hook,可以繞過(guò)系統(tǒng)對(duì) activity注冊(cè)的檢測(cè),即使不注冊(cè),也可以正常跳轉(zhuǎn)。

鳴謝

感謝大神的博文 https://www.jianshu.com/p/eb4121b037e2


正文大綱

一.整體思路
二.源碼索引
三.hook核心代碼
四.最終效果

Demo地址:

https://github.com/18598925736/ActivityHookDemo/tree/startActivityWithoutRegiste

正文

提示:本文所有源碼索引圖,都基于 SDK28-android9.0系統(tǒng).


一.整體思路

在之前Activity啟動(dòng)流程的hook的Demo里,我進(jìn)行了 Activity流程的 hook,最終采用的方案,是 Hook了 AMS,實(shí)現(xiàn)了全局的 startActivity動(dòng)作的 劫持. 現(xiàn)在就從這個(gè)AMS的 hook為起點(diǎn),來(lái)實(shí)現(xiàn) 無(wú)清單啟動(dòng)Activity.

Activity啟動(dòng)流程的 hook的Demo里,最后實(shí)現(xiàn)的效果是,每次跳轉(zhuǎn) Activity,都能看到這個(gè)日志:

那么,我們既然偵測(cè)到了 startActivity這個(gè)方法的調(diào)用,那么自然就可以拿到里面的實(shí)參,比如, Intent。Intent是跳轉(zhuǎn)意圖,從哪里來(lái),跳到哪里去的信息,都包含在 Intent里面.而, manifestActivity的檢測(cè),也是要根據(jù) Intent里面的信息來(lái)的.所以,要騙過(guò)系統(tǒng),要假裝我們跳的 Activity是已經(jīng)注冊(cè)過(guò)的,那么只需要將 Intent里面的信息換成 已經(jīng)在 manifest中注冊(cè)的某個(gè) Activity就可以了( 這里可能就有人像抬杠了,你怎么知道m(xù)anifest里面一定有注冊(cè)Activity....如果一個(gè)Activity都沒(méi)有,你的app是怎么啟動(dòng)的呢,至少得有一個(gè)LauncherActivity吧--!).

確定思路: 1.在AMS的hook函數(shù)中,將 真實(shí)的Intent中的信息,替換成manifest中已有的Activity信息. 騙過(guò)系統(tǒng)的檢測(cè)機(jī)制。2.雖然騙過(guò)了系統(tǒng)的檢測(cè)機(jī)制,但是這么一來(lái),每一次的跳轉(zhuǎn),都會(huì)跳到 "假"的 Activity,這肯定不是我們想要的效果,那么就必須,在真正的跳轉(zhuǎn)時(shí)機(jī)之前,將 真實(shí)的Activity信息,還原回去, 跳到原本該去的 Activity.

對(duì)應(yīng)的核心代碼,其實(shí)也就兩句話:


二.源碼索引

下圖大致畫出了:從 Activity.startActivity動(dòng)作開(kāi)始,到最終 跳轉(zhuǎn)動(dòng)作的最終執(zhí)行者 全過(guò)程.

下面開(kāi)始看源碼,從 Activity.startActivity開(kāi)始:

這里開(kāi)始分支:if(mParent==null),但是兩個(gè)分支最終執(zhí)行如下:

很顯然,兩者都是同樣的調(diào)用過(guò)程:

Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(....);

mMainThread.sendActivityResult(...);

第一句, execStartActivity 是 對(duì)一些參數(shù)的合法性校驗(yàn),如果不合法,那就會(huì)直接拋出異常,比如之前的 第二句, sendActivityResult才是真正的 跳轉(zhuǎn)動(dòng)作執(zhí)行者

先進(jìn)入第一句Instrumentation.ActivityResultar=mInstrumentation.execStartActivity看看,既然是合法性校驗(yàn),且看他是如何校驗(yàn)的。

這是 Instrumentation的 execStartActivity方法結(jié)論:它是通過(guò)AMS去校驗(yàn)的,AMS startActivity會(huì)返回一個(gè)int數(shù)值,隨后, checkStartActivityResult方法會(huì)根據(jù)這個(gè) int值,拋出響應(yīng)的異常,或者什么都不做.

再進(jìn)入第二句 mMainThread.sendActivityResult看真正的跳轉(zhuǎn)動(dòng)作是如何執(zhí)行的:ps:這里其實(shí)有個(gè)訣竅,既然我們的終極目標(biāo)是要騙過(guò)系統(tǒng)的Activity Intent檢測(cè),那么,跟著Intent這個(gè)變量,就不會(huì)偏離方向.

既然intent被封裝到了 ClientTransaction,交給了 mAppThread,那么繼續(xù):

前方有坑,請(qǐng)注意:androidStudio里并不能直接跳轉(zhuǎn),所以要手動(dòng),找到下圖中的方法,這個(gè) ClientTransactionHandler是 ActivityThread的父類.上圖中,調(diào)用了 sendMessage(int,Object),在 ActivityThread中找到這個(gè)方法的實(shí)現(xiàn):找它的最終實(shí)現(xiàn):找到另一個(gè)關(guān)鍵點(diǎn):mH ,H類的定義:(太長(zhǎng)了,我就不完整截圖了,留下關(guān)鍵的信息)

final H mH = new H();

class H extends Handler {

...

public static final int EXECUTE_TRANSACTION = 159;

String codeToString(int code) {

if (DEBUG_MESSAGES) {

switch (code) {

case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";

case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";

}

}

return Integer.toString(code);

}

public void handleMessage(Message msg) {

if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));

switch (msg.what) {

case EXECUTE_TRANSACTION:

final ClientTransaction transaction = (ClientTransaction) msg.obj;

mTransactionExecutor.execute(transaction);

if (isSystem()) {

// Client transactions inside system process are recycled on the client side

// instead of ClientLifecycleManager to avoid being cleared before this

// message is handled.

transaction.recycle();

}

// TODO(lifecycler): Recycle locally scheduled transactions.

break;

...

}

Object obj = msg.obj;

if (obj instanceof SomeArgs) {

((SomeArgs) obj).recycle();

}

if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));

}

}

很明顯,他就是一個(gè) Handler的普通子類,定義了主線程 ActivityThread中可能發(fā)生的各種事件。PS: 這里,我留下了 caseEXECUTE_TRANSACTION:分支,是因?yàn)?#xff0c;之前 ClientTransactionHandler 抽象類里面, sendMessage(ActivityThread.H.EXECUTE_TRANSACTION,transaction);,就是用的這個(gè) EXECUTE_TRANSACTION常量。終于找到了 startActivity的最終執(zhí)行代碼!

final ClientTransaction transaction = (ClientTransaction) msg.obj;

mTransactionExecutor.execute(transaction);

Ok,就到這里了.(事實(shí)上,我本來(lái)還想往下追查, Intent被封裝到 ClientTransaction之后,又被得到了什么樣的處理,最后發(fā)現(xiàn)居然查到了一個(gè)源碼中都不存在的類,我表示看不懂了,就到這里吧,不影響我們 hook)


.hook核心代碼

還記得我們的整體思路么?

1.在AMS的hook函數(shù)中,將 真實(shí)的Intent中的信息,替換成manifest中已有的Activity信息. 騙過(guò)系統(tǒng)的檢測(cè)機(jī)制。2.雖然騙過(guò)了系統(tǒng)的檢測(cè)機(jī)制,但是這么一來(lái),每一次的跳轉(zhuǎn),都會(huì)跳到 "假"的 Activity,這肯定不是我們想要的效果,那么就必須,在真正的跳轉(zhuǎn)時(shí)機(jī)之前,將 真實(shí)的Activity信息,還原回去, 跳到原本該去的 Activity.

說(shuō)通俗一點(diǎn)就是,第一,偽造一個(gè) Intent,騙過(guò) ActivityManifest檢測(cè)。第二,真正要跳轉(zhuǎn)之前,把原始的Intent還原回去.開(kāi)始擼代碼,大量 反射代碼即將到來(lái),注釋應(yīng)該很詳盡了,特別注意:看反射代碼要對(duì)照源代碼來(lái)看,不然很容易走神:

偽造intent,騙過(guò)Activity Manifest檢測(cè)

這里,請(qǐng)對(duì)照:ActivityManager.java的4125-4137行hook核心代碼如下

/**

* 這里對(duì)AMS進(jìn)行hook

*

* @param context

*/

private static void hookAMS(Context context) {

try {

Class> ActivityManagerClz;

final Object IActivityManagerObj;//這個(gè)就是AMS實(shí)例

Method getServiceMethod;

Field IActivityManagerSingletonField;

if (ifSdkOverIncluding26()) {//26,27,28的ams獲取方式是通過(guò)ActivityManager.getService()

ActivityManagerClz = Class.forName("android.app.ActivityManager");

getServiceMethod = ActivityManagerClz.getDeclaredMethod("getService");

IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");//單例類成員的名字也不一樣

} else {//25往下,是ActivityManagerNative.getDefault()

ActivityManagerClz = Class.forName("android.app.ActivityManagerNative");

getServiceMethod = ActivityManagerClz.getDeclaredMethod("getDefault");

IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField("gDefault");//單例類成員的名字也不一樣

}

IActivityManagerObj = getServiceMethod.invoke(null);//OK,已經(jīng)取得這個(gè)系統(tǒng)自己的AMS實(shí)例

// 2.現(xiàn)在創(chuàng)建我們的AMS實(shí)例

// 由于IActivityManager是一個(gè)接口,那么其實(shí)我們可以使用Proxy類來(lái)進(jìn)行代理對(duì)象的創(chuàng)建

// 結(jié)果被擺了一道,IActivityManager這玩意居然還是個(gè)AIDL,動(dòng)態(tài)生成的類,編譯器還不認(rèn)識(shí)這個(gè)類,怎么辦?反射咯

Class> IActivityManagerClz = Class.forName("android.app.IActivityManager");

// 構(gòu)建代理類需要兩個(gè)東西用于創(chuàng)建偽裝的Intent

String packageName = Util.getPMName(context);

String clz = Util.getHostClzName(context, packageName);

Object proxyIActivityManager =

Proxy.newProxyInstance(

Thread.currentThread().getContextClassLoader(),

new Class[]{IActivityManagerClz},

new ProxyInvocation(IActivityManagerObj, packageName, clz));

//3.拿到AMS實(shí)例,然后用代理的AMS換掉真正的AMS,代理的AMS則是用 假的Intent騙過(guò)了 activity manifest檢測(cè).

//偷梁換柱

IActivityManagerSingletonField.setAccessible(true);

Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);

Class> SingletonClz = Class.forName("android.util.Singleton");//反射創(chuàng)建一個(gè)Singleton的class

Field mInstanceField = SingletonClz.getDeclaredField("mInstance");

mInstanceField.setAccessible(true);

mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);

} catch (Exception e) {

e.printStackTrace();

}

}

private static final String ORI_INTENT_TAG = "origin_intent";

/**

* 把InvocationHandler的實(shí)現(xiàn)類提取出來(lái),因?yàn)檫@里包含了核心技術(shù)邏輯,最好獨(dú)立,方便維護(hù)

*/

private static class ProxyInvocation implements InvocationHandler {

Object amsObj;

String packageName;//這兩個(gè)String是用來(lái)構(gòu)建Intent的ComponentName的

String clz;

public ProxyInvocation(Object amsInstance, String packageName, String clz) {

this.amsObj = amsInstance;

this.packageName = packageName;

this.clz = clz;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//proxy是創(chuàng)建出來(lái)的代理類,method是接口中的方法,args是接口執(zhí)行時(shí)的實(shí)參

if (method.getName().equals("startActivity")) {

Log.d("GlobalActivityHook", "全局hook 到了 startActivity");

Intent currentRealIntent = null;//偵測(cè)到startActivity動(dòng)作之后,把intent存到這里

int intentIndex = -1;

//遍歷參數(shù),找到Intent

for (int i = 0; i < args.length; i++) {

Object temp = args[i];

if (temp instanceof Intent) {

currentRealIntent = (Intent) temp;//這是原始的Intent,存起來(lái),后面用得著

intentIndex = i;

break;

}

}

//構(gòu)造自己的Intent,這是為了繞過(guò)manifest檢測(cè)(這個(gè)Intent是偽造的!只是為了讓通過(guò)manifest檢測(cè))

Intent proxyIntent = new Intent();

ComponentName componentName = new ComponentName(packageName, clz);//用ComponentName重新創(chuàng)建一個(gè)intent

proxyIntent.setComponent(componentName);

proxyIntent.putExtra(ORI_INTENT_TAG, currentRealIntent);//將真正的proxy作為參數(shù),存放到extras中,后面會(huì)拿出來(lái)還原

args[intentIndex] = proxyIntent;//替換掉intent

//喲,已經(jīng)成功繞過(guò)了manifest清單檢測(cè). 那么,我不能老讓它跳到 偽裝的Activity啊,我要給他還原回去,那么,去哪里還原呢?

//繼續(xù)看源碼。

}

return method.invoke(amsObj, args);

}

}

真正要跳轉(zhuǎn)之前,把原始的Intent還原回去

PS: 這里 hook mh的手段,并不是針對(duì) mh本身做代理,而是對(duì)mh的mCallback成員. 因?yàn)?#xff1a;

public class Handler {

...

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

}

handler的 dispatchMessage邏輯,是 先執(zhí)行 mCallback的 handlerMessage,然后根據(jù)它的返回值決定要不要執(zhí)行 handler本身的 handlerMessage函數(shù). 我們的目的是還原 Intent,并不需要對(duì)ActivityThread原本的mH做出邏輯修改,所以, hook mCallback,加入還原 Intent的邏輯,即可.

這次hook,對(duì)照的源碼是(源碼太長(zhǎng)了,我就直接截取了ActivityThread里面一些關(guān)鍵的代碼):

下面是 HookMh的完整代碼:

//下面進(jìn)行ActivityThread的mH的hook,這是針對(duì)SDK28做的hook

private static void hookActivityThread_mH_After28() {

try {

//確定hook點(diǎn),ActivityThread類的mh

// 先拿到ActivityThread

Class> ActivityThreadClz = Class.forName("android.app.ActivityThread");

Field field = ActivityThreadClz.getDeclaredField("sCurrentActivityThread");

field.setAccessible(true);

Object ActivityThreadObj = field.get(null);//OK,拿到主線程實(shí)例

//現(xiàn)在拿mH

Field mHField = ActivityThreadClz.getDeclaredField("mH");

mHField.setAccessible(true);

Handler mHObj = (Handler) mHField.get(ActivityThreadObj);//ok,當(dāng)前的mH拿到了

//再拿它的mCallback成員

Field mCallbackField = Handler.class.getDeclaredField("mCallback");

mCallbackField.setAccessible(true);

//2.現(xiàn)在,造一個(gè)代理mH,

// 他就是一個(gè)簡(jiǎn)單的Handler子類

ProxyHandlerCallback proxyMHCallback = new ProxyHandlerCallback();//錯(cuò),不需要重寫全部mH,只需要對(duì)mH的callback進(jìn)行重新定義

//3.替換

//將Handler的mCallback成員,替換成創(chuàng)建出來(lái)的代理HandlerCallback

mCallbackField.set(mHObj, proxyMHCallback);

} catch (Exception e) {

e.printStackTrace();

}

}

private static class ProxyHandlerCallback implements Handler.Callback {

private int EXECUTE_TRANSACTION = 159;//這個(gè)值,是android.app.ActivityThread的內(nèi)部類H 中定義的常量EXECUTE_TRANSACTION

@Override

public boolean handleMessage(Message msg) {

boolean result = false;//返回值,請(qǐng)看Handler的源碼,dispatchMessage就會(huì)懂了

//Handler的dispatchMessage有3個(gè)callback優(yōu)先級(jí),首先是msg自帶的callback,其次是Handler的成員mCallback,最后才是Handler類自身的handlerMessage方法,

//它成員mCallback.handleMessage的返回值為true,則不會(huì)繼續(xù)往下執(zhí)行 Handler.handlerMessage

//我們這里只是要hook,插入邏輯,所以必須返回false,讓Handler原本的handlerMessage能夠執(zhí)行.

if (msg.what == EXECUTE_TRANSACTION) {//這是跳轉(zhuǎn)的時(shí)候,要對(duì)intent進(jìn)行還原

try {

//先把相關(guān)@hide的類都建好

Class> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction");

Class> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem");

Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");//ClientTransaction的成員

mActivityCallbacksField.setAccessible(true);

//類型判定,好習(xí)慣

if (!ClientTransactionClz.isInstance(msg.obj)) return true;

Object mActivityCallbacksObj = mActivityCallbacksField.get(msg.obj);//根據(jù)源碼,在這個(gè)分支里面,msg.obj就是 ClientTransaction類型,所以,直接用

//拿到了ClientTransaction的List mActivityCallbacks;

List list = (List) mActivityCallbacksObj;

if (list.size() == 0) return true;

Object LaunchActivityItemObj = list.get(0);//所以這里直接就拿到第一個(gè)就好了

if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) return true;

//這里必須判定 LaunchActivityItemClz,

// 因?yàn)?最初的ActivityResultItem傳進(jìn)去之后都被轉(zhuǎn)化成了這LaunchActivityItemClz的實(shí)例

Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent");

mIntentField.setAccessible(true);

Intent mIntent = (Intent) mIntentField.get(LaunchActivityItemObj);

Intent oriIntent = (Intent) mIntent.getExtras().get(ORI_INTENT_TAG);

//那么現(xiàn)在有了最原始的intent,應(yīng)該怎么處理呢?

Log.d("1", "2");

mIntentField.set(LaunchActivityItemObj, oriIntent);

return result;

} catch (Exception e) {

e.printStackTrace();

}

}

return result;

}

}

PS:這里有個(gè)坑(請(qǐng)看上面 if(!LaunchActivityItemClz.isInstance(LaunchActivityItemObj))returntrue;, 我為什么要加這個(gè)判斷?因?yàn)?#xff0c;我通過(guò) debug,發(fā)現(xiàn),從 mH里面的 msg.what得到的 ClientTransaction,它有這么一個(gè)成員 ListmActivityCallbacks; 注意看,從 list里面拿到的 ClientTransactionItem 的實(shí)際類型是:LaunchActivityItem. ) 之前我索引源碼的時(shí)候,追查Intent的去向,只知道它最后被封裝成了一個(gè) ClientTransaction

但是,最后我從 mH的 switchcaseEXECUTE_TRANSACTION分支,去 debug(因?yàn)闊o(wú)法繼續(xù)往下查源碼)的時(shí)候,

發(fā)現(xiàn) 原本塞進(jìn)去的?ActivityResultItem?list,居然變成了?LaunchActivityItemlist,而我居然查了半天,查不到是在源碼何處發(fā)生的變化.

而 LaunchActivityItem 和 ActivityResultItem 他們兩個(gè)都是ClientTransaction的子類

public class LaunchActivityItem extends ClientTransactionItem

public class ActivityResultItem extends ClientTransactionItem

emmmm...也是很尷尬。=_ =!

不過(guò),最后能夠確定,從 mH的 switchcaseEXECUTE_TRANSACTION分支得到的 transaction,就是包含了Intent的包裝對(duì)象,所以只需要解析這個(gè)對(duì)象,就可以拿到intent,進(jìn)行還原.


OK,大功告成,安裝好 android 9.0 SDK 28的模擬器,啟動(dòng)起來(lái),運(yùn)行程序,看看能不能無(wú)清單跳轉(zhuǎn):

結(jié)果,臉一黑:報(bào)錯(cuò)!*

一份大禮:

2019-02-27 18:20:12.287 28253-28253/study.hank.com.activityhookdemo E/AndroidRuntime: FATAL EXCEPTION: main

Process: study.hank.com.activityhookdemo, PID: 28253

java.lang.RuntimeException: Unable to start activity ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)

at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)

at android.os.Handler.dispatchMessage(Handler.java:106)

at android.os.Looper.loop(Looper.java:193)

at android.app.ActivityThread.main(ActivityThread.java:6669)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222)

at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155)

at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:61)

at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72)

at study.hank.com.activityhookdemo.methodA.Main2Activity.onCreate(Main2Activity.java:14)

at android.app.Activity.performCreate(Activity.java:7136)

at android.app.Activity.performCreate(Activity.java:7127)

at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)

at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)

at android.os.Handler.dispatchMessage(Handler.java:106)

at android.os.Looper.loop(Looper.java:193)

at android.app.ActivityThread.main(ActivityThread.java:6669)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:435)

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:240)

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:219)

at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155)

at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:61)

at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72)

at study.hank.com.activityhookdemo.methodA.Main2Activity.onCreate(Main2Activity.java:14)

at android.app.Activity.performCreate(Activity.java:7136)

at android.app.Activity.performCreate(Activity.java:7127)

at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)

at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)

at android.os.Handler.dispatchMessage(Handler.java:106)

at android.os.Looper.loop(Looper.java:193)

at android.app.ActivityThread.main(ActivityThread.java:6669)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

提取關(guān)鍵信息:

Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222)

居然找不到包?問(wèn)題出在:NavUtils.getParentActivityName

>

還是被谷歌擺了一道,查原因啊,進(jìn)去 NavUtils.getParentActivityName()去看看:

看來(lái)就是這里報(bào)的錯(cuò),繼續(xù):找到可疑點(diǎn):可能就是這里拋出的異常,繼續(xù):然而,它是一個(gè)接口,那么就找它的實(shí)現(xiàn)類(注意,如果這個(gè)接口涉及到隱藏@hide的類,你用ctrl+T是不能找到的,不過(guò)也有辦法,回到NavUtil.java):哦,原來(lái) pm對(duì)象是來(lái)自 context,既然提到了 context這個(gè)抽象類,它的很多抽象方法的實(shí)現(xiàn)都在 ContextImpl,手動(dòng)進(jìn)入 ContextImpl:找這個(gè)方法:這個(gè) pm對(duì)象原來(lái)是來(lái)自 ActivityThread,然后進(jìn)行了一次封裝,最后返回出去的是一個(gè) ApplicationPackageManager對(duì)象.

那就進(jìn)入主線程咯.看看 IPackageManager的內(nèi)容:它是 AIDL動(dòng)態(tài)生成的接口,用 androidStudio是看不到接口內(nèi)容的,只能到源碼官網(wǎng),查到的接口如下:

interface IPackageManager {

...

ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);

}

Ok,看到 IBinder,就知道應(yīng)該無(wú)法繼續(xù)往下追查了,已經(jīng)跨進(jìn)程了. 前面提到了,從主線程拿到的 pm,被封裝成了 ApplicationPackageManager,那么,進(jìn)入它里面去找: getActivityInfo方法:原來(lái)異常是這里拋出的,當(dāng) mPm.getActivityInfo為空的時(shí)候,才會(huì)拋出. OK,就查到這里,得出結(jié)論: 源碼,其實(shí)對(duì) Activity的合法性進(jìn)行了兩次檢測(cè),一次是在 AMS,一次是在這里的 PMS,前面的 AMS,我們用一個(gè)已有的 Activity偽裝了一下,通過(guò)了驗(yàn)證,那么這里的 PMS,我們也可以采用同樣的方式. 注:上圖的參數(shù) ComponentNameclassName,其實(shí),他就是!Intent的 ComponentName成員:懂了吧··這里對(duì)intent又進(jìn)行了一次檢查,檢查的就是這個(gè) ComponentName.

接下來(lái)用同樣的方式對(duì) PMS的檢測(cè)進(jìn)行 hook,讓它不再報(bào)異常. 此次 hook的參照的源碼是:

hook核心代碼如下(對(duì) sPackageManager進(jìn)行代理替換,讓代理類檢查的永遠(yuǎn)是合法的 Activity):

private static void hookPMAfter28(Context context) throws ClassNotFoundException,

NoSuchFieldException, IllegalAccessException, NoSuchMethodException,

InvocationTargetException {

String pmName = Util.getPMName(context);

String hostClzName = Util.getHostClzName(context, pmName);

Class> forName = Class.forName("android.app.ActivityThread");//PM居然是來(lái)自ActivityThread

Field field = forName.getDeclaredField("sCurrentActivityThread");

field.setAccessible(true);

Object activityThread = field.get(null);

Method getPackageManager = activityThread.getClass().getDeclaredMethod("getPackageManager");

Object iPackageManager = getPackageManager.invoke(activityThread);

String packageName = Util.getPMName(context);

PMSInvocationHandler handler = new PMSInvocationHandler(iPackageManager, packageName, hostClzName);

Class> iPackageManagerIntercept = Class.forName("android.content.pm.IPackageManager");

Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new

Class>[]{iPackageManagerIntercept}, handler);

// 獲取 sPackageManager 屬性

Field iPackageManagerField = activityThread.getClass().getDeclaredField("sPackageManager");

iPackageManagerField.setAccessible(true);

iPackageManagerField.set(activityThread, proxy);

}

static class PMSInvocationHandler implements InvocationHandler {

private Object base;

private String packageName;

private String hostClzName;

public PMSInvocationHandler(Object base, String packageName, String hostClzName) {

this.packageName = packageName;

this.base = base;

this.hostClzName = hostClzName;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if (method.getName().equals("getActivityInfo")) {

ComponentName componentName = new ComponentName(packageName, hostClzName);

return method.invoke(base, componentName, PackageManager.GET_META_DATA, 0);//破費(fèi),一定是這樣

}

return method.invoke(base, args);

}

}


四.最終效果

ok,見(jiàn)證奇跡的時(shí)候到了,準(zhǔn)備好 SDK28-android9.0虛擬機(jī),運(yùn)行demo:上圖中有3個(gè)Activity跳轉(zhuǎn),但是我們看demo的清單文件,只有一個(gè) launchActivity:


結(jié)語(yǔ)

又是歷時(shí)3天,無(wú)清單注冊(cè),啟動(dòng) Activity也完成。結(jié)果是美好的,然而,過(guò)程,是糾結(jié)的,但是也是值得的. 完成了3篇博文,自己對(duì) Hook技術(shù)也有了更深層次的認(rèn)識(shí):所謂 hook,套路是簡(jiǎn)單的,就是:

1.找到hook點(diǎn)(比如上面,3次hook,一次是系統(tǒng)的AMS,一次是 ActivityThread的mH,還有一次是,ActivityThread的sPackageManager,注意,這里的sPackageManager的hook,我只針對(duì)了SDK28-9.0設(shè)備進(jìn)行了hook,在其他版本的設(shè)備上運(yùn)行可能會(huì)出現(xiàn)其他問(wèn)題,比如,Intent中的參數(shù)傳遞不正常等.)

2.用合適的方式創(chuàng)建代理對(duì)象,通常 要hook一個(gè)系統(tǒng)類,就用繼承的方式,重寫某方法。hook一個(gè)系統(tǒng)接口的實(shí)現(xiàn)類,那就用JDK的Proxy動(dòng)態(tài)代理

3.最后用代理對(duì)象,反射set,替換被hook的對(duì)象.

*套路并不難,掌握好 反射,以及 代理模式,就行了. 真正難的是哪里? 是 源碼的閱讀能力,還有寫出兼容性Hook核心代碼的能力!androidSDK 有很多版本迭代,現(xiàn)在最新的是 SDK28,我們要保證我們的hook代碼能夠兼容所有的系統(tǒng)版本,就需要大量閱讀源碼,確保萬(wàn)無(wú)一失,比如上面,如果不是在 SDK28-android9.0的模擬器上運(yùn)行發(fā)現(xiàn)報(bào)異常,我根本就不會(huì)去做最后一次的 hook.

往期hook技術(shù)文章回顧:Hook技術(shù)入門篇Hook——Activity啟動(dòng)流程(1)Hook——Activity啟動(dòng)流程(2)

知道你 “在看”

總結(jié)

以上是生活随笔為你收集整理的android 全局hook_【Hook】实现无清单启动Activity的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。