2023-10-05

編寫Xposed模塊

了解完Xposed框架的相關(guān)知識后,我們還要編寫一些模塊代碼,才能實(shí)現(xiàn)我們的監(jiān)測操作。
首先在gradle里面依賴一下xposed的api:

compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'

在進(jìn)行Xposed模塊開發(fā)之前,我們有必要了解一下Xposed API。完成一個模塊的開發(fā)至少有兩步要做:

1、編寫一個java類并實(shí)現(xiàn)**IXposedHookLoadPackage**接口,實(shí)現(xiàn)**handleLoadPackage**方法進(jìn)行自定義的監(jiān)測操作
2、注冊這個java類

編寫代碼

假如我們需要監(jiān)測的方法是:
[圖片上傳失敗...(image-819ac2-1696475692002)]
那么,我們的初始方法就可以寫成這個樣子:

public class HookTrack implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {

    }
}

handleLoadPackage中,調(diào)用XposedHelpers類的findMethodHook來進(jìn)行,在寫代碼的時候,我們發(fā)現(xiàn)其實(shí)有兩個方式可以選用:
[圖片上傳失敗...(image-219c78-1696475692002)]
區(qū)別在于第一個方法傳入的是class本體,然后源碼那邊使用的classLoader就是class.getClassLoader;第二種不需要class本體,只需要指定這個class的名字,然后再指定加載這個class的classLoader。從便捷上來說,第一種無疑是便捷的。但是第二種的靈活度比第一種高。假如有一些類是第三方SDK里面的,而這個SDK沒在你源碼里面,是以插件形式在你app安裝完后才加進(jìn)來的。這時候,你在編碼階段是沒有辦法得到這個class本體的,所以第二種方法可以看作是能hook運(yùn)行時的class,并且官方注釋還給出了第二種的使用模式:
[圖片上傳失敗...(image-c3e6d8-1696475692002)]
因此,按照官方提供的思路,我們可以這樣寫:

XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getDeviceId",
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getDeviceId()獲取了imei");
                    }
                }
        );

注意到我們最后的那個回調(diào)函數(shù)XC_methodHook,
[圖片上傳失敗...(image-4ef009-1696475692002)]
首先,這是一個抽象類,不是接口。beforeHookMethodafterHookMethod從字面意思也能看出是在hook前后的調(diào)用回調(diào)。然后其構(gòu)造函數(shù)有兩個,有一個是帶int類型的,傳入的是一個設(shè)置hook優(yōu)先級的數(shù)字。
[圖片上傳失敗...(image-7e38fd-1696475692002)]
從方法注釋上看,這個priority會影響后面beforeHookMethodafterHookMethod的調(diào)用順序。優(yōu)先級越高的Hook,其beforeHook方法會越先執(zhí)行,然后其afterHook方法會在最后執(zhí)行。如果存在hook多個方法,且所有的priority都相同,會依次此執(zhí)行完這個方法的before和after在執(zhí)行下一個方法的before和after,以此類推。
而采用無參構(gòu)造的,其priority是一個系統(tǒng)默認(rèn)值50:
[圖片上傳失敗...(image-1a4b70-1696475692002)]
假如我們Hook了3個方法A,B,C。在priority相同和不同時的調(diào)用關(guān)系可以參考下圖:

[圖片上傳失敗...(image-1d1204-1696475692002)]
知道了上面的原理后,我們就應(yīng)該選用默認(rèn)或者相同priority的方式來進(jìn)行hook。
扯了這么多,大家也別嫌麻煩,工欲善其事,必先利其器?,F(xiàn)在再回到之前的代碼。我們在beforeHookMethod里面調(diào)用了

XposedBridge.log(lpparam.packageName + "調(diào)用getDeviceId()獲取了imei");

XposedBridge也是rovo89開發(fā)的一個Xposed的輔助庫,調(diào)用其log方法后可以在手機(jī)端的Xposed管理器里面顯示相關(guān)信息,這一步的意思表示我們監(jiān)測了app調(diào)用android.telephony.TelephonyManager這個類的getDeviceId方法

打印方法調(diào)用棧

