天天看點

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

哈喽小夥伴們大家好~歡迎繼續學習探讨MIUI系統的安全防範知識!在上篇部落格中:Android逆向工程:帶你領略MIUI系統的賬号安全防範機制:賬号是從哪裡擷取的?我們了解到了MIUI系統通過對關鍵代碼進行封裝進系統内,對外采用統一調用接口的方式來防止關鍵代碼被破解窺視,保護了系統應用的安全,同時我們發現了擷取賬号資訊的準确接口,那麼MIUI系統除此之外,還有什麼值得稱道的安全防護措施呢?接着上篇部落格我們尚未解決的問題:為什麼刷機都無法刷掉之前已經登陸的小米賬号?我們發現的那個PassportFindDeviceImpl類它真正的作用是什麼?下面就帶着這些疑問,來開啟我們今天的學習吧!

首先還是有請我們今天的教案對象:我的小米。在下面的學習中,我們主要圍繞“我的小米”進行分析和探讨。在此聲明,本次講解内容不可用于不正當破壞行為,學習技術為主,搞破壞是不可以的!

在上篇部落格中,我們發現了擷取小米賬号的系統方法: ExtraAccountManager.getXiaomiAccount(this);,同時發現如果此方法傳回為null的話,那麼就代表着不存在小米賬号,既然如此,那麼我們就來嘗試一下,如果我們攔截到之後把它的傳回值修改為null會出現什麼樣的情況呢?

下面就開始修改我們的攔截代碼:

XposedHelpers.findAndHookMethod("miui.accounts.ExtraAccountManager", loadPackageParam.classLoader, "getXiaomiAccount", Context.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("小米賬号擷取:抓到方法ExtraAccountManager->getXiaomiAccount()");

                Class classAccount=XposedHelpers.findClass("android.accounts.Account",loadPackageParam.classLoader);

                Field []fs=classAccount.getDeclaredFields();
                for (Field field:fs){
                    field.setAccessible(true);
                    XposedBridge.log("小米賬号擷取:Account類 參數"+field.getName()+"值為:"+field.get(param.getResult()));
                }

                param.setResult(null);
            }
        });
           

在攔截方法内我們設定傳回結果為null,下面就看一下出現了什麼狀況:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

很直接,手機直接被鎖了!為什麼會這樣?看到這個界面,已經關聯的小米賬号,包括解鎖編号,喜歡刷機的小夥伴們估計會很熟悉,因為對之前已經登陸小米賬号的手機進行刷機的時候,刷機完成就會出現這個界面,提示你此手機有關聯的賬号,讓你輸入賬号密碼進行解鎖! 這就是那個刷機也刷不掉的小米賬号!

很不錯,看來MIUI系統确實是有兩把刷子。按照常理,刷機就是更換了一整個系統,其中也包括了那些存放關鍵資訊的系統檔案和系統内部資料庫,在檔案和資料庫已經被更換,資料也被清空的情況下,這個關聯的賬号資訊又是從哪裡擷取到的?MIUI系統又是怎麼知道這個裝置(手機)之前有登入的小米賬号呢?帶着這些疑問,我們接着往下逆向分析!

首先這裡我們對該鎖定界面進行界面元素分析:目标是上面那條String資源:此手機已經關聯到小米賬号(xxxx):

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

很不錯,是一條TextView,id值為:find_device_status。看到這個id值我們心裡差不多明白了七八分,為了驗證我們的猜測,那就去看看這條id被引用的代碼:使用jadx對此id值進行全局字元串搜尋:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

找到了,在類:LockedAccountLoginByFindDeviceFragment中。這個類的名字挺長,但是可以大緻的看出它的功能: 通過查找裝置然後鎖定賬号進行登陸的頁面!很不錯,名字起的倒是很直白啊~找了這個頁面,下面我們就需要知道,這個頁面是在什麼地方被調用的?接下來該怎麼查找?直接全局搜尋LockedAccountLoginByFindDeviceFragment,看看它在别的類中是否有引用,有引用的地方估計就是他被調用的地方!使用Jadax全局搜尋 LockedAccountLoginByFindDeviceFragment:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

