2023-10-05

编写Xposed模块

了解完Xposed框架的相关知识后,我们还要编写一些模块代码,才能实现我们的监测操作。
首先在gradle里面依赖一下xposed的api:

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

在进行Xposed模块开发之前,我们有必要了解一下Xposed API。完成一个模块的开发至少有两步要做:

1、编写一个java类并实现**IXposedHookLoadPackage**接口,实现**handleLoadPackage**方法进行自定义的监测操作
2、注册这个java类

编写代码

假如我们需要监测的方法是:
[图片上传失败...(image-819ac2-1696475692002)]
那么,我们的初始方法就可以写成这个样子:

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

    }
}

handleLoadPackage中,调用XposedHelpers类的findMethodHook来进行,在写代码的时候,我们发现其实有两个方式可以选用:
[图片上传失败...(image-219c78-1696475692002)]
区别在于第一个方法传入的是class本体,然后源码那边使用的classLoader就是class.getClassLoader;第二种不需要class本体,只需要指定这个class的名字,然后再指定加载这个class的classLoader。从便捷上来说,第一种无疑是便捷的。但是第二种的灵活度比第一种高。假如有一些类是第三方SDK里面的,而这个SDK没在你源码里面,是以插件形式在你app安装完后才加进来的。这时候,你在编码阶段是没有办法得到这个class本体的,所以第二种方法可以看作是能hook运行时的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 + "调用getDeviceId()获取了imei");
                    }
                }
        );

注意到我们最后的那个回调函数XC_methodHook,
[图片上传失败...(image-4ef009-1696475692002)]
首先,这是一个抽象类,不是接口。beforeHookMethodafterHookMethod从字面意思也能看出是在hook前后的调用回调。然后其构造函数有两个,有一个是带int类型的,传入的是一个设置hook优先级的数字。
[图片上传失败...(image-7e38fd-1696475692002)]
从方法注释上看,这个priority会影响后面beforeHookMethodafterHookMethod的调用顺序。优先级越高的Hook,其beforeHook方法会越先执行,然后其afterHook方法会在最后执行。如果存在hook多个方法,且所有的priority都相同,会依次此执行完这个方法的before和after在执行下一个方法的before和after,以此类推。
而采用无参构造的,其priority是一个系统默认值50:
[图片上传失败...(image-1a4b70-1696475692002)]
假如我们Hook了3个方法A,B,C。在priority相同和不同时的调用关系可以参考下图:

[图片上传失败...(image-1d1204-1696475692002)]
知道了上面的原理后,我们就应该选用默认或者相同priority的方式来进行hook。
扯了这么多,大家也别嫌麻烦,工欲善其事,必先利其器。现在再回到之前的代码。我们在beforeHookMethod里面调用了

XposedBridge.log(lpparam.packageName + "调用getDeviceId()获取了imei");

XposedBridge也是rovo89开发的一个Xposed的辅助库,调用其log方法后可以在手机端的Xposed管理器里面显示相关信息,这一步的意思表示我们监测了app调用android.telephony.TelephonyManager这个类的getDeviceId方法

打印方法调用栈

上面的所有操作知识标记了调没调用指定的方法。但是如果调用了,是谁调用的,其实我们时不清楚的。这样其实不利于我们查找问题的根源。回看本文的第一张信通院的图,发现他们检测时,其实给了方法调用栈。那么我们现在就来模拟一下这种操作。
我们需要打印的是整个hook期间的方法栈,那么这个操作就应该放在afterHookMethod里面,于是,我们可以写成这样:

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

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        //在这里写调用方法栈过程
                    }
                }
        );

日志打印的话自然还是用到XposedBridgelog方法。由于我们需要hook的方法不止一个,而我们打印方法调用栈又是一样的操作,于是乎我们可以自己写一个抽象类继承XC_MethodHook,只实现afterMethodHook方法,在里面做统一的方法栈追踪操作。因此,我们先自定义一个DumpMethodHook的类,代码如下:

public abstract class DumpMethodHook extends XC_MethodHook {

    /**
     * 该方法会在Hook了指定方法后调用
     * @param param
     */
    @Override
    protected void afterHookedMethod(MethodHookParam param) {
        //在这里,我们dump一下调用的方法栈信息
        dump2();
    }

    /**
     * dump模式一:根据线程进行过滤
     */
    private static void dump() {
        for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
            Thread thread = (Thread) stackTrace.getKey();
            StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
            // 进行过滤
            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("[方法栈调用]");
                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----------------");
    }
}

通过查询资料,我写了两种方法栈打印的操作。第一种打印得比较细一些,但是实际测试要卡顿一点。第二种就和信通院报告差不多了,而且没有明显卡顿。
写好了自定义的回调,这时我们只需要将前面的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 + "调用getDeviceId()获取了imei");
                    }
                }
        );

需要监测的方法

既然合规这件事情是工信部搞出来的,那么我们自然要看一下当时的这份红头文件——工信部信管函「164号文」
下面是我目前整理出来的需要hook的一些方法:

方法名字 所属包名 作用
getDeviceId android.telephony.TelephonyManager 获取设备号
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 获取系统相关信息字符来拼接deviceId
getLastKnownLocation LocationManager 获取GPS定位信息
requestLocationUpdates LocationManager 位置、时间发生改变的时候获取定位信息

上面的方法信息可能不全,如果大家有更好的意见可以留言。我看网上很多资料是没有对requestLocationUpdates和安卓8的新增方法getImei进行监控的,这里我加了进来。

对Hook的APP进行过滤,设置白名单

一般来讲,你的手机安装的不止一个app。如果用上面的代码去监测,实际会监测你手机上所有的app。这就导致日志会很杂乱,我们其实只关心指定的app。因此我们需要设置一个白名单进行过滤:

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

里面填写的就是你需要监测的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 + "调用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 + "调用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 + "调用getSubscriberId获取了imsi");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                android.telephony.TelephonyManager.class.getName(),
                lpparam.classLoader,
                "getImei",
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "调用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 + "调用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 + "调用getMacAddress()获取了mac地址");
                    }
                }
        );

        XposedHelpers.findAndHookMethod(
                java.net.NetworkInterface.class.getName(),
                lpparam.classLoader,
                "getHardwareAddress",
                new DumpMethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log(lpparam.packageName + "调用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 + "调用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 + "调用getLastKnownLocation获取了GPS地址");
                    }
                }
        );

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

注册模块代码

上面的操作到目前为止也只是在你的安卓项目中添加了一个java类。如何让xposed识别到我们写的代码是个xposed模块呢?这就需要注册一下这个类。
注册分两步操作:
1、在AndroidManifest.xml中编写meta信息

<!--  标志该 apk 为一个 Xposed 模块,供 Xposed 框架识别-->
<meta-data
    android:name="xposedmodule"
    android:value="true" />

<!--模块说明,一般为模块的功能描述-->
<meta-data
    android:name="xposeddescription"
    android:value="这个模块是用来检测用户隐私合规的,在用户未授权同意前,调用接口获取信息属于违规" />

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

在application节点里面加上这三个meta信息。那个说明会最终显示在xposed管理器上面:
[图片上传失败...(image-beb013-1696475692002)]
注意:填写meta信息是标记我们这个apk是个xposed模块的关键,否则xposed installer不会识别。

2、在项目asset文件夹下面新建xposed_init文件
[图片上传失败...(image-d81e84-1696475692002)]
在里面写上我们实现IXposedHookLoadPackage那个类的包名+类名

com.cjs.hegui30.HookTrack

这样我们就写好了自定义的xposed模块。Xposed在加载的时候会从这个文件里面读取需要初始化的类。
至此,我们的所有代码就编写完成了,此时装在手机后,可以在xposed installer里面识别激活了。

其他

源码同时捆绑了一个快速测试的demo和相关的apk文件,demo可以单独编译成apk,记得切换
[图片上传失败...(image-9f72eb-1696475692002)]
[图片上传失败...(image-dae7f9-1696475692002)]