上面的所有操作知識標(biāo)記了調(diào)沒調(diào)用指定的方法。但是如果調(diào)用了,是誰調(diào)用的,其實(shí)我們時不清楚的。這樣其實(shí)不利于我們查找問題的根源?;乜幢疚牡牡谝粡埿磐ㄔ旱膱D,發(fā)現(xiàn)他們檢測時,其實(shí)給了方法調(diào)用棧。那么我們現(xiàn)在就來模擬一下這種操作。
我們需要打印的是整個hook期間的方法棧,那么這個操作就應(yīng)該放在afterHookMethod里面,于是,我們可以寫成這樣:

XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getDeviceId",
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getDeviceId()獲取了imei");
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        //在這里寫調(diào)用方法棧過程
                    }
                }
        );

日志打印的話自然還是用到XposedBridgelog方法。由于我們需要hook的方法不止一個,而我們打印方法調(diào)用棧又是一樣的操作,于是乎我們可以自己寫一個抽象類繼承XC_MethodHook,只實(shí)現(xiàn)afterMethodHook方法,在里面做統(tǒng)一的方法棧追蹤操作。因此,我們先自定義一個DumpMethodHook的類,代碼如下:

public abstract class DumpMethodHook extends XC_MethodHook {

    /**
     * 該方法會在Hook了指定方法后調(diào)用
     * @param param
     */
    @Override
    protected void afterHookedMethod(MethodHookParam param) {
        //在這里,我們dump一下調(diào)用的方法棧信息
        dump2();
    }

    /**
     * dump模式一:根據(jù)線程進(jìn)行過濾
     */
    private static void dump() {
        for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
            Thread thread = (Thread) stackTrace.getKey();
            StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
            // 進(jìn)行過濾
            if (thread.equals(Thread.currentThread())) {
                continue;
            }
            XposedBridge.log("[Dump Stack]" + "**********線程名字:" + thread.getName() + "**********");
            int index = 0;
            for (StackTraceElement stackTraceElement : stack) {
                XposedBridge.log("[Dump Stack]" + index + ": " + stackTraceElement.getClassName()
                        + "----" + stackTraceElement.getFileName()
                        + "----" + stackTraceElement.getLineNumber()
                        + "----" + stackTraceElement.getMethodName());
            }
            // 增加序列號
            index++;
        }
        XposedBridge.log("[Dump Stack]" + "********************* over **********************");
    }

    /**
     * dump模式2:類信通院報告模式
     */
    private static void dump2(){
        XposedBridge.log("Dump Stack: "+"---------------start----------------");
        Throwable ex = new Throwable();
        StackTraceElement[] stackElements = ex.getStackTrace();
        if (stackElements != null) {
            for (int i= 0; i < stackElements.length; i++) {
                StringBuilder sb=new StringBuilder("[方法棧調(diào)用]");
                sb.append(i);
                XposedBridge.log("[Dump Stack]"+i+": "+ stackElements[i].getClassName()
                        +"----"+stackElements[i].getFileName()
                        +"----" + stackElements[i].getLineNumber()
                        +"----" +stackElements[i].getMethodName());
            }
        }
        XposedBridge.log("Dump Stack: "+ "---------------over----------------");
    }
}

通過查詢資料,我寫了兩種方法棧打印的操作。第一種打印得比較細(xì)一些,但是實(shí)際測試要卡頓一點(diǎn)。第二種就和信通院報告差不多了,而且沒有明顯卡頓。
寫好了自定義的回調(diào),這時我們只需要將前面的XC_MethodHook替換為DumpMethodHook即可:

XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getDeviceId",
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getDeviceId()獲取了imei");
                    }
                }
        );

需要監(jiān)測的方法

既然合規(guī)這件事情是工信部搞出來的,那么我們自然要看一下當(dāng)時的這份紅頭文件——工信部信管函「164號文」
下面是我目前整理出來的需要hook的一些方法:

方法名字 所屬包名 作用
getDeviceId android.telephony.TelephonyManager 獲取設(shè)備號
getDeviceId(int) android.telephony.TelephonyManager getDeviceId的帶參版本
getImei android.telephony.TelephonyManager 安卓8增加的獲取IMEI的方法
getImei(int) android.telephony.TelephonyManager getImei的帶參版本
getSubscriberId android.telephony.TelephonyManager 獲取IMSI
getMacAddress android.net.wifi.WifiInfo 獲取MAC地址
getHardwareAddress java.net.NetworkInterface 獲取MAC地址
getString android.provider.Settings.Secure 獲取系統(tǒng)相關(guān)信息字符來拼接deviceId
getLastKnownLocation LocationManager 獲取GPS定位信息
requestLocationUpdates LocationManager 位置、時間發(fā)生改變的時候獲取定位信息

