白彩恋
总主编2025 年 12 月 12 日 星期五
各周目合集: 历史渊源
前言
写了好几天,大部分是在一晚上通宵写出来的,有点昏头了…SukiSU Ultra 总是在不断地搬起石头砸自己的脚,我们至今无法知道在其管理器主页所写的“相对独立”究竟是如何相对? 我们无法区分哪些人是他们的开发成员,哪些只是管理员, 但是从他们的言论来看,他们把一个开源项目 代码写的烂、不断拉取其他项目 当作了理所应当的, 难道要靠 不断缝合 来实现所谓的“独立”?
SukiSU Ultra 只有名字是独立的之前看一个人的评论,SukiSU Ultra 要是叫 KernelSU Ultra 或者 SukiSU 都没什么好说的,
可是偏偏却叫了个 SukiSU Ultra 这么个名字感觉不如叫 SukiSU Ultra Turbo Pro Max+ 把 buff 叠得更多些
他们恶心人还怪有一套的,与
满血核心 的作者 Caelifall 一样,我抄就是我有能耐,我能写就是我厉害,别人有什么资格来说?可是呢?莫名其妙的 zakosign 最终还不是会被 KernelSU 官方以 更完善的体系 所取代,一个 莫名其妙、没有水平 的项目怎么可能被认可?一直在推崇的魔改后的抽象 MD3 风格还不是换成了上游的 MIUIX?(但是基于现有情况来看,最后会使用什么 UI 风格仍然是未知的)不过为了能够拉取上游,甚至使用到了 Cursor(或许也可能是项目 烂到只能用 AI 来维护了?)我个人从未使用过 AI Agent,以后也不会使用,如果一个项目要完全靠 AI 来维护的话,那就像以前已经说过了的,已经无药可救了
(虽然并不知道 SukiSU Ultra 的代码究竟是 神人 写的还是 AI 写的)但是说实话,整个 SukiSU Ultra 还没有达到 Caelifall 这样的高度,这样一个 网暴、开户、圈钱、作秀、剽窃、抄袭 样样精通的人,
要么他是一个 高度的神人,要么他就是一个 AI 的重度滥用者(两者都是也说不定呢?),这个高度 SukiSU Ultra 还是太难达到了混乱的分支
每次看 SukiSU Ultra 都能给我带来新的体验,最直观的就是分支变得更莫名其妙了 SukiSU Ultra 目前有以下分支(以 GitHub 排序排列):- main
- Crowdin
- miuix-dev
- miuix
- tmp-builtin
- builtin
- dev
main 是主分支;Crowdin 是多语言翻译分支;
miuix-dev 是 MIUIX UI 的开发分支;miuix 是 MIUIX UI 的分支;
tmp-builtin 与 builtin 这两个分支只有内核源码,但是连个 README 都没有,理论上是给内核集成用的分支,
并且从他们近期发布内容可以得知有 tmp-builtin 分支的存在是因为 builtin 由于强制推送导致错误,有分支保护又无法及时修复;
dev 则是开发分支
理论上这个分支规划貌似做的还是挺好的?实则不然
因为按照内核集成文档,在各个分支中,main 是 susfs-main,test 是 susfs-test,然而 test 分支在哪里并不知道
给非 GKI 内核用的 nongki 分支也不知所踪,带 susfs 的分支也不知道去哪里了
因为他们在分支变动后根本就没人再管文档了,文档的上次更新还是几个月前的,其他开发者要是不分析一下是用都没法用的
(都没法想该怎么办,GitHub 上找一圈还要再去 Telegram 找一圈,说不定还找不到)
builtin 分支都有了结果却没个 builtin-dev 分支,要是有还能出现意外导致分支暂时无法使用,还要再搞个临时分支的情况吗?
实际上就算有他们也不会好好开发,因为据提交内容来看,居然是先在主分支提交再同步到开发分支
(在提交历史中合并的源是 main 分支而不是 dev 分支)
甚至还能出现主分支比开发分支提交还多的情况,
都不知道开发分支是开发在哪里了,真的是把他们自己所说的“不稳定”发挥到极致
讲一个正面的典范:之前在 Flutter 上使用知名开源库 MMKV,在 Flutter 新版本上由于部分 API 弃用,无法正常编译然后我就去提了个 issue,开发者说不知道,
但是我稍微查了一下,实际上只是因为导入了一个弃用 API,但是这个 API 并没有用到,删掉那行
import 即可于是我便向 MMKV 提了个 PR,由于我并没有注意到 MMKV 的主分支与开发分支什么的,就直接向主分支 PR 了然后开发者就提醒我要向开发分支 PR,我才重新向开发分支 PR,
然后我的 PR 就随着开发变动合并进了主分支,开发者在新版本发布后,还在原来的 issue 下提醒我已经更新这才是开发分支的真正用途,所有变动与 PR 都要先基于开发分支,然后才可以合并进主分支给所有人使用但是人家 SukiSU Ultra 都说了自己不稳定了,也不好再评价什么了,dev 分支存在都不用存在,随便写点啥都直接端上来用呗管理器 UI 将要何去何从?
自 SukiSU Ultra 诞生之初就饱受诟病的抽象 MD3 UI,也是跟随 KernelSU 上游把 UI 换成了 MIUIX, 但是就像是上面列出的分支列表,原本的抽象 UI 仍然存在,与 MIUIX UI 处于不同的分支,只不过他们目前发布的都是 MIUIX 的了 我们无从得知他们为什么要这样做,只能推测出如下可能的情况:- 他们以后将要同时发布两个 UI 的管理器(就像是以前两个图标一样)
- 他们会在一个管理器里面做两套 UI
- 他们要把 MIUIX 改得像他们的 MD3 一样抽象
“惊天地泣鬼神”的多语言 UID 扫描器
第一次见到user_scanner 这个东西,我还以为是 Lama3L9R 这个 C 魔怔人写的,结果看提交还是主作者 ShirkNeko 写的,
一个个都让我大开眼界
为什么说 Lama3L9R 魔怔呢?因为他自己说讨厌
C++ 喜欢 C,而且还熟悉 C但是实际情况呢?只从 zakosign 来看,毫无水平(对他其他的项目也没有一点兴趣去看)对一个有着跨平台需求的项目(也许是有吧,但是这种东西真的会有人有用吗?),选择的不是使用跨平台表现优秀的语言,而是去使用 C
(C 有什么问题不用多说了)但是就算用 C 吧,他也不用标准库,为了性能每个平台都单独写一套代码,但凡写一套回退呢?就连 syscall 函数都用汇编写,真的没办法理解是什么导致他要一个劲地脱离标准库面对这么些问题,他的回应也是答非所问,还把一些常识说得特别高大上的样子,更何况他说的东西和他项目的问题没有半点关系所以我只能认为他是一个没有什么水平的自娱自乐的 C 魔怔人了,根本就没法理解他的心境是怎么样的,
他这种人就适合写写 Python 用 print 和 input 玩了然后我看到 user_scanner 是 ShirkNeko 写的后,就更加吃惊了,
没办法理解为什么他们要在 ksud 是 Rust 写的的情况下写这么多 C ,总是选择造新的东西而不是做进 ksud 里面Kotlin 这样的高级语言能够看出是什么样的水平,但是在 C 这种低级语言上更能看到下限有多低
user_scanner 给我的第一感觉就是它有一个极其混乱的日志系统,同时用到了 Android 日志、文件日志、printf 三种输出方式,
虽然并非完全不合理,但是 Android 日志 与 文件日志 共用我实在是想不到他有什么特殊的理由要怎么做,更何况调用方式也是一塌糊涂
最难理解的就是这么个程序它为什么会有多语言?要是真的想做多语言的话,为什么不先给 ksud 整上?
各种千奇百怪的抽象让我对代码本身的质量已经没有了判断能力,这种东西写出来害的可不止作者一人还有所有用户
不过很能肯定,这些代码应该是纯 C23 标准,然而 user_scanner 的编译传参是 -std=c99
有多语言的混乱日志体系
user_scanner 中多语言的存在真的是很难让人想通,可以从以下代码中看出来是有多莫名其妙:
uid_scanner.c
uid_scanner.c
ksud 都还是全英文,给这么个东西整多语言有什么用?
更何况用什么语言还要手动传入,要手动传的多语言有什么意思,加个本机当前语言识别很难吗?
硬编码也是各种形式地硬编码,首先就是硬编码 messages 这么多日志信息,然后调用的时候用序号调用???
根本没办法理解到底是如何写出来的,近百个文本究竟该如何正确使用?是他一点一点加进去的还是写完后一次性修改的?
这些问题无从得知,只有他自己知道,很难想象这种东西到底要如何维护,追加条目还好说,插入与删除的难度无法想象
多语言也不完全多语言,ensure_directory_exists 这个函数原本只有一行 system("mkdir -p /data/misc/user_uid");,
貌似是从 Shell 改成 C 后忘记了还有多语言这回事了,直接硬编码上英文了,
日志输出也不用自己封装的 Android 日志 + 文件日志 了,直接只用 Android 日志,也不知何意味(save_config 这块写入的文件倒是没有多语言,日志什么的输出都上了,配置文件怎么不上一个?
翻译的时候也不知道为什么区别对待,print_usage 中的选项用途只有在中文有写,英文则是没有,
写就是写,不写就是不写,不知道为什么要搞这种区别对待,更何况配置文件里面都已经写了英文版本的用途了
再加之 load_config 中传递给格式化的字符串是硬编码中文,也不做个多语言了,总之就是莫名其妙的
多语言也使得格式化变得更加麻烦,不少地方都需要再加一个 printf("\n");,
不过无论是在原始字符串加上换行,还是在调用的地方加一个 printf("\n");,都是极为麻烦的
脱离人类的语言
很难想象 ShirkNeko 是否还会使用人类的语言,因为根本就想不到他是如何能写出这样的多语言的 在set_language 中他写了一行 write_log("INFO", (lang == LANG_ZH) ? 59 : 58);,
对应的文本是 {"Language switched to English", "语言切换到英文"}(58) 与 {"Language switched to Chinese", "语言切换到中文"}(59)
但是究竟是什么情况下需要在切换到 中文的时候显示 英文,在切换到 英文的时候显示 中文?
虽然他的多语言本身就是个问题,但是真要这么写的话不应该是同一个文本,英文显示切换到英文,中文显示切换到中文吗?
最起码得要判断语言然后硬编码个切换语言的文本吧?为什么会像这样有完全不可能用得到的文本?
但是 ShirkNeko 对使用人类语言的失控程度还远远不止于此,在 show_config 中可以看到,
他居然还能在 if (config.language == LANG_ZH) 分支中写出 (config.language == LANG_ZH) ? "中文" : "英文",
在 else 分支中写出 (config.language == LANG_ZH) ? "Chinese" : "English"
我们也无法得知 ShirkNeko 使用的是不是叫做 中英 与 英中 的语言,在语言确定的情况下仍然可以二次判断当前语言
有日志等级吗?如有
所有的日志输出都是硬编码日志等级,语言都能搞个 enum 了,日志等级却不能搞个? 配置文件中的log_level 也是仅仅有这么个值,不会对运行产生任何影响,形同虚设
最令人匪夷所思的是,在 write_log 调用 Android 日志 时,只有错误走 ANDROID_LOG_ERROR,
其余所有日志等级都走 ANDROID_LOG_INFO
判断其他日志等级或许很难,总之能让除了错误外所有日志都走一个日志等级的话,还不如不用 Android 日志,
已经有了 文件日志 不知道为什么还要搞个其他的有的没的
日志写得比运行还急
在write_log 中每写一次日志就要调用一次 fsync,就是说无论干什么都会确保日志写好了才会执行
随便干点什么都会反复强制写入,非得让吃更多性能,让闪存寿命损耗更快?
一个阶段一个阶段地同步或者只同步错误日志不好吗?日志比扫描还急着投胎了
Kotlin for Shell?也可以 C for Shell!
先前已经见识过不少 SukiSU Ultra 管理器中调用Shell 的代码了,Kotlin 中可以这样做,C 同样可以
就像是 ensure_directory_exists 这个函数,先前已经说过它原本只是调用 Shell 执行 mkdir -p,
在提交历史中可以知道因为没权限改成了 C 的 mkdir 与 chmod,
或许是知道 /data/misc 一定存在,所以不再用 Shell 了,不过我觉得还是 mkdir -p 与 chmod -R 适合他
在 show_status 中还使用 tail -n 10 来输出日志,如果不会写,真的没必要做这么个功能
根源问题:为何独立?
uid_scanner 是 SukiSU Ultra 的一部分,
原来的 kpm 管理都整合进 ksud 里面了,为什么 uid_scanner 却是一个相对独立的程序?
uid_scanner.rs
ksud 里面的,并且自启动还是在 service.d 写入脚本来实现的,而不是用一个更内部的做法
重复处理
uid_scanner.c
ksud 中的调用启动守护进程与 uid_scanner 本身的启动过程高度重合,
把 文件描述符屏蔽、工作目录设置、会话脱离 都执行了两遍,对于一个自己的东西,有必要这样闲的干两遍吗?
“不稳定”?还不安全!
uid_scanner 的运行几乎完全脱离 SukiSU Ultra 的原有内容
它的大部分文件都处于 /data/misc/user_uid 这么个与 /data/adb/ksu 毫不搭边的目录,这会导致什么呢?
在很多人的印象里,各种 root 实现的东西就应该存放在 /data/adb 里面,
什么出问题了或者想换 root 实现了就直接把 /data/adb 删掉就好,通常不需要再干点其他的什么
可是 uid_scanner 却运行于 /data/misc,可它并不是一个可以通过卸载脚本清理内容的模块,
它是一个直接属于 root 实现的功能,没有执行清理的机会,只有在管理器手动关闭时才会清理,
但是直接卸载管理器了怎么办?不能什么都让别人买单吧?
所以所有使用过 uid_scanner 的 SukiSU Ultra 用户,在想清理 root 残留时,
除 /data/adb 外,还要额外注意 uid_scanner 所使用的 /data/misc,否则仍有残留,
要解决要么管理器关闭,要么就手动清理了
明目张胆地交互
把数据存/data/misc 还称不上“不安全”,但是把与内核的交互放在 /proc 那就是完全不把安全当回事了
uid_scanner 的内核空间会创建 /proc/ksu_uid_scanner 以便于与用户空间交互
但是这种写法我只在几年前的外挂程序上见过,但是人家外挂程序都知道弄随机路径了,
uid_scanner 居然在用固定路径
这样想知道一个设备有没有在用 SukiSU Ultra 那简直是轻而易举了,
因为用户空间是否启动 uid_scanner 并不会影响内核空间,/proc 里面的交互节点会一直存在
但凡改一下 KernelSU 原本的文件描述符注入逻辑呢?能这样明目张胆地创建一个节点真的让人很不明白
结语
SukiSU Ultra 仍是如此抽象,一直都是如此,代码写起来完全就是随心所欲, 这样如何能给内核集成开发者与使用者一个交代?完全是把除自己外的所有人都不当回事 不知道这么个固步自封的小团体会不会像 Caelifall 一样骂我圣母心, 但是作为这么多人都在用的项目,总不能在别人看不到的地方一直给别人使绊子甚至自己给自己使绊子吧?附录:满血核心究竟是一个怎样的模块?
我早些已经说过了满血核心的很多问题,但是一直没有说过为什么会有这些问题,因为我怕 Caelifall 看到了就直接修好了, 但是时间久了感觉就没什么必要了,所以还是写写为什么会这么烂,毕竟它的口碑早就烂成什么样了缝合伊始,前世今生
之前也已说过,满血核心是一个模块比作者出名的东西,以至于我不知道 Caelifall 是从 Luxus 改名的, 还以为是别的某个人接手的这么个烂摊子,结果一直都是他一个人 知道了这么个事,他的问题那就真可是源远流长了,我从来都没有觉得骂他的人少过 最为典型的事件就是缝合模块时把 nakixii 的线程优化程序缝合了进去, 但是程序里面有模块 ID 验证,他就索性直接把模块 ID 也改成一样的了 抄能抄成这样的真的是少见了,为了抄成功连模块 ID 都可以抄,他的开发品质就是这样的 他缝合的那些东西应该可以算得上满血核心的前身,并且从他改名前的满血核心一直到现在,也都是缝合与 AI 的产物, 因为他连最基本的开发能力都没有,满血核心中的代码我都不知道他自己能不能看懂 但是现在再去考证就有些麻烦了,因为重构成了C++ 版本,
就算是以前的 Shell 版本,也被改得面目全非,无法与其他模块进行直接对比,
但是看个大概也能看出可能是从哪里抄来的,毕竟能实现他那些功能的模块知名的就那么几个
水平低下,不知廉耻
无论是从满血核心的Shell、C++,以至于 WebUI,都可以看出是一个非常没有水平的废品,
再加之他本人的素质,满血核心就这样臭名远扬下去了
素质问题:丑态尽出
Caelifall 网暴过包括我在内的很多人,在自己群里发个公告挂个人把自己当大爷一样,但是他群里的人不都是付费品屎吗?还会吹嘘自己群里几千人人多,我群里几万人我说过什么了吗?
无论是他自己网暴、教唆网暴,还是他粉丝来网暴我,感觉都挺没什么意义的
按别人说的,他有时间捐这么点钱,还不如买点东西抽给用户,但是他都不把用户当人看,还能怎么办?
代码问题:“勇气可嘉”
我没收集过他的黑料,所以就说些很多人都知道的事情,最切实地还是要从能直接看到的代码上来讲WebUI:难以溯源
满血核心的 WebUI 从 UI 上就相当抽象了,莫名其妙的 UI 抽搐难以理解是怎么写成这样的 按某位经常从事前端开发的话大概来讲,就是能把 UI 写成这样的,要么是神人,要么是 AI 确实,因为满血核心的 WebUI 的确带来了一种非常诡异的感觉 WebUI 的html、css、js 代码全都有一种莫名其妙的现象:部分压缩,部分展开
WebUI 的代码都集中在 index.html、script.js、style.css 中,
除此之外除了一个可能是依赖库的 draw.js,就没有其他的了
但是这三个文件都是部分正常展开行,部分压缩成一行,比较可能的是把构建出的代码进行了部分手改或 AI 改,
但是具体怎样也是无从得知,并且所有的 svg 图标都是内联进 html/js 的,很难分析整个 WebUI 是如何写出来的
C++:掩耳盗铃
首先是不得不佩服他把所有可执行文件都放在了webroot 这么个用来放前端资源的地方,连好好放文件都做不到
并且一堆可执行都是些莫名其妙的名字,也佩服他开发时的心境
所有可执行都使用了 Hikari 来混淆加密,
但是实际上只要想破解,他无论是混淆加密 Shell 也好,C++ 也罢,就那样的水平怎么能挡得住?
没有混淆加密的版本我也有,以 alpha 4.76.8(250817) 举例,这是个在他发布重构版前的一个 C++ 的版本
实际上就是 Shell 转 C++,这个版本是完全照搬 Shell 版本的,文件命名一模一样,重构估计就是给 AI 下个命令的事
剩下的 Shell 代码(service.sh 等关键脚本)中已经去掉了其他脚本对应的可执行的 .sh 后缀,
结果通过逆向发现,C++ 内部的居然还没改,就是说这个版本实际上跑都跑不起来,也不知道重构究竟是如何实现的
通过逆向也能对他的代码质量看个大概,这种东西无论是人写的还是 AI 写的,都没有一点可用性
Shell:暴露本质
一切皆以最后一个不知道为什么他会对以极弱手段保护的Shell版本alpha 4.77.1(250817) 为例
Shell 脚本展现出比命还重要的态度,他看得这么重的这些代码反而是能暴露他问题的本质
有一个小问题我也不知道是谁起的头,为什么好多模块都把
versionCode 写成 versioncode 了?毫无意义、徒增风险的验证
在安装脚本中,验证用户的逻辑居然是硬编码的 QQ 号,验证成功会写入个/data/local/tests/system/mega 这么个文件
这么做就是为了好在其他脚本中验证这个文件的存在,好让复制模块目录的用户无法使用(防盗)
只能说谁家好人这么验证啊,没点技术这么瞎搞一点用都没有
硬编码滥用
依旧是喜闻乐见的硬编码,满血核心的硬编码包括但不限于以下内容:- 在已经定义
MOD_PATH=$(dirname "$0")的情况下硬编码路径 - 在安装脚本退出硬编码
rm -rf "/data/adb/modules_update/Caelifall_SensorDecoy" 2>/dev/null并exit 1 - 在安装脚本硬编码
/data/adb/modules/Caelifall_SensorDecoy/module.prop来判断能否实现他的覆盖安装 - 定义
Device_market_name=$(getprop ro.vendor.oplus.market.name)与cleaned_name=$(echo "$Device_market_name" | tr -d ' ' | tr '[:upper:]' '[:lower:]')实现通过机型名称判断机型 - 在卸载脚本硬编码
rm -rf "/data/adb/modules_update/Caelifall_SensorDecoy" 2>/dev/null
非人逻辑,缺乏常识
满血核心之所以长期以来饱受诟病的其中一条是卸载无法恢复, 是因为 Caelifall 缺乏对系统服务的常识,肆意修改persist 属性所导致的
他在代码中频繁以 persist.sys.horae.enable 作为温控是否启用的依据,
并且在启用/关闭时都会执行 setprop 与 start/stop
他一直用 setprop 与 start/stop 重复开/关两次,
但是 persist 属性是持久化的,用 setprop 改还会影响到本地属性,
就算要改属性也应当是通过 resetprop 来临时性修改,
这样也用不着再在卸载脚本里面用 setprop 来改回去了
(有些地方还在调用 setprop ctl.start/setprop ctl.stop,不知何意味(实际上有部分
persist 属性他也没在卸载脚本里面恢复if (( $(echo "$android_os_version_number < 14" | bc -l) )) 来判断 Android 版本,
直接用版本号而非 SDK 来判断我真是第一次见,更何况 (( ... )) 并非 POSIX 语法,我一般默认都是 AI 写的
(其他地方也有多处用到了 (( ... )) 语法)
判断机型拿名称判断我也是第一次见,更何况是
*一加ace2*|*一加ace2v*|*一加ace2pro*|*一加ace3*|*一加ace3pro*|*一加ace3v*|*一加11*|*一加12*、
*一加ace5*|*一加ace5pro*|*一加ace5至尊版*|*一加ace5竞速版*|*一加13*|*一加13t*
这种前面都把后面匹配了的无意义判断
他在写大小写转换的时候还是 tr 'a-z' 'A-Z' 与 tr '[:upper:]' '[:lower:]' 两种形式混用,
也许是有部分是抄的,也许是有部分是 AI 吧?
变量定义也是 A、B、C 等等随心用:
ps -ef | grep -Fw "sh ..." | grep -v grep 开判断的,
好好利用配置文件是一件相当困难的事情呐
处理个文件也是很奇怪,好好的 TMPDIR 不用,非得在安装时自己创建一个 /data/adb/bk,
而且用完了还不删,非得在卸载脚本删是什么逻辑?
进退两难:破坏还是破坏
我无法理解为什么对cgroup 的处理能被称为“ColorOS 墓碑完全体”,明明就是在破坏 cgroup 与 cgroup 中二选一
frozen 与 unfrozen 的冻结都开启效果才好
但是 frozen 是 冻结,unfrozen 是 解冻,冻结 与 解冻 全部 冻结 除了造成死机还能干什么?
(不过还是要看当前的墓碑用的是哪种冻结方式,用了这两个是真得暴毙)
把这个代码扔给任何一个研究过墓碑的都会被笑死
frozen 与 unfrozen 两个 cgroup 节点直接删掉,
并且他也是以这两个节点是否存在来作为有没有开启这个“墓碑”功能的依据的,不开不应该不管吗,怎么还删了?
(所以所有满血核心用户使用即关闭 cgroup V2 的 frozen 与 unfrozen)
所以关是彻底关,开是彻底冻,总的来说还是不被冻死机好一点
但是实际上这个功能完全没有存在的必要,都已经确保是 Android 14+ 了,
还要对 cgroup V2 的节点下如此黑手?ColorOS 的 hans 墓碑都推出多久了啊?
Caelifall 和 ShirkNeko 都是硬删 cgroup 的狠人啊…
意难平
有的用户还因为满血核心的梗被 Caelifall 踢出去的…蹭热度吗?实则不然
Caelifall 连SUU 与 SSU 都分不清,还以为我在拿 SSU 蹭他的热度,我有必要蹭他黑红的热度吗?
但是 SSU 中清理系统缓存的功能的确是专门为满血核心而写的,因为之前有它的用户在使用后系统故障,
清理了一下系统缓存的确就好了,我索性就在 SSU 里面添加了这个功能