編寫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)]
首先,這是一個抽象類,不是接口。beforeHookMethod和afterHookMethod從字面意思也能看出是在hook前后的調(diào)用回調(diào)。然后其構(gòu)造函數(shù)有兩個,有一個是帶int類型的,傳入的是一個設(shè)置hook優(yōu)先級的數(shù)字。
[圖片上傳失敗...(image-7e38fd-1696475692002)]
從方法注釋上看,這個priority會影響后面beforeHookMethod和afterHookMethod的調(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)用方法棧過程
}
}
);
日志打印的話自然還是用到XposedBridge的log方法。由于我們需要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里面裝,具體操作如下:
打開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>在菜單界面有兩個關(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>添加應(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>在克隆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>安裝完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>最右邊的那邊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>在彈出來的菜單界面中有兩個項(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>可以看見有一個合規(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>重啟成功的話,底部會有提示語展示
<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、如何檢測
- 先打開我們安裝的測試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>
- 查看日志
回到我們在安裝講解中的第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)。