app加固原理(二)

減少入侵性

前言

  • 上次我們寫的代碼會有下面這樣的問題糯累,我們看到四大組件獲的的application是殼程序里的application刹碾,那這樣殼程序是不能提供別人使用的,所以這次講的是怎么減少這樣的入侵
  • 那就是找到 application的加載過程旬薯,然后把真實的application替換成原包里設置的


    1562854402300.png
  • 先上兩張圖


    Application綁定過程.png

    Application綁定過程2.png
  • 上面兩張圖是用戶點擊桌面app啟動的流程般又,可以參考Activity啟動流程 最后會調(diào)用 bindApplication 方法
public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, boolean autofillCompatibilityEnabled) {

            if (services != null) {
                if (false) {
                    // Test code to make sure the app could see the passed-in services.
                    for (Object oname : services.keySet()) {
                        if (services.get(oname) == null) {
                            continue; // AM just passed in a null service.
                        }
                        String name = (String) oname;

                        // See b/79378449 about the following exemption.
                        switch (name) {
                            case "package":
                            case Context.WINDOW_SERVICE:
                                continue;
                        }

                        if (ServiceManager.getService(name) == null) {
                            Log.wtf(TAG, "Service " + name + " should be accessible by this app");
                        }
                    }
                }

                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
            data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
            sendMessage(H.BIND_APPLICATION, data);
        }

綁定 application的時候會 發(fā)送一個消息,注意類型是BIND_APPLICATION丐黄,然后我們看處理的地方

public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
            }
            //..........忽略.............
}   

繼續(xù)調(diào)用 handleBindApplication(data); 從字面意思翻譯是 處理綁定application

private void handleBindApplication(AppBindData data) {
        
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
            // @4
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
        } finally {
            // If the app targets < O-MR1, or doesn't change the thread policy
            // during startup, clobber the policy to maintain behavior of b/36951662
            if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                    || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }

        //................忽略...................
 }

主要是對application的各種初始化斋配,主要看 app = data.info.makeApplication(data.restrictedBackupMode, null);這行代碼就是生成application ,然后傳了兩個參數(shù)。 然后看 @4 標記這里艰争,前面說要替換系統(tǒng)所有用application的地方這里就是一處十偶,制作出來后的application賦值給mInitialApplication

public final class LoadedApk {

    //.........忽略............
    private Application mApplication;
    //.........忽略............
    
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
            //初始化了直接返回
        if (mApplication != null) {
            return mApplication;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

        Application app = null;
        //獲取功能清單下application節(jié)點的的name屬性 
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            //創(chuàng)建上下文
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //初始化 傳三個參數(shù)類加載器、代理類园细、上下文
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        //@2
        mActivityThread.mAllApplications.add(app);
        //@3
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
        final int N = packageIdentifiers.size();
        for (int i = 0; i < N; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
        }

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        return app;
    }
    //..........忽略.............
}

LoadedApk這個類就 APK在內(nèi)存中的表示惦积,可以得到如代碼,資料猛频,功能清單等資料
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext); 這行調(diào)用的方法

public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        //一個工廠 制作出來了Application
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
         //綁定上下文
        app.attach(context);
        return app;
}
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
}

咦狮崩,看到這里不就是反射嗎,原來是從這里創(chuàng)建的鹿寻,然后下一行代碼 綁定

 public class Application extends ContextWrapper implements ComponentCallbacks2 {
  //..........忽略.............
    /**
     * @hide
     */
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
 //..........忽略.............
}

看到這里明白了把睦柴,這就是為什么之前把解密dex和加載dex文件放到attachBaseContext()里面了。

好了毡熏,到現(xiàn)在知道了從該哪里加載application了坦敌,那就好辦了, appContext.setOuterContext(app); 這里加載進來的,我只需要把 ContextImpl 這個類反射出來痢法,加載好的 的application設置到mOuterContext這個屬性上就好了
然后這里 @2 mActivityThread.mAllApplications.add(app);還用到了 把application加載到一個集合里
還有這里 @3 mApplication = app;
最后 @4 instrumentation.callApplicationOnCreate(app);
還有有時會用到 getContext().getApplicationInfo().className 所有要替換 ApplicationInfo -> className

總結要替換的 application

ContextImpl->mOuterContext(app)         通過Application的attachBaseContext回調(diào)參數(shù)獲取
ActivityThread->mActivityThread(ArrayList)  通過ContextImpl的mMainThread屬性獲取
LoadedApk->mApplication         通過ContextImpl的mPackageInfo屬性獲取
ActivityThread->mInitialApplication         通過ContextImpl的mMainThread屬性獲取
ApplicationInfo -> className