搜尋發現,在整個“我的小米”項目中, LockedAccountLoginByFindDeviceFragment類隻在LoginBaseFragment類的checkFindDeviceStatusIfNecessary()方法下被引用了,那估計被調用就是這裡沒跑了,我們過去看一下checkFindDeviceStatusIfNecessary()方法的代碼:

protected void checkFindDeviceStatusIfNecessary() {
        if (PassportExternal.getPassportFindDeviceInterface() != null) {
            this.mCheckFindDeviceStatusTask = new CheckFindDeviceStatusTask.Builder(getActivity()).setCheckOperationFailedRunnable(new CheckOperationFailedRunnable() {
                public void run(String errorMessage) {
                    LoginBaseFragment.this.showCheckFindDeviceStatusFailedDialog(errorMessage);
                }
            }).setCheckOperationSuccessRunnable(new CheckOperationSuccessRunnable() {
                public void run(boolean isOpenFindDevice, String lockedUserId, String displayId) {
                    PassportStatHelper.statLoginCountEvent(StatConstants.CHECK_FIND_DEVICE_STATUS_SUCCESS, LoginBaseFragment.this.mOnSetupGuide);
                    if (isOpenFindDevice) {
                        LockedAccountLoginByFindDeviceFragment fragment = LockedAccountLoginByFindDeviceFragment.getLockedAccountLoginByFindDeviceFragment(lockedUserId, LoginBaseFragment.this.mServiceId, LoginBaseFragment.this.mOnSetupGuide, LoginBaseFragment.this.mFindPasswordOnPc, displayId);
                        fragment.setOnLoginInterface(LoginBaseFragment.this.mOnLoginInterface);
                        SysHelper.replaceToFragment(LoginBaseFragment.this.getActivity(), fragment, true);
                    }
                }
            }).build();
            this.mCheckFindDeviceStatusTask.executeOnExecutor(XiaomiPassportExecutor.getSingleton(), new Void[0]);
        }
    }
           

checkFindDeviceStatusIfNecessary()方法從名字上就可以大緻猜出它的功能:如果必要的話就檢查查找裝置狀态!我們關鍵看下面那個if判斷中的代碼:

if (isOpenFindDevice) {
                        LockedAccountLoginByFindDeviceFragment fragment = LockedAccountLoginByFindDeviceFragment.getLockedAccountLoginByFindDeviceFragment(lockedUserId, LoginBaseFragment.this.mServiceId, LoginBaseFragment.this.mOnSetupGuide, LoginBaseFragment.this.mFindPasswordOnPc, displayId);
                        fragment.setOnLoginInterface(LoginBaseFragment.this.mOnLoginInterface);
                        SysHelper.replaceToFragment(LoginBaseFragment.this.getActivity(), fragment, true);
                    }
           

這裡判斷一個isOpenFindDevice的布爾變量值,如果為true的話,那麼就會建立一個LockedAccountLoginByFindDeviceFragment,接着就會開啟那個鎖屏頁面!關鍵值在這個isOpenFindDevice變量,那這個isOpenFindDevice變量又是從哪來的呢?看這些代碼:

.setCheckOperationSuccessRunnable(new CheckOperationSuccessRunnable() {
                public void run(boolean isOpenFindDevice, String lockedUserId, String displayId) {
                    PassportStatHelper.statLoginCountEvent(StatConstants.CHECK_FIND_DEVICE_STATUS_SUCCESS, LoginBaseFragment.this.mOnSetupGuide);
                    if (isOpenFindDevice) {
                        LockedAccountLoginByFindDeviceFragment fragment = LockedAccountLoginByFindDeviceFragment.getLockedAccountLoginByFindDeviceFragment(lockedUserId, LoginBaseFragment.this.mServiceId, LoginBaseFragment.this.mOnSetupGuide, LoginBaseFragment.this.mFindPasswordOnPc, displayId);
                        fragment.setOnLoginInterface(LoginBaseFragment.this.mOnLoginInterface);
                        SysHelper.replaceToFragment(LoginBaseFragment.this.getActivity(), fragment, true);
                    }
                }
            }).build();
           