上面的方法信息可能不全,如果大家有更好的意見可以留言。我看網(wǎng)上很多資料是沒有對requestLocationUpdates和安卓8的新增方法getImei進(jìn)行監(jiān)控的,這里我加了進(jìn)來。

對Hook的APP進(jìn)行過濾,設(shè)置白名單

一般來講,你的手機(jī)安裝的不止一個app。如果用上面的代碼去監(jiān)測,實(shí)際會監(jiān)測你手機(jī)上所有的app。這就導(dǎo)致日志會很雜亂,我們其實(shí)只關(guān)心指定的app。因此我們需要設(shè)置一個白名單進(jìn)行過濾:

/**
  * 需要Hook的包名白名單
  */
 private static final String[] whiteList = {
         "com.cjs.drv",
         "com.cjs.hegui30.demo"
 };

里面填寫的就是你需要監(jiān)測的app的包名。
然后我們在HandleLoadPackage方法的最開始,寫一段過濾的操作:

/*判斷hook的包名*/
boolean res = false;
for (String pkgName : whiteList) {
    if (pkgName.equals(lpparam.packageName)) {
        res = true;
        break;
    }
}
if (!res) {
    Log.e(TAG, "不符合的包:" + lpparam.packageName);
    return;
}

最終,貼上一個成品的代碼:

public class HookTrack implements IXposedHookLoadPackage {
    private static final String TAG = "HookTrack";

    /**
     * 需要Hook的包名白名單
     */
    private static final String[] whiteList = {
            "com.cjs.drv",
            "com.bw30.zsch",
            "com.bw30.zsch.magic",
            "com.cjs.hegui30.demo"
    };

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {

        if (lpparam == null) {
            return;
        }

        Log.e(TAG, "開始加載package:" + lpparam.packageName);
        /*判斷hook的包名*/
        boolean res = false;
        for (String pkgName : whiteList) {
            if (pkgName.equals(lpparam.packageName)) {
                res = true;
                break;
            }
        }
        if (!res) {
            Log.e(TAG, "不符合的包:" + lpparam.packageName);
            return;
        }

        //固定格式
        XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(), // 需要hook的方法所在類的完整類名
                lpparam.classLoader,                            // 類加載器,固定這么寫就行了
                "getDeviceId",                     // 需要hook的方法名
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getDeviceId()獲取了imei");
                    }
                }
        );
        XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getDeviceId",
                int.class,
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getDeviceId(int)獲取了imei");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getSubscriberId",
                int.class,
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getSubscriberId獲取了imsi");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getImei",
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getImei獲取了imei");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getImei",
                int.class,
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getImei(int)獲取了imei");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                android.net.wifi.WifiInfo.class.getName(),
                lpparam.classLoader,
                "getMacAddress",
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getMacAddress()獲取了mac地址");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                java.net.NetworkInterface.class.getName(),
                lpparam.classLoader,
                "getHardwareAddress",
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getHardwareAddress()獲取了mac地址");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                android.provider.Settings.Secure.class.getName(),
                lpparam.classLoader,
                "getString",
                ContentResolver.class,
                String.class,
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用Settings.Secure.getstring獲取了" + param.args[1]);
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                LocationManager.class.getName(),
                lpparam.classLoader,
                "getLastKnownLocation",
                String.class,
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用getLastKnownLocation獲取了GPS地址");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                LocationManager.class.getName(),
                lpparam.classLoader,
                "requestLocationUpdates",
                String.class,
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "調(diào)用requestLocationUpdates獲取了GPS地址");
                    }
                }
        );
    }
}

注冊模塊代碼

上面的操作到目前為止也只是在你的安卓項(xiàng)目中添加了一個java類。如何讓xposed識別到我們寫的代碼是個xposed模塊呢?這就需要注冊一下這個類。
注冊分兩步操作:
1、在AndroidManifest.xml中編寫meta信息

