ReZygisk 外围:IPC、模块脚本、构建与 WebUI
本文是 ReZygisk 源码解读系列第 5 篇(末篇)。全系列:
00总览与整体架构01守护进程 zygiskd 与 Root 适配02注入机制:monitor 监控与 ptracer 注入03注入体 injector 与反检测基石04外围:IPC 协议、模块脚本、构建签名与 WebUI(本篇)代码引用格式
文件:行号。
一、本篇定位
前四篇讲清了 ReZygisk 的核心机制。本篇收束剩下的"骨架与外壳",分四块:
- IPC 与通信协议——把散落在 01/02/03 的三类 socket 汇总成一张完整的通信图谱;
- 模块脚本——一个模块是怎么被安装、开机拉起、卸载的;
- 构建、签名与打包——源码如何变成一个可刷入、带完整性校验的 zip;
- WebUI——
webroot/这个单页应用如何展示状态、控制 monitor。
二、IPC 与通信协议总览
ReZygisk 各组件靠 三类 Unix domain socket 通信:
| # | socket | 类型 | 路径 | 端点 | 协议 |
|---|---|---|---|---|---|
| ① | daemon socket | SOCK_STREAM |
/data/adb/rezygisk/cp32.sock、cp64.sock |
libzygisk/injector ↔ zygiskd | enum DaemonSocketAction(9 动作) |
| ② | controller socket | SOCK_DGRAM |
/data/adb/rezygisk/init_monitor |
zygiskd → monitor,用户 ctl → monitor | enum rezygiskd_command |
| ③ | companion socket | socketpair + fd 转交 |
匿名 | zygiskd ↔ companion,APP ↔ companion | 模块自定义 |
2.1 daemon socket(详见 01 篇)
zygiskd 用 create_daemon_socket(zygiskd/src/zygiskd.c:135)建 SOCK_STREAM 监听 socket,客户端在 loader/src/common/daemon.c 的 rezygiskd_connect(daemon.c:18)连接。一次请求一次短连接,裸二进制、主机字节序。9 个动作(ZygoteInjected..RemoveModule)语义见 01 篇第五节。
2.2 controller socket(详见 02 篇)
monitor 用 rezygiskd_listener_init(loader/src/ptracer/monitor.c:150)建 SOCK_DGRAM socket(路径 TMP_PATH "/init_monitor")。两个方向:
- zygiskd → monitor:
unix_datagram_sendto(zygiskd/src/utils.c:121)发送状态。命令码在constants.h用LP_SELECT按位宽编码:ZYGOTE_INJECTED = LP_SELECT(5,4)、DAEMON_SET_INFO = LP_SELECT(7,6)、DAEMON_SET_ERROR_INFO = LP_SELECT(9,8),对应monitor.h的enum rezygiskd_command(4..9)。 - 用户/WebUI → monitor:
send_control_command(monitor.c:1053)发送START/STOP/EXIT(1/2/3)。
2.3 companion socket(详见 01 篇第六节)
spawn_companion(zygiskd.c:141)用 socketpair 建立 zygiskd↔companion 通道,再用 write_fd(SCM_RIGHTS)把 APP 的连接 fd 转交给 companion,此后 APP 与 companion 直连。
2.4 两套编码原语
协议两端各有一套等价原语,这是两端能对话的基础:
守护进程侧 zygiskd/src/utils.c |
注入端侧 loader/src/common/socket_utils.c |
作用 |
|---|---|---|
write_string/read_string |
同名 | "size_t 长度 + 原始字节" |
write_uint8_t/uint32_t/size_t(宏生成) |
同名 | 定长整型,主机字节序 |
write_fd/read_fd |
同名 | SCM_RIGHTS 传递文件描述符 |
SCM_RIGHTS fd 传递用 sendmsg/recvmsg 的辅助数据携带一个 int fd,是 companion 接力、模块目录 fd(GetModuleDir)、namespace fd 等"跨进程交接"的关键。
2.5 SELinux 上下文
socket 要让 zygote 有权连接,必须打对标签:
set_socket_create_context("u:r:zygote:s0")(utils.c:56):让 daemon socket 带 zygote 域上下文。chcon(path, "u:object_r:zygisk_file:s0")(unix_listener_from_path,utils.c:174):给 socket 文件打zygisk_file标签。
这两个标签的权限由 sepolicy.rule 授予(见第三节)。
三、模块脚本与安装/启动/卸载
ReZygisk 以标准 Magisk 模块格式 的 zip 分发,兼容 KernelSU / APatch 安装器。
3.1 安装流程
customize.sh(module/src/customize.sh)要点:
- 来源与版本(
:10-52):必须从 KSU/APatch/Magisk app 安装(非 recovery),各自检查最低版本(@MIN_*_VERSION@在构建时被替换为 common.mk 的值)。 - 拒绝多 root(
:25-30):KSU 环境下which magisk命中则 abort——与 zygiskd 的Multiple检测(01 篇)呼应,从安装期就拦截。 - 架构 binaries(
:159-203):按设备 ABI 提取并重命名——bin/<arch>/zygiskd→bin/zygiskd{32,64},lib/<arch>/libzygisk_ptrace.so→bin/zygisk-ptrace{32,64},lib/<arch>/libzygisk.so→lib(64)/libzygisk.so,并提取machikado.<arch>。 - 权限(
:205-214):bin/设0755;lib/lib64设system_lib_file上下文(让 zygote 能加载注入体)。 - Huawei Maple(
:216-221):若启用则写system.prop关闭(Maple 的 system_server 创建在 Zygisk 控制之外)。
verify.sh 的 extract 函数:解压每个文件的同时校验其 .sha256,防下载损坏/篡改。
3.2 开机启动链
post-fs-data.sh:核心启动点。ZYGISK_ENABLED(Magisk 内置 Zygisk 已开)则退出避免冲突;Magisk 环境下手动触发其它模块的post-fs-data.sh;创建/data/adb/rezygisk;按 CPU abilist 启动zygisk-ptrace64/32 monitor(:50-56)——这是 02 篇 monitor 的起点。service.sh:若ZYGISK_ENABLED则改 module.prop 描述提醒关掉内置 Zygisk;手动触发其它模块的service.sh。rezygisk.sh:被装到/data/adb/post-fs-data.d/与/data/adb/post-mount.d/,把module.prop从module.prop.bak还原——即使 ReZygisk 被禁用,描述也回到干净初始状态、不残留旧状态文字。(注释解释 KSU 2.x 在挂载前跑 post-fs-data.d,故同时放一份到 post-mount.d。)
3.3 sepolicy.rule
module/src/sepolicy.rule 增加的 SELinux 规则(节选):
type zygisk_file file_type # 定义 zygisk_file 类型
allow zygote zygisk_file sock_file { read write } # zygote 可读写 socket
allow zygote su dir search # setns 到子进程 mns
allow zygote ksu { lnk_file file } read # KSU 改名的 su 上下文
allow zygote zygote process execmem # 执行注入的内存
allow system_server system_server process execmem
allow zygote system_lib_file file execmod # Tango Zygote 需要
这些规则让注入、namespace 切换、socket 通信在 SELinux enforcing 下可行;execmem/execmod 是远程注入可执行内存的必要权限。
3.4 设备目录布局
/data/adb/modules/rezygisk/
├── module.prop module.prop.bak # 元信息 + 备份
├── customize.sh post-fs-data.sh service.sh uninstall.sh
├── sepolicy.rule rezygisk.sh
├── machikado.<arch> misaki.sig *.sha256 # 签名(第四节)
├── system.prop # (仅 Huawei Maple)
├── bin/ zygiskd32 zygiskd64 zygisk-ptrace32 zygisk-ptrace64
├── lib/ libzygisk.so # 32 位注入体
├── lib64/ libzygisk.so # 64 位注入体
└── webroot/ # WebUI
/data/adb/rezygisk/ # 运行时目录 (TMP_PATH)
├── cp32.sock cp64.sock # daemon socket
├── init_monitor # controller socket
├── state.json # 状态(供 WebUI)
└── webui_error.log # WebUI 错误日志
/data/adb/post-fs-data.d/rezygisk.sh
/data/adb/post-mount.d/rezygisk.sh
3.5 卸载
uninstall.sh:删除 /data/adb/rezygisk 运行时目录,删除 post-fs-data.d/post-mount.d 里的 rezygisk.sh,目录空时 rmdir。
四、构建、签名与打包
4.1 三层 Makefile
common.mk集中定义:MODULE_NAME=ReZygisk、MODULE_ID=rezygisk、VERSION=v1.0.0、VER_CODE(git rev-list --count HEAD)、COMMIT_HASH(git rev-parse --short HEAD)、ARCHS = arm64-v8a armeabi-v7a x86_64 x86、NDK_VARIANT=27、ANDROID_PLATFORM=21、C_STANDARD=gnu23,以及安装期最低版本MIN_KSU_VERSION=12096/MIN_KSUD_VERSION=12096/MIN_MAGISK_VERSION=26402/MIN_APATCH_VERSION=11010。- 顶层
Makefile的$(MODULE_DONE)(Makefile:62)做组装:sed把module.prop的${moduleId}等和脚本里的@DEBUG@/@MIN_*_VERSION@替换为实际值,按架构复制 binaries,复制 webroot,调用sign.py,最后zip -r9打包。 loader/Makefile:libzygisk.so= injector + common + PLTI + CSOLoader,-nostartfiles链接(呼应 03 篇裸entry());libzygisk_ptrace.so= ptracer + common,-fPIE。release 用-O3 -flto -DNDEBUG,debug 用-g -O0。zygiskd/Makefile:zygiskd用-fPIE -pie链接成可执行文件。
安装期 vs 运行期版本下限:common.mk 的
MIN_MAGISK_VERSION=26402/MIN_APATCH_VERSION=11010用于customize.sh安装检查;而magisk.h硬编码26000、apatch.h硬编码10657用于运行时检测(01 篇)。两者独立,安装检查更严格。
4.2 依赖来源
zygote_internal:.gitmodules唯一 git submodule,来自Dr-TSNG/ZygiskNext(ReZygisk fork 自 Zygisk Next)。- PLTI / CSOLoader:位于
loader/src/external/plti、loader/src/external/csoloader,各有独立 Makefile 被loader/Makefile递归构建。CI 用submodules: recursive拉取。PLTI 是 PLT hook 库(03 篇),CSOLoader 是自定义 ELF 加载器(02/03 篇)。
4.3 三层签名体系(scripts/sign.py)
用 Ed25519 实现三层完整性校验:
- machikado.
( sign_machikado,sign.py:41):对该架构关键运行时文件(module.prop、各脚本、sepolicy.rule、libzygisk.so、zygisk-ptrace{32/64}、zygiskd{32/64})按设备虚拟路径排序,逐文件构造name + 0x00 + 8字节LE大小 + 内容数据块,整体 Ed25519 签名。文件 =签名(64B) + 公钥。供运行时验证自身加载的文件未被替换。 - misaki.sig(
sign_misaki,sign.py:114):整个模块所有文件的 Ed25519 签名。 - .sha256(
compute_sha256_hashes,sign.py:93):每文件一个哈希,安装期verify.sh校验。 --no-sign模式(sign.py:150):无密钥时创建空 machikado/misaki,仅算 sha256——普通用户自构建(未签名)的产物。
4.4 CI
.github/workflows/ci.yml(Untrusted CI,PR 触发):make all,不签名。.github/workflows/trusted_ci.yml(Trusted CI,pushmain触发):先从secrets.ORG_PRIVATE_KEY/PUBLIC_KEY写入module/private_key/public_key再make all——产出官方签名版。
这解释了 README 里 release/debug 两种 zip,以及"官方签名版"与"自构建版"的区别。
五、WebUI(webroot)
webroot/ 是纯前端单页应用,由 root 管理器内置 WebView 加载,展示状态、控制 monitor。
5.1 架构与桥接
- 桥接(
webroot/js/kernelsu.js):exec(command, options)通过全局ksu.exec(root 管理器注入的 JS 接口)执行 shell 命令,回调返回{errno, stdout, stderr};ksu未定义(PC 测试)时回退到development_kit.js假响应。 - 页面加载(
webroot/js/pages/pageLoader.js):所有页面 HTML/CSS/JS 一次性加载再切换显示(避免动态渲染闪烁),每页有loadOnce/loadOnceView/load生命周期。
5.2 数据流:state.json 与 ctl
WebUI 与守护进程的全部交互都通过 shell 命令:
| 操作 | 命令 | 来源 |
|---|---|---|
| 读运行状态 | /system/bin/cat /data/adb/rezygisk/state.json |
home/actions/modules |
| 读版本 | cat /data/adb/modules/rezygisk/module.prop |
home |
| 系统信息 | uname -r / getprop ro.build.version.release |
home |
| 控制 monitor | /data/adb/modules/rezygisk/bin/zygisk-ptrace64 ctl start|stop|exit |
actions |
state.json 由 monitor 的 update_status(02 篇,monitor.c:879)写入,含 root、monitor.state、rezygiskd.{64,32}.{state,modules}、zygote.{64,32}。各页解析:
- home(
home/index.js):整体健康度(ok/partially/notWorking)、Android/内核版本、ReZygisk 版本、root 实现、Zygote32/64 注入状态。 - actions(
actions/index.js):monitor 状态(0 tracing/1 stopping/2 stopped/3 exiting),三按钮发ctl start(启动)、ctl exit(停止)、ctl stop(暂停)——对应 02 篇send_control_command。 - modules(
modules/index.js):列出rezygiskd.{64,32}.modules,grep 各模块module.prop取显示名 + 架构(32/64)。 - settings(
settings/index.js):全屏/系统字体开关、语言/主题入口、致谢(PerformanC / Dr-TSNG / 5ec1cff / ThePedroo / RainyXeon)、许可证 AGPL 3。
5.3 i18n 与主题
- i18n:HTML 用
{{key}}占位,pageLoader.js的solveStrings从lang/<lang>.json(14 种语言)替换;语言存localStorage,settings 的语言 minipage 切换。 - 主题(
themes/main.js):amoled/dark/light/system,每个设置一组 CSS 变量(themes/dark.js等)+ 导航栏图标模式(darkNavbar.js/lightNavbar.js)。 - 动画(
pages/animator.js):MD3 shared-axis 页面切换(左右推动)+ minipage 右侧滑入。
六、小结
ReZygisk 的"外壳"同样讲究:
- 三类 socket 织成通信网:daemon socket(stream,DaemonSocketAction)承载注入端↔守护进程 RPC,controller socket(datagram,init_monitor)承载状态上报与 ctl 控制,companion socket 承载模块伴生进程接力;
SCM_RIGHTS与 SELinux 上下文是其在受限环境工作的关键。 - 模块脚本 实现"开机即注入":
post-fs-data.sh拉起 monitor,rezygisk.sh维持 module.prop 干净,sepolicy.rule授予 SELinux 权限,安装期就拒绝多 root / 旧版本 / recovery。 - 构建是分层 Makefile + Ed25519 三层签名:
make all多架构编译 → 组装(占位符替换 + 复制)→sign.py生成 machikado/misaki/sha256 → 打包;Trusted CI 用组织密钥产出官方签名版。 - WebUI 是零后端单页应用:全部数据靠
ksu.exec执行cat state.json等命令获取,控制靠zygisk-ptrace64 ctl,配合 i18n 与主题给出直观状态面板。
至此,ReZygisk 源码解读系列完结。从守护进程的中枢调度(01),到 ptrace 注入的精巧时机(02),到注入体的 hook 与反检测(03),再到外围的协议、脚本、构建与界面(04),共同构成了这个"纯 C、防检测、FOSS"的 Zygisk 独立实现。
评论
- 还没有评论,来说点什么吧。