Android 源碼分析實戰(zhàn) - 授權(quán)時攔截 QQ 用戶名和密碼

1. 說在前面

本文內(nèi)容其實是不適合發(fā)出來的,希望大家切勿用作商業(yè)用途,也切勿將功能發(fā)布到線上環(huán)境。技術(shù)一定是為生活服務的,是為了大家共同的美好生活。判斷一件事是否值得做,一定是利人利己,錯誤的事可能舒服自己而痛苦了別人,損人利己的事千萬不可為。

2. 需求背景

在測試功能時,我們可能會遇到一些偶現(xiàn)崩潰的情況,往往難以復現(xiàn)。在修改代碼時,有時往往改了這個 Bug ,在某個其他地方又引發(fā)了另一個 Bug,或者又是不經(jīng)意間修改了 UI 樣式界面。當然以上問題,我們可以看日志跟蹤,也可以多寫單元測試?;诘鹊葓鼍埃覀儾块T共建了一個 sdk ,我也是其中的一員。

其中重要的一個環(huán)節(jié),復現(xiàn) bug 時需要還原當時現(xiàn)場。也就是說要還原當時測試同學的操作步驟,需要還原當時請求的網(wǎng)絡數(shù)據(jù),需要還原數(shù)據(jù)庫,需要還原 SharedPreferences ,如果這些現(xiàn)場都能還原,就很有可能復現(xiàn)并解決這個奔潰。而這里還涉及到賬號還原,也就是說要還原當時測試同學登錄的賬號,我這里說的是在另一臺手機上自動切換登錄。

同時基于以上功能,我們就可以事先錄制很多正常操作路徑,也可以把當時的每個界面布局錄制下來。這樣每次發(fā)布之前,都在云平臺上跑一遍,就能測試發(fā)現(xiàn)很多問題。如何還原網(wǎng)絡數(shù)據(jù)、還原數(shù)據(jù)庫等現(xiàn)場本文暫時不講,本文主要來分析如何還原賬號信息。溫馨提示,只要對源碼足夠熟悉,這些都不是事。

3. 需求分析

PCG 部門的所有產(chǎn)品都是比較成熟的產(chǎn)品,任何一個產(chǎn)品都有億量級的用戶,我們寫的 sdk 是無法侵入業(yè)務代碼的。也就是說業(yè)務開發(fā)的同學在 Application 中配置一個入口,以上這些功能就都要能實現(xiàn)?,F(xiàn)在回到賬號還原上來,如果要還原賬號現(xiàn)場,那么必定會有賬號攔截與自動切換登錄,而這整個過程,業(yè)務上層是不會給我們適配代碼的。因為騰訊視頻、應用寶與騰訊新聞等等,整個 PCG 的應用都需要集成我們的 sdk。我們在寫代碼的時候一定要考慮通用性、適配性與集成成本等等。只是該功能只在測試環(huán)境集成。

PCG 應用業(yè)務側(cè)登錄都是用的 QQ 與微信第三方登錄,我們規(guī)定測試同學只能用 QQ 授權(quán)登錄,方便自動攔截切換登錄實現(xiàn)。那么接下來第一步就是如何攔截登錄的賬號信息,我之前考慮過只攔截 QQ 授權(quán)時的信息,但后面發(fā)現(xiàn)授權(quán)信息的 token 會有過期時間,后面就果斷放棄了。所以要實現(xiàn)該功能且要做到所有應用都通用,就只能想辦法攔截到 QQ 授權(quán)時的用戶名和密碼。

4. 需求實現(xiàn)

怎么在不侵入業(yè)務邏輯代碼情況下,攔截到 QQ 授權(quán)時的用戶名和密碼呢?估計大部分人都會認為無法實現(xiàn),但其實只要對源碼夠熟悉,分析實現(xiàn)起來還是挺簡單的。業(yè)務側(cè)普通的授權(quán)方式是沒有輸入用戶名和密碼的過程,我們想要攔截這些信息,勢必需要用戶有這個主動的操作過程,基于這點我們就需要引導用戶跳轉(zhuǎn)到 H5 的授權(quán)界面。那這還不簡單,我們在業(yè)務邏輯中直接打開 QQ 的 H5 授權(quán)不就可以了?但問題是我們不能改業(yè)務邏輯代碼,而且該 sdk 也不會上線,因此我們只能在 Application 初始化 sdk 里面中去做處理。