我們發現這是開啟了一個線程,線程名字為:CheckOperationSuccessRunnable,在這個線程裡run方法内,isOpenFindDevice變量被傳入!看下該線程的名字大緻可以猜到:檢查操作成功後進行的操作,原來這個是檢查執行結束之後調用的,這個isOpenFindDevice變量實質上是一個檢查結果的标志,true代表發現了關聯的賬号資訊,然後開啟鎖屏頁面,false代表沒有發現關聯賬号資訊,就會正常登入!

既然不是檢查實作的線程,那麼我們接着把目光轉移到上面:

this.mCheckFindDeviceStatusTask = new CheckFindDeviceStatusTask.Builder(getActivity()).setCheckOperationFailedRunnable(new CheckOperationFailedRunnable() {
                public void run(String errorMessage) {
                    LoginBaseFragment.this.showCheckFindDeviceStatusFailedDialog(errorMessage);
                }
            }).setCheckOperationSuccessRunnable(new CheckOperationSuccessRunnable() {
                public void run(boolean isOpenFindDevice, String lockedUserId, String displayId) {
                    PassportStatHelper.statLoginCountEvent(StatConstants.CHECK_FIND_DEVICE_STATUS_SUCCESS, LoginBaseFragment.this.mOnSetupGuide);
                    if (isOpenFindDevice) {
                        LockedAccountLoginByFindDeviceFragment fragment = LockedAccountLoginByFindDeviceFragment.getLockedAccountLoginByFindDeviceFragment(lockedUserId, LoginBaseFragment.this.mServiceId, LoginBaseFragment.this.mOnSetupGuide, LoginBaseFragment.this.mFindPasswordOnPc, displayId);
                        fragment.setOnLoginInterface(LoginBaseFragment.this.mOnLoginInterface);
                        SysHelper.replaceToFragment(LoginBaseFragment.this.getActivity(), fragment, true);
                    }
                }
            }).build();
           

看第一行代碼,我們發現一個名字為:CheckFindDeviceStatusTask的線程被調起,後面setCheckOperationFailedRunnable方法看名字就會知道,這是線程執行失敗後執行的方法,會開啟一個叫做CheckOperationFailedRunnable線程,正好與剛才我們分析那個setCheckOperationSuccessRunnable方法和CheckOperationSuccessRunnable線程是對應的!這裡就差不多明白了,執行檢查操作的正是CheckFindDeviceStatusTask線程,它分别設定了檢查失敗和檢查成功兩個回調方法,那下面我們就去看看這個CheckFindDeviceStatusTask實作代碼:

public class CheckFindDeviceStatusTask extends AsyncTask<Void, Void, PassportCheckFindDeviceResult> {
    private static final String PROGRESS_DIALOG_TAG = "CheckFindDeviceStatusTaskProgressDialog";
    private final Activity mActivity;
    private final CheckOperationFailedRunnable mCheckOperationFailedRunnable;
    private final CheckOperationSuccessRunnable mCheckOperationSuccessRunnable;
    private SimpleDialogFragment mProgressDialogFragment;

    public static class Builder {
        private Activity mActivity;
        private CheckOperationFailedRunnable mCheckOperationFailedRunnable;
        private CheckOperationSuccessRunnable mCheckOperationSuccessRunnable;

        public Builder(Activity activity) {
            this.mActivity = activity;
        }

        public Builder setCheckOperationFailedRunnable(CheckOperationFailedRunnable runnable) {
            this.mCheckOperationFailedRunnable = runnable;
            return this;
        }

        public Builder setCheckOperationSuccessRunnable(CheckOperationSuccessRunnable runnable) {
            this.mCheckOperationSuccessRunnable = runnable;
            return this;
        }

        public CheckFindDeviceStatusTask build() {
            return new CheckFindDeviceStatusTask(this.mActivity, this.mCheckOperationFailedRunnable, this.mCheckOperationSuccessRunnable);
        }
    }

    public interface CheckOperationFailedRunnable {
        void run(String str);
    }

    public interface CheckOperationSuccessRunnable {
        void run(boolean z, String str, String str2);
    }