<!--  標(biāo)志該 apk 為一個 Xposed 模塊,供 Xposed 框架識別-->
<meta-data
    android:name="xposedmodule"
    android:value="true" />

<!--模塊說明,一般為模塊的功能描述-->
<meta-data
    android:name="xposeddescription"
    android:value="這個模塊是用來檢測用戶隱私合規(guī)的,在用戶未授權(quán)同意前,調(diào)用接口獲取信息屬于違規(guī)" />

<!--模塊兼容版本-->
<meta-data
    android:name="xposedminversion"
    android:value="54" />

在application節(jié)點(diǎn)里面加上這三個meta信息。那個說明會最終顯示在xposed管理器上面:
[圖片上傳失敗...(image-beb013-1696475692002)]
注意:填寫meta信息是標(biāo)記我們這個apk是個xposed模塊的關(guān)鍵,否則xposed installer不會識別。

2、在項(xiàng)目asset文件夾下面新建xposed_init文件
[圖片上傳失敗...(image-d81e84-1696475692002)]
在里面寫上我們實(shí)現(xiàn)IXposedHookLoadPackage那個類的包名+類名

com.cjs.hegui30.HookTrack

這樣我們就寫好了自定義的xposed模塊。Xposed在加載的時候會從這個文件里面讀取需要初始化的類。
至此,我們的所有代碼就編寫完成了,此時裝在手機(jī)后,可以在xposed installer里面識別激活了。

其他

源碼同時捆綁了一個快速測試的demo和相關(guān)的apk文件,demo可以單獨(dú)編譯成apk,記得切換
[圖片上傳失敗...(image-9f72eb-1696475692002)]
[圖片上傳失敗...(image-dae7f9-1696475692002)]


操作手冊

一、準(zhǔn)備條件

1、編譯合規(guī)檢測的Xposed模塊源碼

下載源碼,修改設(shè)置白名單,編譯成apk,安裝到手機(jī)
相關(guān)操作參考《安卓端自行實(shí)現(xiàn)工信部要求的隱私合規(guī)檢測一(教你手寫Xposed模塊代碼)》

注意:源碼里面包含了各種安裝包及demo

2、已經(jīng)root的手機(jī)可以下載Xposed.apk

Xposed在github上面開源,可以自己下載XposedInstaller的源碼進(jìn)行編譯,也可以直接下載已經(jīng)編譯好的apk。

需要注意的是,安卓5以上和以下的安裝版本是不一樣的
支持安卓5及以上的XposedInstaller
支持安卓5以下的XposedInstaller

3、沒有root的手機(jī)可以下載VirtualXposed.apk

VirtualXposed在github上面有專門的release頁面,其作者在0.20.x的版本的時候放棄了對32位應(yīng)用的支持,理由是谷歌商店未來只允許64位的app上架,不想花更多精力維護(hù)32位的開發(fā)。如果你的app為32位應(yīng)用,因此不能用0.20.x及之后版本的VirtualXposed

0.20.3版本(支持64位應(yīng)用)
0.18.2版本(支持32位應(yīng)用)
注意:由于0.20.x版本更換了包名,所以它和0.18.x的版本能同時安裝

4、下載合規(guī)檢測測試程序

該項(xiàng)非必須,僅作為合規(guī)快速校驗(yàn)

下載地址:合規(guī)檢測測試程序apk


二、具體操作

這里以對“合規(guī)檢測測試程序“的校驗(yàn)進(jìn)行說明。

1、安裝

  • 已root的用戶
 1. 安裝xposed.apk

在root手機(jī)上安裝xposed框架的操作可能會比較麻煩,詳情問問度娘

 2. 安裝合規(guī)檢測xposed模塊.apk
 3. 安裝合規(guī)檢測測試程序.apk
  • 未root用戶
 1. 先安裝virtual-xposed.apk
 2. 后續(xù)操作有兩種方案:

第一種可以和已root用戶一樣,直接將合規(guī)檢測xposed模塊.apk和合規(guī)檢測測試程序.apk安裝在手機(jī)真機(jī)上,然后在virtual-xposed里面克隆這兩個app;
第二種的話就是不在真機(jī)裝,直接在virtual-xposed里面裝,具體操作如下:

  1. 打開virtual-xposed,點(diǎn)擊底部的菜單按鈕,進(jìn)入到virtual-xposed的菜單界面
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715095447417.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖1" align=center />
    </div>

  2. 在菜單界面有兩個關(guān)鍵點(diǎn)需要注意,一個是頂部的添加應(yīng)用,一個是底部的重啟。后者在激活模塊的時候需要使用。這里我們先點(diǎn)擊添加應(yīng)用
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715100616238.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖2" align=center />
    </div>

  3. 添加應(yīng)用界面默認(rèn)的tab項(xiàng)就是克隆APP,如果使用克隆的方式的話,就要先找到安裝的合規(guī)檢測xposed模塊和合規(guī)檢測測試程序,并勾選,然后選擇底部的安裝;如果選擇的是虛擬機(jī)安裝,就直接點(diǎn)擊右下角的加號按鈕,選擇本地的apk安裝包進(jìn)行安裝。
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715101257676.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖3" align=center />
    </div>

  4. 在克隆APP中,會彈出是否使用太極安裝,我們還是選擇virtual-xposed安裝
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715101819206.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖4" align=center />
    </div>

  5. 安裝完app后,下一步就是激活模塊。我們返回到1中的界面,上滑,進(jìn)入到app列表界面,在這里可以看見我們剛剛安裝的3個app
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715102701144.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖5" align=center />
    </div>

  6. 最右邊的那邊Xposed Installer就是我們的xposed控制界面 ,點(diǎn)擊進(jìn)入。大大的綠勾表示我們的xposed框架已經(jīng)激活。點(diǎn)擊左上角三杠
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715102939132.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖6" align=center />
    </div>

  7. 在彈出來的菜單界面中有兩個項(xiàng)要注意:模塊用于安裝/卸載xposed模塊,日志用于查看合規(guī)檢測的結(jié)果,后面會用到。這里我們點(diǎn)擊模塊。
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715103119338.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖7" align=center />
    </div>

  8. 可以看見有一個合規(guī)檢測的xposed模塊,勾上它。勾上后需要重啟系統(tǒng)。如果是root用戶在真機(jī)下操作xposed框架,就需要真機(jī)重啟。但是我們用的是virtual-xposed,所以只需要重啟虛擬機(jī)就行了。這時候,返回到2的界面,點(diǎn)擊重啟
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715103340219.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖8" align=center />
    </div>

  9. 重啟成功的話,底部會有提示語展示
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715103551253.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖9-1" align=center />
    <img src="https://img-blog.csdnimg.cn/20210715103629292.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖9-2" align=center />
    </div>

2、如何檢測

  1. 先打開我們安裝的測試app

里面有四個按鈕,分別對應(yīng)四種不同的檢測條件,我們以第一種模擬獲取IMSI來說明,先別點(diǎn)擊任何按鈕。
<div align="center">

<img src="https://img-blog.csdnimg.cn/20210715103931453.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖10" align=center />

</div>

  1. 查看日志

回到我們在安裝講解中的第7步界面,里面有個日志選項(xiàng),點(diǎn)擊它。該界面右上角有保存按鈕,還有個三個點(diǎn)的菜單按鈕,點(diǎn)擊菜單按鈕后有兩個項(xiàng)需要注意:

  • 立即清理日志會清空當(dāng)前界面的所有日志,我們先點(diǎn)擊清空一下。
  • 重新載入會刷新最新的日志進(jìn)界面。因?yàn)槿罩镜娘@示不是自動的,要想看到最新的結(jié)果,就要手動刷新。
    <div align="center">
    <img src="https://img-blog.csdnimg.cn/20210715104617934.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖11-1" align=center />
    <img src="https://img-blog.csdnimg.cn/20210715104640552.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NqczE1MzQ3MTcwNDA=,size_16,color_FFFFFF,t_70" width = "300" alt="圖11-2" align=center />
    </div>

接下來回到我們的測試app界面,點(diǎn)擊模擬獲取IMSI,然后再返回到日志界面,并且重新載入,接著就能看見如下的日志記錄:
[圖片上傳失敗...(image-19438-1696475692002)]

上面的截圖主要針對virtual-xposed來講的。對于xposed而言,操作大同小異。最大的區(qū)別就是xposed需要真實(shí)地重啟手機(jī),這點(diǎn)的話要麻煩一點(diǎn)。

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

相關(guān)閱讀更多精彩內(nèi)容

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