那么怎么才能在不該動原有業(yè)務邏輯情況下,點擊 QQ 授權(quán)是自動跳轉(zhuǎn)到 H5 授權(quán)界面呢?我們勢必要去翻一下 QQ 登錄提供的 sdk 源碼,發(fā)現(xiàn)其中會判斷有沒有安裝 QQ 應用,如果沒有則是提示下載 QQ ,如果有安裝 QQ 則會跳轉(zhuǎn)到 AgentActivity 進行授權(quán)。因此我們只要想辦法欺騙 QQ 的授權(quán) sdk 就行,當調(diào)方法問有沒有安裝 QQ 時,我們返回安裝了;當啟動 AgentActivity 授權(quán)時,我們偷偷的將其引導到 H5 授權(quán)界面去輸入用戶名和密碼,只有這樣我們才有機會攔截到用戶名和密碼。那么如何才能欺騙呢?這就取決于我們對源碼的熟悉程度了。

    // PMS
    private HandlerInvokeCallback mPMSCallback = new HandlerInvokeCallback() {
        @Override
        public void beforeInvoke(Method method, Object[] args) {
            try {
                // 一般上層業(yè)務邏輯中會有判斷 QQ 有沒有安裝
                if (CommonUtils.equals("getPackageInfo", method.getName())) {
                    if (CommonUtils.equals(args[0], QQ_PACKAGE_NAME)) {
                        // 替換成當前應用的包名,無論是否安裝 QQ 返回都是安裝
                        args[0] = mApplication.getPackageName();
                    }
                }
                // SDK 中會查詢 QQ_OAUTH_ACTIVITY ,默認返回 QQLoginH5AuthorizeActivity
                if (CommonUtils.equals("queryIntentActivities", method.getName())) {
                    Intent queryIntent = (Intent) args[0];
                    ComponentName componentName = queryIntent.getComponent();
                    if (componentName == null) {
                        return;
                    }
                    String queryClassName = componentName.getClassName();
                    if (CommonUtils.equals(queryClassName, QQ_OAUTH_ACTIVITY)) {
                        args[0] = new Intent(mApplication, QQLoginH5AuthorizeActivity.class);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                LogUtil.w(TAG, "pms beforeInvoke exception: " + e.getMessage());
            }
        }

        @Override
        public Object afterInvoke(Method method, Object[] args, Object returnObj) {
            return returnObj;
        }
    };

    // AMS
    private HandlerInvokeCallback mAMSCallback = new HandlerInvokeCallback() {
        @Override
        public void beforeInvoke(Method method, Object[] args) {
            try {
                // 把跳轉(zhuǎn)到 QQ 原生的頁面,都替換跳轉(zhuǎn)到 QQLoginH5AuthorizeActivity
                if (CommonUtils.equals("startActivity", method.getName())) {
                    Intent intent = (Intent) args[2];
                    ComponentName componentName = intent.getComponent();
                    if (componentName == null) {
                        return;
                    }
                    if (CommonUtils.equals(componentName.getClassName(), QQ_OAUTH_ACTIVITY)) {
                        intent.setComponent(new ComponentName(mApplication, QQLoginH5AuthorizeActivity.class));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                LogUtil.w(TAG, "ams beforeInvoke exception: " + e.getMessage());
            }
        }

        @Override
        public Object afterInvoke(Method method, Object[] args, Object returnObj) {
            return returnObj;
        }
    };

    // Application 中初始化入口
    @Override
    public void init(Application application) {
        this.mApplication = application;
        SharedPreferences accountSp = mApplication.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        mAccountSpEditor = accountSp.edit();
        // 讀取之前存的 QQ 用戶名和密碼
        mQQAccount = accountSp.getString(SP_QQ_ACCOUNT_KEY, "");
        mQQPwd = accountSp.getString(SP_QQ_PWD_KEY, "");
        mAppId = accountSp.getString(SP_QQ_APP_ID_KEY, "");
        mAuthLoginType = accountSp.getInt(SP_QQ_AUTH_TYPE_KEY, 0);
        // 手Q應用適配另一套
        if (CommonUtils.equals(application.getPackageName(), QQ_PACKAGE_NAME)) {
            adapterInterceptQQAccount();
        } else {
            hookPMSAndAMS();
        }
        LogUtil.i(TAG, "init read account info: " + mQQAccount + ", " + mQQPwd + ", " + mAppId + ", " + mAuthLoginType);
    }

只要跳轉(zhuǎn)到了 H5 的授權(quán)頁面,攔截用戶名和密碼就很容易了。其實開發(fā)中我們看似很多實現(xiàn)不了的功能,只要我們靜下心來去分析,還是能夠?qū)崿F(xiàn)的。這其實還是一個比較簡單的功能,再擴展一些像還原網(wǎng)絡現(xiàn)場,勢必需要攔截監(jiān)控用戶的網(wǎng)絡,而微視用的 wns 、手Q用的是 msf、騰訊體育是自己修改的 OkHttp 千奇百怪。

其實生活中總會有些事,不管我們有多少困難,有多少委屈,有多少艱苦,我們做下去,我們最終必然會感覺到驕傲。

視頻地址:https://pan.baidu.com/s/1jdUJtbYdf2HW101MR9Ut5w
視頻密碼:ffo1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容