    private CheckFindDeviceStatusTask(Activity activity, CheckOperationFailedRunnable checkOperationFailedRunnable, CheckOperationSuccessRunnable checkOperationSuccessRunnable) {
        this.mActivity = activity;
        this.mCheckOperationFailedRunnable = checkOperationFailedRunnable;
        this.mCheckOperationSuccessRunnable = checkOperationSuccessRunnable;
    }

    protected void onPreExecute() {
        this.mProgressDialogFragment = (SimpleDialogFragment) this.mActivity.getFragmentManager().findFragmentByTag(PROGRESS_DIALOG_TAG);
        if (this.mProgressDialogFragment == null) {
            this.mProgressDialogFragment = new AlertDialogFragmentBuilder(2).setMessage(this.mActivity.getString(R.string.passport_login_check_find_device)).create();
            this.mProgressDialogFragment.setCancelable(false);
            this.mProgressDialogFragment.show(this.mActivity.getFragmentManager(), PROGRESS_DIALOG_TAG);
        }
    }

    protected PassportCheckFindDeviceResult doInBackground(Void... params) {
        return PassportExternal.getPassportFindDeviceInterface().checkFindDeviceStatus(this.mActivity.getApplicationContext());
    }

    protected void onPostExecute(PassportCheckFindDeviceResult result) {
        if (!(this.mProgressDialogFragment == null || this.mProgressDialogFragment.getActivity() == null || this.mProgressDialogFragment.getActivity().isFinishing())) {
            this.mProgressDialogFragment.dismissAllowingStateLoss();
        }
        if (result != null && this.mActivity != null && !this.mActivity.isFinishing()) {
            if (result.checkOperationResult == CheckOperationResult.FAILED) {
                if (this.mCheckOperationFailedRunnable != null) {
                    this.mCheckOperationFailedRunnable.run(result.errorMessage);
                }
            } else if (result.checkOperationResult != CheckOperationResult.SUCCESS) {
                throw new IllegalStateException("Normally not reachable. ");
            } else if (this.mCheckOperationSuccessRunnable != null) {
                this.mCheckOperationSuccessRunnable.run(result.isOpen, result.sessionUserId, result.displayId);
            }
        }
    }
}
           

代碼量有點多,不過沒關系,我們首先看到CheckFindDeviceStatusTask繼承的是AsyncTask,那就好辦了,執行具體操作邏輯的方法是doInBackground(),我們直接看它的doInBackground()實作方法:

protected PassportCheckFindDeviceResult doInBackground(Void... params) {
        return PassportExternal.getPassportFindDeviceInterface().checkFindDeviceStatus(this.mActivity.getApplicationContext());
    }
           

隻有一句代碼,調用了這個checkFindDeviceStatus()方法!看到這裡,小夥伴們有沒有發現這個checkFindDeviceStatus()方法我們很眼熟啊,不就是那個PassportFindDeviceImpl類中重寫實作的PassportFindDeviceInterface接口中的方法嗎?!我們點選這個方法去檢視它的來源:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

果然是它!那這裡就好辦了,我們直接打開 PassportFindDeviceImpl類再次分析一下這個checkFindDeviceStatus()方法:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

我們在昨天分析查找擷取賬号資訊的時候,查到過這個方法,隻不過那個時候調用的是它的onLoginSuccess()方法。我們可以看到在方法checkFindDeviceStatus()内,通過代碼FindDeviceInfo info = findDeviceStatusManager.getFindDeviceInfoFromServer(); 擷取到了一個FindDeviceInfo的執行個體,這個執行個體則用來給下面的PassportCheckFindDeviceResult執行個體指派,最後傳回這個PassportCheckFindDeviceResult執行個體。getFindDeviceInfoFromServer()方法和FindDeviceInfo類都是無法檢視的,他們同樣是封裝在系統内的方法,不存在該項目内。包路徑:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

我們還可以看到指派總共有四個值,分别為:isOpen,isLocked,sessionUserId,displayId。注意這個isOpen值,這個布爾值就是上面說的那個關鍵判斷值:isOpenFindDevice!我們回去看下CheckFindDeviceStatusTask的收尾方法:onPostExecute:

protected void onPostExecute(PassportCheckFindDeviceResult result) {
        if (!(this.mProgressDialogFragment == null || this.mProgressDialogFragment.getActivity() == null || this.mProgressDialogFragment.getActivity().isFinishing())) {
            this.mProgressDialogFragment.dismissAllowingStateLoss();
        }
        if (result != null && this.mActivity != null && !this.mActivity.isFinishing()) {
            if (result.checkOperationResult == CheckOperationResult.FAILED) {
                if (this.mCheckOperationFailedRunnable != null) {
                    this.mCheckOperationFailedRunnable.run(result.errorMessage);
                }
            } else if (result.checkOperationResult != CheckOperationResult.SUCCESS) {
                throw new IllegalStateException("Normally not reachable. ");
            } else if (this.mCheckOperationSuccessRunnable != null) {
                this.mCheckOperationSuccessRunnable.run(result.isOpen, result.sessionUserId, result.displayId);
            }
        }
    }
           

在這裡對傳回的PassportCheckFindDeviceResult執行個體進行了處理,注意這句代碼:

this.mCheckOperationSuccessRunnable.run(result.isOpen, result.sessionUserId, result.displayId);
           

調用了檢查成功方法,傳入的關鍵判斷值正是isOpen變量!

分析到這裡,我們差不多就明白了大緻過程:在無法通過正常途徑獲得到賬号資訊的情況下,即方法ExtraAccountManager.getXiaomiAccount(this);傳回的Account執行個體為空的時候(表示沒有賬号登入的時候),系統就會去啟動CheckFindDeviceStatusTask這個線程去進行檢查,這裡的檢查是檢查是否存在關聯賬号,如果存在關聯賬号資訊,那麼就會展示鎖定頁面,提示你輸入密碼進行解鎖,如果沒有發現關聯賬号,那麼就不會展示鎖定頁面!

關鍵還是這個讀取到的FindDeviceInfo執行個體,我們雖然無法窺探它的代碼,但是我們照樣可以看到他的變量值,編寫攔截代碼,目标類是FindDeviceStatusManager,目标方法是:getFindDeviceInfoFromServer():

XposedHelpers.findAndHookMethod("miui.cloud.finddevice.FindDeviceStatusManager", loadPackageParam.classLoader, "getFindDeviceInfoFromServer", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("小米賬号擷取:抓到方法FindDeviceStatusManager->getFindDeviceInfoFromServer()");
                Class classFindDeviceInfo=XposedHelpers.findClass("miui.cloud.finddevice.FindDeviceInfo",loadPackageParam.classLoader);
             
                Field []fs=classFindDeviceInfo.getDeclaredFields();
                for (Field field:fs){
                    field.setAccessible(true);
                    XposedBridge.log("小米賬号擷取:FindDeviceInfo類:參數"+field.getName()+"值為:"+field.get(param.getResult()));

                
                }

              
            }
        });
           

這裡我們攔截到方法後,然後通過反射機制通路FindDeviceInfo執行個體的變量值進行列印,下面就運作一下看看這個FindDeviceInfo執行個體的值都是什麼:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

這下終于明白了!displayId值原來為是那個解鎖編号,sessionUserId值就是小米ID!原來檢查查找裝置竟然是這個鬼東西,就算刷機也能發現關聯賬号資訊就是從這裡面擷取的!

下面我們對攔截代碼進行修改,把displayId和sessionUserId值修改為null,isOpen值修改為false!這樣看看它還會不會把我的界面給鎖定了,修改攔截代碼如下:

XposedHelpers.findAndHookMethod("miui.cloud.finddevice.FindDeviceStatusManager", loadPackageParam.classLoader, "getFindDeviceInfoFromServer", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("小米賬号擷取:抓到方法FindDeviceStatusManager->getFindDeviceInfoFromServer()");
                Class classFindDeviceInfo=XposedHelpers.findClass("miui.cloud.finddevice.FindDeviceInfo",loadPackageParam.classLoader);
                Object objectDeviceInfo=classFindDeviceInfo.newInstance();
                Field []fs=classFindDeviceInfo.getDeclaredFields();
                for (Field field:fs){
                    field.setAccessible(true);
                    XposedBridge.log("小米賬号擷取:FindDeviceInfo類:參數"+field.getName()+"值為:"+field.get(param.getResult()));

                    switch (field.getName()){
                        case "displayId":field.set(objectDeviceInfo,null);break;
                        case "sessionUserId":field.set(objectDeviceInfo,null);break;
                        case "isLocked":field.setBoolean(objectDeviceInfo,false);break;
                        case "isOpen":field.setBoolean(objectDeviceInfo,false);break;
                        default:field.set(objectDeviceInfo,field.get(param.getResult()));
                    }
                }

                param.setResult(objectDeviceInfo);
            }
        });
           

我們通過反射建立一個FindDeviceInfo類對象,設定新對象的變量值,然後修改傳回結果為新的對象。運作一下看看效果:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

哈哈,成功了,沒有把我的頁面給鎖定了!不過還是有細心的小夥伴會問,為什麼輸入框内還是出現了之前登入的小米賬号呢?不急,我們這就看看,對頁面進行分析:檢視這個輸入框的id為:et_account_name,然後老樣子用jadx進行全局搜尋,找到它的引用在類LoginBaseFragment中:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

類中查找這個輸入框設定文本的方法:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

 在onViewCreated()方法内發現了該輸入框設定的文本,為字元串lastLoginUserId,lastLoginUserId又是方法getLastLoginAccountName()的傳回值,我們去看一下這個getLastLoginAccountName()方法:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

原來是從xml檔案中拿到的,那這個xml字段又是在什麼時候被放進去的呢?還記得我們上篇部落格分析的那個addOrUpdateAccountManager()方法了嗎?:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

我們當時隻重點看了那個onAddOrUpdateAccountManagerSuccess()方法,在這個方法的下面,是saveLastLoginAccountName()方法,我們去看看這個方法做了什麼:

Android逆向工程:MIUI系統大揭秘:去不掉的小米賬号!

答案非常的顯而易見,把最新登陸的賬号儲存在了xml檔案中!這樣做的目的并不涉及到安全政策,隻是為了友善使用者,直接輸入密碼就可以登入,不用輸入賬号了!

好了,至此我們成功的修改掉了鎖定頁面,我們再次把目光放到 FindDeviceInfo info = findDeviceStatusManager.getFindDeviceInfoFromServer();上。為什麼刷機會刷不掉?這裡很顯然的是,MIUI系統把賬号資訊存放到了一個就硬體裝置裡,對外提供了一個擷取方法 getFindDeviceInfoFromServer()來擷取儲存在硬體内的賬号資料。這個硬體是什麼?很大的可能是CPU,CPU内經常會被硬體廠商放入一些對使用者來說極為重要的關鍵資料,放在CPU内比放在系統檔案内可要安全可靠的多。比如華為,他把使用者的指紋資訊放在了CPU内,對外隻提供一個比對接口,拒不同意騰訊要求把指紋資訊上傳至騰訊雲端,也是以到現在華為手機上使用微信支付還是不能使用指紋支付!

這裡部落客點評MIUI系統的安全政策就是:把使用者的關鍵資訊放置在了CPU内(這裡姑且認為是CPU),對外提供了一個統一負責寫入和讀取的類:FindDeviceStatusManager。當我們在刷機的時候,把儲存在系統檔案和資料庫中的賬号資訊删除掉了,通過那個統一擷取賬号資訊接口ExtraAccountManager.getXiaomiAccount(Context);無法擷取賬号資訊,這時候系統就會去調用FindDeviceStatusManager的擷取接口,來檢視CPU内是否儲存的有使用者資訊,如果存在的話,那就說明該手機處于不安全的狀态(比如手機丢失被人為刷機),就會把頁面鎖定,輸入密碼才能進行解鎖,這樣就會極大的保證了手機裝置和賬号的安全性!

 好了,本篇部落格到此結束,有不明白的地方請評論留言,我看到後會及時進行回複!有需要引用本文的地方請标明出處,謝謝合作!最後祝大家豬年大吉,紅紅火火!