5ec1cff
JS 中向表单填写文件的方法 https://stackoverflow.com/a/56447852 let list = new DataTransfer(); let file = new File(["content"], "filename.jpg"); list.items.add(file); input.files = list.files;
如此一来就可以利用 chrome 扩展实现提交任意图片给 saucenao 搜索了(包括来自 file url 的图片,因为图片数据直接上传而不是发送 url )
思路大致如下:
1. 注册 contextMenu ,接收图片 url
2. contextMenu 回调打开一个 tab ,url 是选择的图片
3. 在 tab 中执行 content noscript ,利用 canvas 得到图片数据,利用 DataTransfer 将数据转换为文件,并通过 js 创建表单提交给 saucenao
下面是使用 MV3 的实现:https://gist.github.com/5ec1cff/5f08e79e86edff5b0061f685d8042233
思路大致如下:
1. 注册 contextMenu ,接收图片 url
2. contextMenu 回调打开一个 tab ,url 是选择的图片
3. 在 tab 中执行 content noscript ,利用 canvas 得到图片数据,利用 DataTransfer 将数据转换为文件,并通过 js 创建表单提交给 saucenao
下面是使用 MV3 的实现:https://gist.github.com/5ec1cff/5f08e79e86edff5b0061f685d8042233
Gist
Yet Another Saucenao Search Extension
Yet Another Saucenao Search Extension. GitHub Gist: instantly share code, notes, and snippets.
5ec1cff
如此一来就可以利用 chrome 扩展实现提交任意图片给 saucenao 搜索了(包括来自 file url 的图片,因为图片数据直接上传而不是发送 url ) 思路大致如下: 1. 注册 contextMenu ,接收图片 url 2. contextMenu 回调打开一个 tab ,url 是选择的图片 3. 在 tab 中执行 content noscript ,利用 canvas 得到图片数据,利用 DataTransfer 将数据转换为文件,并通过 js 创建表单提交给 saucenao 下面是使用…
之所以这么绕有几个原因:
1. extension 似乎没有标准的 API 可以实现「打开一个页面,自动上传文件,提交表单」的操作,所以只能在某个网页中用 js 操作 DOM 模拟这个动作
2. 也没有任何标准 API 能实现直接获取 img 的(原始)二进制内容,或者得到 file 对象,用 canvas 可以得到图像数据,但是会有跨域问题,所以选择打开图像 url 对应的页面,注入 content noscript ,避免了跨域
3. 其实提交 url 给 saucenao 完全没问题,大部分情况下 url 都是可用的(比 google images 好),因此这个 extension 的意义主要就在于能实现来自 file url 的图片的自动上传
1. extension 似乎没有标准的 API 可以实现「打开一个页面,自动上传文件,提交表单」的操作,所以只能在某个网页中用 js 操作 DOM 模拟这个动作
2. 也没有任何标准 API 能实现直接获取 img 的(原始)二进制内容,或者得到 file 对象,用 canvas 可以得到图像数据,但是会有跨域问题,所以选择打开图像 url 对应的页面,注入 content noscript ,避免了跨域
3. 其实提交 url 给 saucenao 完全没问题,大部分情况下 url 都是可用的(比 google images 好),因此这个 extension 的意义主要就在于能实现来自 file url 的图片的自动上传
尝试给 zygisksu 的 release 版加上 debuginfo ,以便调试
https://doc.rust-lang.org/cargo/reference/profiles.html
在 cargo.toml 中配置 release 的 profile :
https://doc.rust-lang.org/cargo/reference/profiles.html
在 cargo.toml 中配置 release 的 profile :
[profile.release]build:
# strip = true
opt-level = "z"
lto = true
debug = true
cargo ndk --platform 29 --bindgen -t x86_64 build --profile release
rustc 支持生成 split-debuginfo ,不过剥离出来的不知道怎么用,gdb 不识别https://github.com/firmianay/Life-long-Learner/blob/master/Android-Security-Internals/README.md#%E5%86%85%E6%A0%B8%E5%B1%82%E7%9A%84%E6%9D%83%E9%99%90%E6%89%A7%E8%A1%8C
以前一直好奇 android 是怎么根据有 gid 限制创建 socket 的权限的,现在才知道居然是魔改内核
内核选项:CONFIG_ANDROID_PARANOID_NETWORK
在我的 API 30 设备上内核还开启了这个选项,不过在 AVD 的 API 30 (5.4), API 31 (5.10) 以及之后的版本都没有这个选项了,可能是使用了别的实现
以前一直好奇 android 是怎么根据有 gid 限制创建 socket 的权限的,现在才知道居然是魔改内核
内核选项:CONFIG_ANDROID_PARANOID_NETWORK
在我的 API 30 设备上内核还开启了这个选项,不过在 AVD 的 API 30 (5.4), API 31 (5.10) 以及之后的版本都没有这个选项了,可能是使用了别的实现
GitHub
Life-long-Learner/Android-Security-Internals/README.md at master · firmianay/Life-long-Learner
Personal Notes About Everything. Contribute to firmianay/Life-long-Learner development by creating an account on GitHub.
5ec1cff
https://github.com/firmianay/Life-long-Learner/blob/master/Android-Security-Internals/README.md#%E5%86%85%E6%A0%B8%E5%B1%82%E7%9A%84%E6%9D%83%E9%99%90%E6%89%A7%E8%A1%8C 以前一直好奇 android 是怎么根据有 gid 限制创建 socket 的权限的,现在才知道居然是魔改内核 内核选项:CONFIG_ANDROID_PARANOID_NETWORK…
现在是使用 bpf 程序
packages/modules/Connectivity/bpf_progs/netd.c
首次提交 于 2019 年
这个程序不再通过 groups 判断是否具有 网络权限 ,而是从 bpf 程序定义的 map (uid_permission_map) 中查找目标进程 uid (非 gid 或 groups)的权限记录,如果 map 有相应 uid 的记录,且记录中没有 网络权限,才会拒绝创建 socket 。
因此现在 gids 有 inet 也不一定能创建 socket ,而不存在于 pm 的 uid 反而具有 网络权限 。
你可以在
map 由 系统服务 或 netd 负责更新,相关代码:
packages/modules/Connectivity/service/src/com/android/server/BpfNetMaps.java
cgroupsock/inet/create 控制创建 socket 的权限packages/modules/Connectivity/bpf_progs/netd.c
首次提交 于 2019 年
这个程序不再通过 groups 判断是否具有 网络权限 ,而是从 bpf 程序定义的 map (uid_permission_map) 中查找目标进程 uid (非 gid 或 groups)的权限记录,如果 map 有相应 uid 的记录,且记录中没有 网络权限,才会拒绝创建 socket 。
因此现在 gids 有 inet 也不一定能创建 socket ,而不存在于 pm 的 uid 反而具有 网络权限 。
你可以在
/sys/fs/bpf/netd_shared/map_netd_uid_permission_map 看到这个 mapmap 由 系统服务 或 netd 负责更新,相关代码:
packages/modules/Connectivity/service/src/com/android/server/BpfNetMaps.java
setNetPermForUids
packages/modules/Connectivity/service/native/TrafficController.cpp TrafficController::setPermissionForUidsForwarded from 【NSFW️?】陰キャネット
This media is not supported in your browser
VIEW IN TELEGRAM
google 搜索框的傻逼 设计:如果某个关键词联想出了右边的卡片,鼠标刚好悬停在那的时候会自动把那个关键词填上去
有时候还会出现这种情况:没法删除文字,因为删除文字后,如果剩余文字还能得到卡片,那么马上又会触发 mouseover 事件把卡片的关键词填上去
一个简单的 UserScript 解决这个问题帮 Google 擦屁股 :
有时候还会出现这种情况:没法删除文字,因为删除文字后,如果剩余文字还能得到卡片,那么马上又会触发 mouseover 事件把卡片的关键词填上去
一个简单的 UserScript 解决这个问题
// ==UserScript==
// @name Fuckgoogle
// @namespace Violentmonkey Scripts
// @match https://www.google.com/search
// @grant unsafeWindow
// @version 1.0
// @author 5ec1cff
// @denoscription 2023/6/16 12:44:17
// ==/UserScript==
((window) => {
let form = null;
window.document.documentElement.addEventListener("mouseover", function (e) {
const te = e.toElement;
if (form == null) form = window.document.querySelector("form[role=search]");
if (form != null && form.contains(te)) e.stopPropagation();
}, true)
})(unsafeWindow)
ZygiskOnKernelSU 一直以来都存在着一个奇怪的 bug ,至今已经收到无数起汇报(可能最早的一例)。这些汇报的共同特征是:都来自 oplus 或者 coloros 系统;都出现了 zygiskd32 崩溃的现象;崩溃的堆栈基本上都来自 malloc ;都只有 release 构建出现问题。
早期的分析发现,崩溃的线程是处理 daemon socket 启动的线程,其中一个命令是根据不同 root 实现去查询某 uid 是否具有 root 权限,而问题似乎来自这里,因为每次执行完这个命令后,就会发生崩溃。当时一个看似可行的修复方法是将相关代码禁止内联。
而近期 root profile 推出后,zygisksu 也做出了相应修改,在这个提交中,never inline 被去掉,于是又出现了上述问题的汇报。
经过讨论 [1] ,怀疑是内核的 bool size 和 rust 中的 bool size 不一致的问题,rust 的 bool size 是 1,如果内核的 bool size 是 4,可能导致内存被破坏。而利用 prctl 查询 root 权限,需要传入一个 bool 指针,由内核改写返回值,问题可能就在这里。因此随后的一个 CI 进行了修改,并且有人汇报此 CI 的 release 版本正常 [2] 。
于是主线也做了类似的修正,不过没有用到上述 CI 的提交。但这个修正的 CI 的 release 仍有人汇报仍然出现崩溃。
实际上上述猜测是有问题的,因为随后的实验又发现,内核的 bool size 为 1 ,看起来和 rust 是一致的。后来又有人提出 copy_to_user 在长度小于 16 的时候可能出现覆盖现象。总之,问题看上去就是某种意外的内存覆写导致的。
然而想要解释问题的具体原因难度很大,因为 release 构建缺少调试信息,而且崩溃的堆栈回溯大部分帧都来自 libc ,难以从 zygiskd 自身的代码发现问题。不论如何,我还是发了一个带有 debug info 的 release 版本,等待有问题的用户参与调查。
就在最近有 ColorOS 用户积极参与了问题调查,真相似乎终于揭开。
为了验证 bool size 的问题,我给那个用户发了一个程序,检测 prctl 是否会覆写,结论是没有错误覆写 [3]。
虽然 bool 没有问题,但是种种迹象表明,应该就是这个 prctl 调用出现的问题,而且一定产生了意外的内存覆写。于是我看了看 kernelsu 的源码,发现了蹊跷之处。
在执行 CMD_UID_GRANTED_ROOT 的时候,kernelsu 会将 prctl 的 arg5 当作一个用户指针,尝试写入一个 reply_ok 返回值。这个返回值区别于 arg4 ,arg4 是命令的 bool 返回值(即 uid 是否具有 root)。
而 zygisksu 中忽略了 arg5 ,且调用 prctl 仅仅传了 4 个参数,因此可以在 dmesg 看到大量的 reply err ,因为 arg5 的地址非法。
不过调用的时候不写 arg5 ,不代表它就没有值了,32 位 arm 的调用约定是 r0~r3 作为前四个参数,之后的参数由调用者放到栈上。现在调用者没有把 arg5 放到栈上,因此取到的值就不是我们能控制的了,这种随机性也和问题报告中,崩溃点各不相同的现象相对应。
将 prctl 的参数列表补全后,release 版本终于能够正常使用。
虽然没有让参与调查者在自己的设备上跟踪有问题版本 zygiskd 的系统调用,不过我在自己的设备上测试,发现 32 位的 prctl 调用的第五个参数确实是一个合法地址,甚至是 libc malloc 内存区域的地址。当 zygiskd32 调用了 uid_granted_root 后,KernelSU 也没有打出 reply err 的日志,说明这个地址确实被覆写了,至于为什么没有崩溃,以我目前的能力尚不能解释,只能当作玄学了。可想而知,有多少看似能正常运行着 zygisksu 的系统,他们的 zygiskd32 的内存其实已经被破坏了。
可以说,zygisksu 到这个问题发现为止一直运行在 bug 上。
zygisksu 的教训告诉我们:调用 prctl 这样的函数时一定要将不用的参数填上 0 ,否则可能出现意想不到的未定义行为。
另外,从 arm64 的系统调用跟踪发现,arg5 是 0 ,也许是因为 64 位上这个参数来自寄存器。
早期的分析发现,崩溃的线程是处理 daemon socket 启动的线程,其中一个命令是根据不同 root 实现去查询某 uid 是否具有 root 权限,而问题似乎来自这里,因为每次执行完这个命令后,就会发生崩溃。当时一个看似可行的修复方法是将相关代码禁止内联。
而近期 root profile 推出后,zygisksu 也做出了相应修改,在这个提交中,never inline 被去掉,于是又出现了上述问题的汇报。
经过讨论 [1] ,怀疑是内核的 bool size 和 rust 中的 bool size 不一致的问题,rust 的 bool size 是 1,如果内核的 bool size 是 4,可能导致内存被破坏。而利用 prctl 查询 root 权限,需要传入一个 bool 指针,由内核改写返回值,问题可能就在这里。因此随后的一个 CI 进行了修改,并且有人汇报此 CI 的 release 版本正常 [2] 。
于是主线也做了类似的修正,不过没有用到上述 CI 的提交。但这个修正的 CI 的 release 仍有人汇报仍然出现崩溃。
实际上上述猜测是有问题的,因为随后的实验又发现,内核的 bool size 为 1 ,看起来和 rust 是一致的。后来又有人提出 copy_to_user 在长度小于 16 的时候可能出现覆盖现象。总之,问题看上去就是某种意外的内存覆写导致的。
然而想要解释问题的具体原因难度很大,因为 release 构建缺少调试信息,而且崩溃的堆栈回溯大部分帧都来自 libc ,难以从 zygiskd 自身的代码发现问题。不论如何,我还是发了一个带有 debug info 的 release 版本,等待有问题的用户参与调查。
就在最近有 ColorOS 用户积极参与了问题调查,真相似乎终于揭开。
为了验证 bool size 的问题,我给那个用户发了一个程序,检测 prctl 是否会覆写,结论是没有错误覆写 [3]。
虽然 bool 没有问题,但是种种迹象表明,应该就是这个 prctl 调用出现的问题,而且一定产生了意外的内存覆写。于是我看了看 kernelsu 的源码,发现了蹊跷之处。
在执行 CMD_UID_GRANTED_ROOT 的时候,kernelsu 会将 prctl 的 arg5 当作一个用户指针,尝试写入一个 reply_ok 返回值。这个返回值区别于 arg4 ,arg4 是命令的 bool 返回值(即 uid 是否具有 root)。
而 zygisksu 中忽略了 arg5 ,且调用 prctl 仅仅传了 4 个参数,因此可以在 dmesg 看到大量的 reply err ,因为 arg5 的地址非法。
不过调用的时候不写 arg5 ,不代表它就没有值了,32 位 arm 的调用约定是 r0~r3 作为前四个参数,之后的参数由调用者放到栈上。现在调用者没有把 arg5 放到栈上,因此取到的值就不是我们能控制的了,这种随机性也和问题报告中,崩溃点各不相同的现象相对应。
将 prctl 的参数列表补全后,release 版本终于能够正常使用。
虽然没有让参与调查者在自己的设备上跟踪有问题版本 zygiskd 的系统调用,不过我在自己的设备上测试,发现 32 位的 prctl 调用的第五个参数确实是一个合法地址,甚至是 libc malloc 内存区域的地址。当 zygiskd32 调用了 uid_granted_root 后,KernelSU 也没有打出 reply err 的日志,说明这个地址确实被覆写了,至于为什么没有崩溃,以我目前的能力尚不能解释,只能当作玄学了。可想而知,有多少看似能正常运行着 zygisksu 的系统,他们的 zygiskd32 的内存其实已经被破坏了。
zygisksu 的教训告诉我们:调用 prctl 这样的函数时一定要将不用的参数填上 0 ,否则可能出现意想不到的未定义行为。
另外,从 arm64 的系统调用跟踪发现,arg5 是 0 ,也许是因为 64 位上这个参数来自寄存器。
GitHub
一加ace pro安装后提示崩溃 · Issue #13 · Dr-TSNG/ZygiskNext
在Kernel Su管理器中提示:zygisk has crashed 我应该怎样获取并提供错误日志?
❤2
KernelSU 出现 app profile 后关于 ksu、zygisksu 和 Shamiko 的一些说明:
1. 对于非 root app,app profile 有「卸载模块」的开关,可以选择跟随默认配置或单独给某个 app 自定义。默认配置由设置中的「默认卸载模块」开关影响。
2. 如果有 zygisk 模块请求 umount ,zygisksu 会给需要卸载模块的进程做 umount ;内核版本 > 5.9 时候,ksu 也会给中需要卸载模块的进程在内核做 umount 。
3. Shamiko 会请求 zygisksu 做 umount ,并给需要卸载模块的进程做隐藏处理。因此,App profile 的「卸载模块」等效于让 Shamiko 隐藏。
4. Shamiko 在 ksu 上的黑白名单开关等效于 ksu 的「默认卸载模块」开关。
4.1. 白名单就是打开「默认卸载模块」开关(这个开关 ksu 默认打开)。只有被关掉「卸载模块」的 app 才不做隐藏。
4.2. 黑名单就是关闭「默认卸载模块」开关,只有被打开「卸载模块」的 app 才做隐藏。
5. 如果有挂载系统 App 的模块,应该取消相应系统 App 的卸载模块确保正常使用;如果桌面图标出现异常,也要取消对桌面的卸载。当然,你也可以尝试把系统 App 安装到 /data (即在系统中安装相应 apk)。
6. zygisksu 的 umount 只会处理和 ksu 相关的挂载点,如果使用第三方模块在全局挂载了其他内容,不保证能隐藏。
1. 对于非 root app,app profile 有「卸载模块」的开关,可以选择跟随默认配置或单独给某个 app 自定义。默认配置由设置中的「默认卸载模块」开关影响。
2. 如果有 zygisk 模块请求 umount ,zygisksu 会给需要卸载模块的进程做 umount ;内核版本 > 5.9 时候,ksu 也会给中需要卸载模块的进程在内核做 umount 。
3. Shamiko 会请求 zygisksu 做 umount ,并给需要卸载模块的进程做隐藏处理。因此,App profile 的「卸载模块」等效于让 Shamiko 隐藏。
4. Shamiko 在 ksu 上的黑白名单开关等效于 ksu 的「默认卸载模块」开关。
/data/adb/shamiko/whitelist 会被忽略。4.1. 白名单就是打开「默认卸载模块」开关(这个开关 ksu 默认打开)。只有被关掉「卸载模块」的 app 才不做隐藏。
4.2. 黑名单就是关闭「默认卸载模块」开关,只有被打开「卸载模块」的 app 才做隐藏。
5. 如果有挂载系统 App 的模块,应该取消相应系统 App 的卸载模块确保正常使用;如果桌面图标出现异常,也要取消对桌面的卸载。当然,你也可以尝试把系统 App 安装到 /data (即在系统中安装相应 apk)。
6. zygisksu 的 umount 只会处理和 ksu 相关的挂载点,如果使用第三方模块在全局挂载了其他内容,不保证能隐藏。
👍9😁1
5ec1cff
这样的问题是没法访问 /Android/data ,有时候经常需要访问 QQ 下载的文件,普通用户的权限很难做到透过文件系统访问。 在 root 的情况下,虽然可以使用 root 启动 webdav ,不过为了确保最小权限,想了一个比较绕的方法:先 su ,通过 capsh 设置 groups 为 sdcard_rw(1015) 再降权回去。 su -c "PATH=$PATH $(which capsh) --groups=1015,9997,3003 --uid=$(id -u) -- -c 'exec…
论升级依赖的重要性:
androidx navigation 2.5.3 的 deeplink 匹配是坏的。
某个 destination 有两个 deeplink:
1.
在 2.5.3 版本中,如果 action 匹配。则会在多个 deep link 中比较,并且 exact deep link 总是被采用,即使没有匹配到任何参数。
因此如果启动 url 1 并且带 action ,则 2 会被匹配,进而导致一系列问题。
升级到 2.6.0 已经修复,修复的方法是要求必选参数被正确匹配才进行比较:
commit
相关代码
上面这些就是自己调查了半天的结果,最后发现一开始就升级就好了,纯属浪费时间, 所以出现问题的时候,第一时间升级依赖可能可以快速解决问题。
androidx navigation 2.5.3 的 deeplink 匹配是坏的。
某个 destination 有两个 deeplink:
1.
tieba.baidu.com/p/{tid}?pid={pid}
2. com.baidu.tieba://unidispatch/pb?tid={tid}
前者的 path 包含参数,不属于 exact deep link ,后者则属于 exact deep link 。在 2.5.3 版本中,如果 action 匹配。则会在多个 deep link 中比较,并且 exact deep link 总是被采用,即使没有匹配到任何参数。
因此如果启动 url 1 并且带 action ,则 2 会被匹配,进而导致一系列问题。
升级到 2.6.0 已经修复,修复的方法是要求必选参数被正确匹配才进行比较:
commit
相关代码
Forwarded from 5ec1cff
知名开源跨平台截图工具 ShareX 宣布增加截图次数限制
- 新用户每天 30 次
- 普通用户每天 60 次
- 高级版用户每天 600 次
高级版在此获取:https://getsharex.com/premiumget rickrolled LOL
来源
- 新用户每天 30 次
- 普通用户每天 60 次
- 高级版用户每天 600 次
高级版在此获取:https://getsharex.com/premium
😁6👎2
发现家里某品牌智能电视的漏洞……
第一步:尝试打开 adb ,在设置的设备信息页面按「上上下下左右左右」(コナミコマンド) 即可进入工程菜单(图1),可以打开无线 adb
第二步:通过无线 adb 连接,发现系统是 Android 10 ,getenforce 返回 permissive ,看来可以利用 magica 提权了。
第三步:使用修改后的 magica (移除了安装 magisk 环节)提权(图2),启动 telnet ,得到 root shell ,但这时候只能任意 setuid ,并没有完全的 cap (图3)
第四步:利用 root uid 可以修改属性,尝试 magisk resetprop ,但未成功。考虑到属性文件可以直接写,于是干脆直接 patch 二进制
然后放回设备,覆盖原来的文件,这里保险起见,先移动原先的 prop 文件再把新的文件放入。
随后在部分提权的 root shell 中 重启 adbd ,然后 adb root ,即得完全特权的 root shell (图5)。由于没有 SELinux ,修改 ro.debuggable 就能使 adbd 转换到 root 了。
上面的操作可能会对属性服务造成一定的影响,可以在操作完成后把原先备份的文件还原回去。
要利用上述漏洞,只要能安装任意应用即可,因此还是很危险的。
感觉国内不少 Android TV 在 SELinux 方面都很摆烂,我以前也用类似的方法 pwn 了家里另一台没有 SELinux 保护的电视 [1] 。
由此可见,SELinux 是 Android 系统安全的基石,在高版本上尤其如此。
第一步:尝试打开 adb ,在设置的设备信息页面按「上上下下左右左右」
第二步:通过无线 adb 连接,发现系统是 Android 10 ,getenforce 返回 permissive ,看来可以利用 magica 提权了。
第三步:使用修改后的 magica (移除了安装 magisk 环节)提权(图2),启动 telnet ,得到 root shell ,但这时候只能任意 setuid ,并没有完全的 cap (图3)
第四步:利用 root uid 可以修改属性,尝试 magisk resetprop ,但未成功。考虑到属性文件可以直接写,于是干脆直接 patch 二进制
getprop -Z ro.debuggable
u:object_r:exported2_default_prop:s0
把 /dev/_ _properties _ _ /u:object_r:exported2_default_prop:s0 拉出来,用二进制编辑器直接修改 ro.debuggable=1 (图4)然后放回设备,覆盖原来的文件,这里保险起见,先移动原先的 prop 文件再把新的文件放入。
随后在部分提权的 root shell 中 重启 adbd ,然后 adb root ,即得完全特权的 root shell (图5)。由于没有 SELinux ,修改 ro.debuggable 就能使 adbd 转换到 root 了。
上面的操作可能会对属性服务造成一定的影响,可以在操作完成后把原先备份的文件还原回去。
要利用上述漏洞,只要能安装任意应用即可,因此还是很危险的。
感觉国内不少 Android TV 在 SELinux 方面都很摆烂,我以前也用类似的方法 pwn 了家里另一台没有 SELinux 保护的电视 [1] 。
由此可见,SELinux 是 Android 系统安全的基石,在高版本上尤其如此。
❤5👍3
之前有人汇报 zksu umount 不干净,可以检测到 lsposed 挂载的 dex2oat
从日志中发现,umount
观察 mountinfo ,发现这个系统的 apex 模块都放在 /system/apex ,通过 bind mount 挂载每个模块。
LSPosed 会在 service 阶段挂载 dex2oat wrapper 到
而
而 post-fs-data 时,ksu 在 /system 挂载了 overlay ,因此 /system/apex/ 下的挂载点就被屏蔽了,无法通过 umount 卸载,除非先把 overlay /system 卸载了。而 zksu 的 umount 是简单按照 path 倒序 umount 的,因此 /system 后被卸载,先卸载被屏蔽的
虽然 init ns 中,两个挂载点是 shared 的,但 zygote 的 unshare 会把所有挂载点改成 slave 的,因此 app 进程的 ns 中两个挂载点不会相互传播 umount ,所以 zygisksu 虽然成功卸载了 /apex 下的 dex2oat ,但无法卸载 /system/apex 的。
最后我给了一个 workaround :在 LSPosed 挂载之前,把
我有考虑过在 zygisksu 中处理,但是这实际上很麻烦,因为没有一个系统调用可以在不变动挂载的情况下访问被屏蔽的挂载点。如果用户的系统存在类似的无法隐藏的问题,可以参考上面的方法解决。
https://news.1rj.ru/str/LSPosedDiscussion/1065339/1145697
https://news.1rj.ru/str/KernelSU_group/3252/119873
P.S. 绑定挂载的 apex 目录实际上来自系统的扁平化 APEX,用于在不支持 loop device 的旧设备上实现 APEX
从日志中发现,umount
/system/apex/com.android.art/bin/dex2oat... 返回错误 22 (EINVAL)观察 mountinfo ,发现这个系统的 apex 模块都放在 /system/apex ,通过 bind mount 挂载每个模块。
LSPosed 会在 service 阶段挂载 dex2oat wrapper 到
/apex/com.android.art 下。而
/system/apex/com.android.art 被 bind mount 到 /apex/com.android.art ,且 init ns 的挂载点都是 shared 的,因此这两个挂载点也在同一个 peer group 中,LSPosed 挂载的 wrapper 被传播到 /system/apex 下了。而 post-fs-data 时,ksu 在 /system 挂载了 overlay ,因此 /system/apex/ 下的挂载点就被屏蔽了,无法通过 umount 卸载,除非先把 overlay /system 卸载了。而 zksu 的 umount 是简单按照 path 倒序 umount 的,因此 /system 后被卸载,先卸载被屏蔽的
/system/apex/.. 自然是失败的。虽然 init ns 中,两个挂载点是 shared 的,但 zygote 的 unshare 会把所有挂载点改成 slave 的,因此 app 进程的 ns 中两个挂载点不会相互传播 umount ,所以 zygisksu 虽然成功卸载了 /apex 下的 dex2oat ,但无法卸载 /system/apex 的。
最后我给了一个 workaround :在 LSPosed 挂载之前,把
/apex/com.android.art 的 peer group 重置一下,也就是先 make private 再 make public ,这样就不会传播到 /system/apex 了。我有考虑过在 zygisksu 中处理,但是这实际上很麻烦,因为没有一个系统调用可以在不变动挂载的情况下访问被屏蔽的挂载点。如果用户的系统存在类似的无法隐藏的问题,可以参考上面的方法解决。
https://news.1rj.ru/str/LSPosedDiscussion/1065339/1145697
https://news.1rj.ru/str/KernelSU_group/3252/119873
P.S. 绑定挂载的 apex 目录实际上来自系统的扁平化 APEX,用于在不支持 loop device 的旧设备上实现 APEX
🤔10👌2