操作手册

一、准备条件

1、编译合规检测的Xposed模块源码

下载源码,修改设置白名单,编译成apk,安装到手机
相关操作参考《安卓端自行实现工信部要求的隐私合规检测一(教你手写Xposed模块代码)》

注意:源码里面包含了各种安装包及demo

2、已经root的手机可以下载Xposed.apk

Xposed在github上面开源,可以自己下载XposedInstaller的源码进行编译,也可以直接下载已经编译好的apk。

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

3、没有root的手机可以下载VirtualXposed.apk

VirtualXposed在github上面有专门的release页面,其作者在0.20.x的版本的时候放弃了对32位应用的支持,理由是谷歌商店未来只允许64位的app上架,不想花更多精力维护32位的开发。如果你的app为32位应用,因此不能用0.20.x及之后版本的VirtualXposed

0.20.3版本(支持64位应用)
0.18.2版本(支持32位应用)
注意:由于0.20.x版本更换了包名,所以它和0.18.x的版本能同时安装

4、下载合规检测测试程序

该项非必须,仅作为合规快速校验

下载地址:合规检测测试程序apk


二、具体操作

这里以对“合规检测测试程序“的校验进行说明。

1、安装

  • 已root的用户
 1. 安装xposed.apk

在root手机上安装xposed框架的操作可能会比较麻烦,详情问问度娘

 2. 安装合规检测xposed模块.apk
 3. 安装合规检测测试程序.apk
  • 未root用户
 1. 先安装virtual-xposed.apk
 2. 后续操作有两种方案:

第一种可以和已root用户一样,直接将合规检测xposed模块.apk和合规检测测试程序.apk安装在手机真机上,然后在virtual-xposed里面克隆这两个app;
第二种的话就是不在真机装,直接在virtual-xposed里面装,具体操作如下:

  1. 打开virtual-xposed,点击底部的菜单按钮,进入到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. 在菜单界面有两个关键点需要注意,一个是顶部的添加应用,一个是底部的重启。后者在激活模块的时候需要使用。这里我们先点击添加应用
    <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. 添加应用界面默认的tab项就是克隆APP,如果使用克隆的方式的话,就要先找到安装的合规检测xposed模块和合规检测测试程序,并勾选,然后选择底部的安装;如果选择的是虚拟机安装,就直接点击右下角的加号按钮,选择本地的apk安装包进行安装。
    <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中的界面,上滑,进入到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控制界面 ,点击进入。大大的绿勾表示我们的xposed框架已经激活。点击左上角三杠
    <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. 在弹出来的菜单界面中有两个项要注意:模块用于安装/卸载xposed模块,日志用于查看合规检测的结果,后面会用到。这里我们点击模块
    <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. 可以看见有一个合规检测的xposed模块,勾上它。勾上后需要重启系统。如果是root用户在真机下操作xposed框架,就需要真机重启。但是我们用的是virtual-xposed,所以只需要重启虚拟机就行了。这时候,返回到2的界面,点击重启
    <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

里面有四个按钮,分别对应四种不同的检测条件,我们以第一种模拟获取IMSI来说明,先别点击任何按钮
<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步界面,里面有个日志选项,点击它。该界面右上角有保存按钮,还有个三个点的菜单按钮,点击菜单按钮后有两个项需要注意:

  • 立即清理日志会清空当前界面的所有日志,我们先点击清空一下
  • 重新载入会刷新最新的日志进界面。因为日志的显示不是自动的,要想看到最新的结果,就要手动刷新。
    <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界面,点击模拟获取IMSI,然后再返回到日志界面,并且重新载入,接着就能看见如下的日志记录:
[图片上传失败...(image-19438-1696475692002)]

上面的截图主要针对virtual-xposed来讲的。对于xposed而言,操作大同小异。最大的区别就是xposed需要真实地重启手机,这点的话要麻烦一点。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容