若没有启用 Magisk 自带 Zygisk,则只要模块带有 Zygisk 库,Magisk 就不会加载它,无论模块是否包括不需要 Zygisk 就可使用的功能(例如 TrickyStore)。这导致 TrickyStore 在未启用 Zygisk 的 Magisk 上完全失效。
暂时没有好办法解决这个问题。我本来想加一个 flag,但 pr 被关闭。下述链接提供的方案可能会破坏 Magisk + ZygiskNext,因为 ZygiskNext 没有办法在 post-fs-data 阶段前获取环境信息,也无法确保模块先于 ZygiskNext 被 Magisk 加载。
https://github.com/topjohnwu/Magisk/pull/8257
暂时没有好办法解决这个问题。我本来想加一个 flag,但 pr 被关闭。下述链接提供的方案可能会破坏 Magisk + ZygiskNext,因为 ZygiskNext 没有办法在 post-fs-data 阶段前获取环境信息,也无法确保模块先于 ZygiskNext 被 Magisk 加载。
https://github.com/topjohnwu/Magisk/pull/8257
GitHub
Make Zygote injection optional for Zygisk modules by aviraxp · Pull Request #8257 · topjohnwu/Magisk
This is useful when a module has Zygisk features, but also has other features which can work without Zygisk, such as TrickyStore.
最近部分用户反馈打不开 Kotak Neo,研究后发现,其实是检测到了 LSPosed。
检测方法弱智且有效:LSPosed 为了确保日志记录成功,会启动一个 watchdog thread,重置一些系统属性,从而确保有足够的日志缓冲区。
问题在于,重置 prop 的条件写的过于宽泛,导致一些并不常见的 prop(如 persist.log.tag.crash 和 persist.log.tag)也被赋值。虽然这些 prop 在正常设备中完全可能存在,但出现的概率很低。因此,若这几个 prop 同时存在,基本可以认定为安装了 LSPosed。
https://github.com/LSPosed/LSPosed/blob/master/daemon%2Fsrc%2Fmain%2Fjni%2Flogcat.cpp#L266-L270
检测方法弱智且有效:LSPosed 为了确保日志记录成功,会启动一个 watchdog thread,重置一些系统属性,从而确保有足够的日志缓冲区。
问题在于,重置 prop 的条件写的过于宽泛,导致一些并不常见的 prop(如 persist.log.tag.crash 和 persist.log.tag)也被赋值。虽然这些 prop 在正常设备中完全可能存在,但出现的概率很低。因此,若这几个 prop 同时存在,基本可以认定为安装了 LSPosed。
https://github.com/LSPosed/LSPosed/blob/master/daemon%2Fsrc%2Fmain%2Fjni%2Flogcat.cpp#L266-L270
GitHub
LSPosed/daemon/src/main/jni/logcat.cpp at master · LSPosed/LSPosed
LSPosed Framework. Contribute to LSPosed/LSPosed development by creating an account on GitHub.
钱庄
最近部分用户反馈打不开 Kotak Neo,研究后发现,其实是检测到了 LSPosed。 检测方法弱智且有效:LSPosed 为了确保日志记录成功,会启动一个 watchdog thread,重置一些系统属性,从而确保有足够的日志缓冲区。 问题在于,重置 prop 的条件写的过于宽泛,导致一些并不常见的 prop(如 persist.log.tag.crash 和 persist.log.tag)也被赋值。虽然这些 prop 在正常设备中完全可能存在,但出现的概率很低。因此,若这几个 prop 同时存在,基本可以认定为安装了…
某些系统错误地 allow untrusted_app system_data_file dir read(如三星官方 ROM 及一些低质量第三方 ROM),导致 HMA 的配置文件夹可以被枚举。Kotak Neo也利用了此检测。
自测方法:在没有 root 的终端中 ls /data/system,若成功则受影响。
自测方法:在没有 root 的终端中 ls /data/system,若成功则受影响。
Forwarded from 5ec1cff (f1rst last)
发现 CFA (非 multiplatform)运行在后台的时候总是会每秒刷出这样的日志:
缓解措施是关闭这个功能。
参考:如何更新前台服务通知
ActivityManager: Foreground service started from background can not have location/camera/microphone access: service com.github.kr328.clash/.service.TunService
一番调查发现问题来源于这里,是通知显示流量统计的功能,这个功能每秒更新一次通知,每次都通过前台服务调用 Service.startForeground
来更新通知,所以当应用在后台调用的时候系统就会出这个 log 。缓解措施是关闭这个功能。
参考:如何更新前台服务通知
昨天给 CMFA 提交了 pr 修复:https://github.com/MetaCubeX/ClashMetaForAndroid/commit/26c2ecef6d7277df6120cbb2392c647e54c76315
GitHub
Avoid starting foreground service repeatedly · MetaCubeX/ClashMetaForAndroid@26c2ece
A rule-based tunnel for Android. Contribute to MetaCubeX/ClashMetaForAndroid development by creating an account on GitHub.
自 Android 14 QPR3 开始,PM#getPackageArchiveInfo API 就无法正常返回 application info flags(永远是0),导致 LSPosed 寄生管理器无法正常打开。目前的 workaround 是手动给解析出的 flag 加上 FLAG_HAS_CODE。
当时我们就怀疑这是个 AOSP bug,最近调查后发现确实如此。近期,旧解析器切换到 PackageParser2 后,getPackageArchiveInfo 方法没有调用 hideAsFinal(),导致 assignDerivedFields2() 没有被执行,因此 appinfo.flags 没有被初始化。
修复也很简单,在解析包时显式调用 hideAsFinal() 即可。这个问题自 QPR3 到 Android 15 正式版均存在。
https://android-review.googlesource.com/c/platform/frameworks/base/+/3255356
当时我们就怀疑这是个 AOSP bug,最近调查后发现确实如此。近期,旧解析器切换到 PackageParser2 后,getPackageArchiveInfo 方法没有调用 hideAsFinal(),导致 assignDerivedFields2() 没有被执行,因此 appinfo.flags 没有被初始化。
修复也很简单,在解析包时显式调用 hideAsFinal() 即可。这个问题自 QPR3 到 Android 15 正式版均存在。
https://android-review.googlesource.com/c/platform/frameworks/base/+/3255356
分享马赫mood的单曲《良药》: http://163cn.tv/w4IsFH1 (来自@网易云音乐)
网易云音乐
良药
歌曲名《良药》,由 马赫mood 演唱,收录于《热土》专辑中
最近研究了小米安全开放服务客户端 SDK 的实现,意外发现小米实际上实现了一套简化版的密钥验证,囊括了从 Trust Application、AIDL HAL、客户端 API 到应用 SDK 的全流程。下面以红米 Note 12 为例,逐项拆解一下:
1)系统启动:由 init 执行 /odm/bin/mrmd,启动名为 vendor.xiaomi.hardware.mrm 的 AIDL 服务,并注册三个名为 IMrm、IMiCertificate、IMrmRemoteAuth 的接口,供客户端使用。服务的具体逻辑在 /vendor/lib64/libmiriskmanager.so。服务启动时会调用高通 QSEECOM API 接口,请求内核向 TEE 加载名为 miriskm 的 TEE 应用。
2)应用发送验证请求:应用调用 sdk 的 init 方法,应用绑定至小米手机管家的 com.xiaomi.security.xsof.SAFETY_DETECT_SERVICE,并传入 APP_ID 和 APP_KEY(应用注册时由小米分配)及挑战码。小米手机管家再次绑定至 MiTrustService 的 com.xiaomi.trustservice.IMiTrustService,IMiTrustService 调用上述 AIDL 服务的 MiRiskManager::getStatusByAuth 接口,开始验证流程。
3)验证:分为两部分,一部分为用户空间的验证,逻辑很简单,无非 prop、selinux、su 文件(注意,验证在 mrmd 进程中完成,这是个原生进程,denylist 只针对 app 进程,因此需要手动对 mrmd umount su);另一部分为 TEE 中验证,这里调用 QSEECOM API 与 TEE 通信,关键代码在 TEE 应用中。我没有仔细梳理逻辑,但可以看出将用户侧数据送进了 TEE,并在 TEE 中获取解锁状态、生成包含验签信息在内的 JSON 后,返回至客户端。
4)验签:可以本地对比 hash,也支持将数据上传至小米服务器远程验签。
整体来看,若证书生成及验签流程正确,这就是个精简版的密钥验证。目前小米仍未强制此项验证,因此可以通过改机型、关闭 framework 端服务等方式绕过此项检测,而真正破解则需要小米颁发的私钥。(像不像 PIF?)目前,第三方应用中仅发现 Hunter 搭载了该 SDK。
题外话:研究时顺便看了下内核中的 TEE 接口,发现 libmiriskmanager 有三种与内核通信的实现:高通机型使用高通提供的 QSEECOM,MTK机型一部分使用 MTK 与豆荚科技联合开发的 TEEi,还有少数机器使用小米自研 MiTEE(从驱动代码看,实际上基于 Linaro 提供的开源实现 OP-TEE 二次开发)。
1)系统启动:由 init 执行 /odm/bin/mrmd,启动名为 vendor.xiaomi.hardware.mrm 的 AIDL 服务,并注册三个名为 IMrm、IMiCertificate、IMrmRemoteAuth 的接口,供客户端使用。服务的具体逻辑在 /vendor/lib64/libmiriskmanager.so。服务启动时会调用高通 QSEECOM API 接口,请求内核向 TEE 加载名为 miriskm 的 TEE 应用。
2)应用发送验证请求:应用调用 sdk 的 init 方法,应用绑定至小米手机管家的 com.xiaomi.security.xsof.SAFETY_DETECT_SERVICE,并传入 APP_ID 和 APP_KEY(应用注册时由小米分配)及挑战码。小米手机管家再次绑定至 MiTrustService 的 com.xiaomi.trustservice.IMiTrustService,IMiTrustService 调用上述 AIDL 服务的 MiRiskManager::getStatusByAuth 接口,开始验证流程。
3)验证:分为两部分,一部分为用户空间的验证,逻辑很简单,无非 prop、selinux、su 文件(注意,验证在 mrmd 进程中完成,这是个原生进程,denylist 只针对 app 进程,因此需要手动对 mrmd umount su);另一部分为 TEE 中验证,这里调用 QSEECOM API 与 TEE 通信,关键代码在 TEE 应用中。我没有仔细梳理逻辑,但可以看出将用户侧数据送进了 TEE,并在 TEE 中获取解锁状态、生成包含验签信息在内的 JSON 后,返回至客户端。
4)验签:可以本地对比 hash,也支持将数据上传至小米服务器远程验签。
整体来看,若证书生成及验签流程正确,这就是个精简版的密钥验证。目前小米仍未强制此项验证,因此可以通过改机型、关闭 framework 端服务等方式绕过此项检测,而真正破解则需要小米颁发的私钥。(像不像 PIF?)目前,第三方应用中仅发现 Hunter 搭载了该 SDK。
题外话:研究时顺便看了下内核中的 TEE 接口,发现 libmiriskmanager 有三种与内核通信的实现:高通机型使用高通提供的 QSEECOM,MTK机型一部分使用 MTK 与豆荚科技联合开发的 TEEi,还有少数机器使用小米自研 MiTEE(从驱动代码看,实际上基于 Linaro 提供的开源实现 OP-TEE 二次开发)。