如何啟動(dòng)一個(gè)沒有在AndroidManifest中注冊(cè)的activity(插件化)

我們先來(lái)看一下activity啟動(dòng)的過(guò)程

activity的startActivity:

public void startActivity(Intent intent) { 
this.startActivity(intent, null); 
} 

經(jīng)過(guò)一系列調(diào)用,會(huì)調(diào)到startActivityForResult:
其中調(diào)用了Instrumentation中的execStartActivity()方法


public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, 
@Nullable Bundle options) { 
if (mParent == null) { 
options = transferSpringboardActivityOptions(options); 
Instrumentation.ActivityResult ar = 
mInstrumentation.execStartActivity( 
this, mMainThread.getApplicationThread(), mToken, this, 
intent, requestCode, options); 
if (ar != null) { 
mMainThread.sendActivityResult( 
mToken, mEmbeddedID, requestCode, ar.getResultCode(), 
ar.getResultData()); 
} 
if (requestCode >= 0) { 
mStartedActivity = true; 
} 
cancelInputsAndStartExitTransition(options); 
} else { 
if (options != null) { 
mParent.startActivityFromChild(this, intent, requestCode, options); 
} else { 
mParent.startActivityFromChild(this, intent, requestCode); 
} 
} 
} 

execStartActivity:

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 = ActivityManagerNative.getDefault() 
.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; 
} 

其中啟動(dòng)activity的方法是ActivityManagerNative.getDefault().startActivity()。ActivityManagerNative繼承了Binder,同時(shí)實(shí)現(xiàn)了IActivityManager接口,啟動(dòng)activity用到了Android中binder機(jī)制,getDefault方法實(shí)際上獲得了系統(tǒng)ActivityManagerService
checkStartActivityResult方法會(huì)檢查,到這里我們已經(jīng)找到的報(bào)錯(cuò)的地方

getDefault:返回了IActivityManager的一個(gè)單例巢钓,有沒有辦法把這個(gè)單例替換掉,自己實(shí)現(xiàn)startActivity方法來(lái)繞過(guò)系統(tǒng)對(duì)activity的校驗(yàn)?zāi)?/p>

思路:通過(guò)動(dòng)態(tài)代理的方式修改接口

步驟

1.在manifest中注冊(cè)代理的activity

<activity android:name=".ProxyActivity"/>

2.通過(guò)反射修改ActivityManagerNative類的 gDefault屬性

public void hookStartActivity() throws Exception{
       // 1>:獲取 ActivityManagerNative里面的 gDefault;
       Class<?> amnClass = Class.forName("android.app.ActivityManagerNative") ;
       // 通過(guò) ActivityManagerNative 類 獲取 gDefault屬性
       Field gDefaultField = amnClass.getDeclaredField("gDefault");
       gDefaultField.setAccessible(true);  // 設(shè)置權(quán)限
       Object gDefault = gDefaultField.get(null) ;

       // 2>:獲取gDefault中的 mInstance屬性;
       Class<?> singletonClass = Class.forName("android.util.Singleton") ;
       Field mInstanceField = singletonClass.getDeclaredField("mInstance");
       mInstanceField.setAccessible(true);
       Object iamInstance = mInstanceField.get(gDefault);

       Object a;
       Class<?> iamClass = Class.forName("android.app.IActivityManager") ;
       a = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),
               new Class[] {iamClass} ,
               // InvocationHandler:必須有一個(gè)執(zhí)行者栖茉,就是誰(shuí)去執(zhí)行這個(gè)方法
               new StartActivityInvocationHandler(iamInstance)) ;

       // 3>:重新指定
       mInstanceField.set(gDefault,a);
   }

3.設(shè)置代理

private class StartActivityInvocationHandler implements InvocationHandler {

        // 這個(gè)才是方法的執(zhí)行者
        private Object mObject ;
        // 通過(guò)構(gòu)造方法把mObject傳遞進(jìn)來(lái)
        public StartActivityInvocationHandler(Object object){
            this.mObject = object ;
        }

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

            // 在這里可以 hook到 IActivityManager中所有的方法
            Log.e("TAG" , method.getName()) ;

            // 替換intent ,過(guò)AndroidManifest.xml 檢測(cè)
            if (method.getName().equals("startActivity")){
                // 1. 首先獲取原來(lái)的intent
                Intent originIntent = (Intent) args[2];
                // 2. 創(chuàng)建一個(gè)安全的intent
                Intent safeIntent = new Intent(mContext , mProxyClass) ;
                // 3. 替換第二個(gè)參數(shù)
                args[2] = safeIntent ;
                // 4. 綁定原來(lái)的Intent
                safeIntent.putExtra(EXTER_ORIGIN_INTENT , originIntent) ;
            }

            return method.invoke(mObject , args);
        }

demo

https://github.com/CodeHurricane/PlugInDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末孵延,一起剝皮案震驚了整個(gè)濱河市吕漂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌隙袁,老刑警劉巖痰娱,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異菩收,居然都是意外死亡梨睁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門娜饵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)坡贺,“玉大人,你說(shuō)我怎么就攤上這事”榉兀” “怎么了拳亿?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)愿伴。 經(jīng)常有香客問我肺魁,道長(zhǎng),這世上最難降的妖魔是什么隔节? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任鹅经,我火速辦了婚禮,結(jié)果婚禮上怎诫,老公的妹妹穿的比我還像新娘瘾晃。我一直安慰自己,他們只是感情好幻妓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布蹦误。 她就那樣靜靜地躺著,像睡著了一般肉津。 火紅的嫁衣襯著肌膚如雪强胰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天阀圾,我揣著相機(jī)與錄音哪廓,去河邊找鬼。 笑死初烘,一個(gè)胖子當(dāng)著我的面吹牛涡真,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肾筐,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哆料,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了吗铐?” 一聲冷哼從身側(cè)響起东亦,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唬渗,沒想到半個(gè)月后典阵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡镊逝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年壮啊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撑蒜。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹啼,死狀恐怖玄渗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狸眼,我是刑警寧澤藤树,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拓萌,受9級(jí)特大地震影響岁钓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜微王,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一甜紫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骂远,春花似錦、人聲如沸腰根。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)额嘿。三九已至瘸恼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間册养,已是汗流浹背东帅。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留球拦,地道東北人靠闭。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像坎炼,于是被迫代替她去往敵國(guó)和親愧膀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容