前言
App 合规检测工具简单实现与使用
设备的个人信息(例如:联系人信息、短信内容等)属于用户隐私,违规获取个人信息被视为侵犯用户权益;在设备上获取个人信息一般需先获取到相应的权限才能进行获取,例如想获取相册图片,那么需要申请读权限,用户同意权限申请之后可读取相册图片。
但某些应用存在违规获取个人信息的行为,何为违规行为?我认为可能存在下述特征的行为:
- 在用户不知情下获取(在用户同意隐私政策前获取个人信息?)
- 未明确使用意图的(获取到的信息使用去向描述清楚了?)
- 超范围获取(比如壁纸App可能去申请定位权限、蓝牙权限?)
此前,可能存在很大一部分的应用对用户的个人信息权益保护不是很到位,个人信息违规获取的行为没有得到有效得解决。直到2019 年低,工信部发布关于印发《App违法违规收集使用个人信息行为认定方法》的通知,市场上应用违规获取的行为得到有效的抑制。
关于此通知大概内容是这样:
- 未公开收集使用规则的行为
- 未明示收集使用个人信息的目的、方式和范围的行为
- 未经用户同意收集个人信息的行为【该点为本文讨论和解决的重点】
- 收集与其提供服务无关的个人信息的行为
- 未经同意向他人提供个人信息的行为
- 未按法律规定提供删除或更正个人信息功能的
除此之外,国内游戏渠道(比如 OPPO)在应用提审过程中还有其他要求,比如:
- 不允许频繁获取设备信息(IMEI、SSID、AndroidID 等)
检测 OPPO 联运包获取设备信息,你会发现 OPPO 联运游戏 SDK 就是在不断获取 AndroidID,频率还不低 😓💦。
下面开始讲本文的知识点,围绕如何检测应用是否存在未经用户同意收集个人信息或设备信息的行为,以及检测是否存在频繁获取个人信息或设备信息的行为。
主要是讲述我对检测工具的使用历程。
以下说的个人信息获取和设备信息获取,你可以看作是同一个东西。当然实际上是有区别的。
检测模块编写
介绍 🍵
该模板编写主要是 Hook 知识点,Hook 作为‘钩子’可以粗略理解为拦截某段代码的执行,并在代码段前后插入监控事件,程序执行此代码段同时也将触发监控事件,将监控事件返回给外层用于进一步处理。个人理解,关于 Hook 的详细自行了解。
使用 Hook 较为关键的一点就是找到较为准确的 Hook 点,真是关键之一。
Hook 框架 🤔
不自己写 Hook?能力真的有限,还是找个框架吧;以后轮子还是要造的,万一造的轮子跟别人的比略有独特之处呢?好比梦想还是要有的,万一实现了呢?
Android 上的 Hook 框架,想必很多人都略有耳闻,那就是当年狂热的 Xposted。所以接下来编写的检测工具模板就是基于 Xposted 库实现。
Xposted 模块编写教程在网上是满天飞,这里就引用一篇文章: 新手不要再被误导!这是一篇最新的Xposed模块编写教程
看完上面引用的文章,我们知道此 xposted 库使用的 hook 模板,那么我们后面写的设备信息获取检测也是使用该模板,不同之处就是 hook 点,模板如下:
XposedHelpers.findAndHookMethod(
className,//需要 hook 的类名,因为使用反射,需要全类路径
methondName,//需要 hook 的方法名
params1.class,//hook 方法的参数,参数可能是多个,因此是一个变长参数;参数类型顺序需和原方法保持一致(方法重载嘛)
params2.class,
...
new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//hook before
}
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//hook after
}});
举个栗子🌰
在 Android 中获取 AndroidID 的代码通常是:
Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ANDROID_ID);
- className:android.provider.Settings
- methodName:getString 【带参方法,注意第二个参数可根据不同的值获取不同的信息】
- params1:ContentResolver.class
- params2:String.class
那么,根据模板就可以写出第一个 xposted hook 方法,打包出来就是一个 xposted 模板。
// 检测 AndroidID 获取
XposedHelpers.findAndHookMethod(
"android.provider.Settings",
"getString",
ContentResolver.class,
String.class,
new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//hook before
}
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//hook after
if(param == null)return;
//方法参数是一个可变参数,也就是一个列表
Object[] args = param.args;
if(args == null || args.lenght <= 0)return;
//getString 方法我们需要知道第二个参数值才有意义,才能继续判断是获取 androidid 还是别的信息
if(args.lenght >= 2){
String params2 = (String)args[1];
//public static final String ANDROID_ID = "android_id";
if("android_id".equal(params2)){
Log.d("TAG","检测到获取 androidid")
}
}
}});
就这样,检测 androidid 的获取就写好了,简单吧😄这仅仅是使用,如果你想知道 xposted 是如何实现的那你要去看源码:android-hacker-VirtualXposted
看 android 源码,获取 androidid getString 方法第二个参数可选值:
//所以,当你 Hook 这个方法时候,不仅仅可以 Hook AndroidID
//public static final String ANDROID_ID = "android_id";
static {
MOVED_TO_SECURE = new HashSet<>(30);
MOVED_TO_SECURE.add(Secure.ADAPTIVE_SLEEP);
MOVED_TO_SECURE.add(Secure.ANDROID_ID);
MOVED_TO_SECURE.add(Secure.HTTP_PROXY);
MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED);
MOVED_TO_SECURE.add(Secure.LOCK_BIOMETRIC_WEAK_FLAGS);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_ENABLED);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_VISIBLE);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
MOVED_TO_SECURE.add(Secure.LOGGING_ID);
MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_ENABLED);
MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_LAST_UPDATE);
MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_REDIRECT_URL);
MOVED_TO_SECURE.add(Secure.SETTINGS_CLASSNAME);
MOVED_TO_SECURE.add(Secure.USE_GOOGLE_MAIL);
MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
MOVED_TO_SECURE.add(Secure.WIFI_NUM_OPEN_NETWORKS_KEPT);
MOVED_TO_SECURE.add(Secure.WIFI_ON);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_AP_COUNT);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_MAX_AP_CHECKS);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_ON);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_COUNT);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_DELAY_MS);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS);
// At one time in System, then Global, but now back in Secure
MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS);
}
现成的检测模板? 🤔️
⚠️自己写的合规检测模板还在优化,优化完成后一定上传。
读到这里,根据上面的 xposted hook 模板,你已经具备了写合规检测模板的能力了,学废了,学废了,真的学会了。
比如合规检测模板应该 hook 哪些呢?个人看法大概列举几点,但不局限于以下几点:
- androidID 检测
- IMEI 检测
- SSID 检测
- IMSI 检测
- MAC 检测
- OAID 检测【因为获取 IMEI 的问题,现在国内很多都在使用 OAID】
- 定位信息读取检测
- 联系人列表读取检测
- 短信内容检测
- 相册读取检测
- 软件安装列表读取检测
- 应用信息读取检测
编写合规检测模板需要考虑的问题可能有哪些?
- 多进程环境,避免某些操作重复执行【判断是否主进程,只在主进程执行初始化等】
- 检测结果数据处理【引入数据持久化框架,比如 GreenDao】
比如我们应该快速知道 androidId 获取了多少次?都是在哪里调用获取的,是第三方sdk还是自己的代码?
- Android 平台上的数据库框架使用初始化似乎都依赖 context 初始化【那么我们要先 hook application attach 或 onCreate 方法拿到 Context】
- 为什么要检测结果统计和处理?
我的答案是为了更快、更直观地知道检测结果
问题是过程中发现的,我的问题不一定是你的问题,根据实际情况而定,随机应变。
到这里,假设你已经写好了检,接下来要介绍如何使用该模板以及遇到的问题。
Use VirtualXposted 💔
方案可行,不推荐,存在较大的局限
为什么不推荐?有什么局限?
不能检测存在 xposte 防护的应用。
xposted hook 特征明显,容易被检测,比如你的应用中接入的某个第三方 SDK 有做 xposted 防护,运行过程中检测到环境存在 xposted,可能会做相应处理,禁止程序继续正常运行,不利于检测应用个人信息获取情况。
如何使用
1、下载安装 virtualXposted
2、激活 xposted
3、添加模板和待检测应用
4、启动模块
最后就是运行应用,查看你模块中输出的日志数据。
因为存在 xposted 防护的局限,不能满足当前需求,另寻他法所以了解到‘太极’。
Use Taichi 💔
方案不可行,不推荐,写好的模板无法使用
为什么不推荐?
写好的检测模板被限制使用。
GitHub 官网:github.com/taichi-fram…
如何使用太极就不过多解释了,这里说下遇到的问题。
问题一:暂不支持
写好的模块不能使用,靓仔一时语塞😓
看下官网有对‘暂时不支持’的描述,有以下两种可能:
- 当前太极是太极阴模式(有阴、阳两种模式,自行官网了解)
- 当前模块不存在白名单中
暂时没办法,试试把太极激活为‘太极阳’模式,然而呢还是现实暂不支持😓
那估计是最后一种可能了,模板不存在与白名单中,要等级到白名单是要联系作者,太麻烦了,寻找其他方法吧,所以就有了下面的另一个框架介绍。(如何开启太极阳,官网有教程,需要借助 Magisk,下文会用到这个工具)
Use Lsposted ❤️
方案可行,推荐使用
为什么推荐?
LSP 侵入性弱,不易被检测到,一定程度上能够避免 xposted 防护。
⚠️注意事项(必读)
1、BootLoader 解锁将清除设备数据,如有需要请注意数据备份。
2、此操作需要设备解锁 Bootloader,类似于刷机。若BootLoader 解锁失败,其他操作已无意义,没有继续操作的必要,建议更换设备。
3、各厂商设备 BootLoader 解锁方式可能不尽相同。
3.1 小米官网仍然开放 BL 解锁工具,建议首选小米手机
3.2 购买第三方解锁工具 UAndroid
3.3 OPPO 官方也有‘深度测试’解锁工具,但申请名额有限,审核时间长,不推荐
如何使用
以小米手机为例,因为 BL 解锁方便。
1、设备 Bootloader 解锁
小米的 BL 解锁对设备的以及账号绑定也有要求,比如账号与设备绑定时间等。官网下载得到的可能不是最新版本,请使用最新版本。
2、下载安装 Magisk
3、下载备用 adb-fastboot
链接:mrzzoxo.lanzouw.com/iMbPYz63p6f
4、准备对应设备型号的刷机包
小米刷机包:mirom.ezbox.idv.tw/phone/
一般建议下载大陆稳定版(若是其他厂商刷机包,自行寻找)
5、刷入 Magisk
尽量按顺序操作
-
从刷机包中解压获取 boot.img 文件(若无此文件建议更换刷机包,也可以另寻他法从刷机包中解析获得此文件)
-
打开 Magisk 选择 boot.img 安装补丁,等待刷入完成(此截图是已安装完成的,以实际为准)
- 刷入完成,查看日志获得生成的 img 文件
- 复制生成的 img 文件和 boot.img 文件到 adb-fastboot 目录下,打开 打开CMD命令行.bat(若 boot.img 不存在此目录下,刷入 magisk 无效)
-
设备进入 BootLoader 模式
- 方式一:执行 adb reboot bootloader
- 方式二:关机状态下,同时按住 音量— + 电源键
- 执行以下命令等待完成,使用命令重启设备;重启后重新打开 magisk 显示版本号表示成功。
6、刷入 LSPosted
- 开启 Zygisk,按提示重启设备
- 开启 Zygisk,按提示重启设备
- 下载LSPosted-zygisk备用(必选)
如果设备支持 Zygisk,ritu、LSPosted-ritu 模块可不需要,建议使用支持 Zygisk 的设备
暂时无法在飞书文档外展示此内容
- 打开 Magisk 开始刷入必选模块,按照提示重启设备后显示 Zygisk 是,桌面出现 LSPosted
6、安装检测模块
如果之前没有安装我们的 合规检测模块,那么现在要安装啦!!!
启动模块,勾选要检测的应用,返回桌面执行应用就可以看日志
其他
- ⚠️ Android 9 及其以下设备才支持 IMEI 获取,在测试设备选择上需注意
- 学习网站:XDA Forums
该网站的主要讨论手机系统,并提供相关设备的技术信息、ROM升级、技术支持、Q&A
可提供 UAndroid 工具,用于各厂商设备 BootLoader 解锁
付费工具、也可以账号租用方式使用
(专业搞机,某宝也有相关技术服务)