application與四大組件的關系

  • Activity
    • getApplication()得到application狱窘,在attach() 方法里 mApplication = application; 賦值
    • 在ActivityThread中會接收消息 RELAUNCH_ACTIVITY
    handleRelaunchActivity()---> handleRelaunchActivityInner() -->handleLaunchActivity();
    
    final Activity a = performLaunchActivity(r, customIntent);
    //activity創(chuàng)建
    activity =mInstrumentation.newActivity()
    
    調(diào)用了activity.attach() 完成綁定
    Application app=r.packageInfo.makApplicatio()
    
  • Service
    在ActivityThread中會接收消息CREATE_SERVICE
    
    源碼和Acitivty類同
    
  • BroadCastReciver
     在ActivityThread中會接收消息 RECEIVER
    
     handlerReceiver()        receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);
    
      把上下文件封裝了一層,以防止用戶在接收者中進行注冊廣播和綁定服務
     ReceiverRestrictedContext -> registerReceiver(){
      throw new ReceiverCallNotAllowedException(
                     "BroadcastReceiver components are not allowed to register   to receive intents");
                     }
     在ActivityThread中會接收消息BIND_APPLICATION 
    
  • ContentProvider
    ContentProvider
    
    在ActivityThread中會接收消息 BIND_APPLICATION
    handleBindApplication()
    
    AcitivityThread中 handleBindApplication方法中   if(!data.restrictedBackupMode){}
    //安裝
    installContentProviders()
    installProvider()
    
    同樣是通過newInstance反射創(chuàng)建
    localProvider.attachInfo(c);
    mContext=context;
    
              
    讓if(context.getPackageName不等于ai.packagename)我們就可以切換  application成功
    然后不走前兩個if 走下面的
    c=context.createPackageContext();
    
    ContextImpl.java中的實現(xiàn)
    //這里返回Context财搁,那我么重新這個方法蘸炸,把創(chuàng)建的application返回就行  了。
    @Override
    public Context createPackageContext(String packageName, int flags)
    

代碼實現(xiàn)

  • 今天就不貼代碼了尖奔,需要的小伙伴在 GitHub上看我的實現(xiàn)吧

效果

1562920242534.png
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搭儒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子提茁,更是在濱河造成了極大的恐慌淹禾,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茴扁,死亡現(xiàn)場離奇詭異铃岔,居然都是意外死亡,警方通過查閱死者的電腦和手機丹弱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門德撬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人躲胳,你說我怎么就攤上這事蜓洪。” “怎么了坯苹?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵隆檀,是天一觀的道長。 經(jīng)常有香客問我,道長恐仑,這世上最難降的妖魔是什么泉坐? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮裳仆,結果婚禮上腕让,老公的妹妹穿的比我還像新娘。我一直安慰自己歧斟,他們只是感情好纯丸,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著静袖,像睡著了一般觉鼻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上队橙,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天坠陈,我揣著相機與錄音,去河邊找鬼捐康。 笑死仇矾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的吹由。 我是一名探鬼主播若未,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼朱嘴,長吁一口氣:“原來是場噩夢啊……” “哼倾鲫!你這毒婦竟也來了?” 一聲冷哼從身側響起萍嬉,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤乌昔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后壤追,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磕道,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年行冰,在試婚紗的時候發(fā)現(xiàn)自己被綠了溺蕉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡悼做,死狀恐怖疯特,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肛走,我是刑警寧澤漓雅,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響邻吞,放射性物質(zhì)發(fā)生泄漏组题。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一抱冷、第九天 我趴在偏房一處隱蔽的房頂上張望崔列。 院中可真熱鬧,春花似錦旺遮、人聲如沸峻呕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘦癌。三九已至,卻和暖如春跷敬,著一層夾襖步出監(jiān)牢的瞬間讯私,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工西傀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斤寇,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓拥褂,卻偏偏與公主長得像娘锁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子饺鹃,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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

  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,040評論 0 8
  • 本文重點介紹應用程序的啟動過程莫秆,應用程序的啟動過程實際上就是應用程序中的默認Activity的啟動過程,本文將詳細...
    天宇sonny閱讀 400評論 1 0
  • 本文出自 Eddy Wiki 悔详,轉載請注明出處:http://eddy.wiki/interview-androi...
    eddy_wiki閱讀 3,263評論 0 20
  • 別人的總結不一定適合自己镊屎,所以盡量多做一些自己的總結,針對自己的薄弱點重點說明茄螃,適當?shù)慕梃b別人缝驳,少走一些彎路。最重...
    renkuo閱讀 7,405評論 2 48
  • 筆記
    近俺者赤閱讀 251評論 0 0