iloader 侧载工具能力水位审计:5 条关键链路逐一开盒,缺口与补丁路径全记录
【免费下载链接】iloaderUser friendly sideloader项目地址: https://gitcode.com/GitHub_Trending/iloa/iloader
iloader 是开源 iOS 侧载工具的图形界面实现,负责 Apple ID 登录、IPA 安装、SideStore 部署与配对文件投放。本文以 v2.4.1(commit e05c91d)的 Rust 后端 src-tauri/src/ 与 React 前端 src/ 为唯一主线,对照 iOS 侧载工具的品类基准,做一份可逐条复核的能力水位审计。证据边界:只读上述两个目录;CONTRIBUTING.md 仅用于范围声明,仓库内没有的 docs、测试与基准不作引用。读完后你能做到三件事:区分三档图例与证据分级的口径、在仓库中定位每个标记落到的文件与函数、知道补齐某条能力要改哪个文件哪个函数、是否必须跨仓库。
度量口径:怎么评、评什么、不评什么
| 标记 | 含义 | 常见误读 |
|---|---|---|
| ✅ | 通路端到端可用,后端与 UI 均有源码证据 | 这不是"无已知缺陷",只代表通路存在 |
| 🟡 | 有通路,但关键语义未实现 | 这不是"差一点完成",而是能用但缺核心行为 |
| ❌ | 无对应实现层 | 这不是"计划中",除非代码注释自证有意不做 |
证据分级,三档各一句话:已验证事实——代码可直接证实,无歧义;注释口径——代码注释里给出的数值或说法,未经实测,引用时标注"注释口径";未测量——无基准、无数据,明说缺口在哪,本文不给数字。
范围声明:审计对象是 v2.4.1(package.json 与 Cargo.toml 版本字段一致)在 commit e05c91d 的现状。CONTRIBUTING.md 第一行声明项目正在大改版(UI 重构与新支持平台),故本文只审计当前 tag 现状,不把计划中当结论。
文档来源声明:仓库内没有 docs 目录,也没有 hardware-parity、gaps 一类的现成审计文档,本文是逐文件走读后的重建,非对某份文档的转述。测试与基准同样缺失:src-tauri/ 无#[test]、无 benches,src/ 无*.test.*,正确性结论全部是代码可读证的推断,不是运行事实。
链路一:usbmuxd 设备枚举链路
| 能力项 | 品类基准预期 | 本项目现状 | 源码锚点 |
|---|---|---|---|
| 设备枚举 | 列出已连接设备 | ✅ 并行拉取名称与版本 | list_devices在 src-tauri/src/device.rs |
| 版本/连接类型采集 | 每设备取 iOS 版本 | ✅ lockdown 取两字段 | list_devices在 src-tauri/src/device.rs |
| 设备断连自动重连 | 线断后自动恢复 | ❌ 零监听,仅手动刷新 | get_usbmuxd在 src-tauri/src/device.rs |
| 枚举失败处理 | 定位到单台设备 | ❌ 只弹一条 toast | loadDevices在 src/Device.tsx |
这一行的 ❌ 写在 src-tauri/src/device.rs 里,整条链路没有任何监听调用,断连后只有一条错误映射:
let devs = usbmuxd.get_devices().await.map_err(|e| { AppError::Usbmuxd("Failed to list devices from usbmuxd".into(), e.to_string()) })?;这段证明了:设备枚举失败只映射为一次性的 AppError,无重选逻辑。
缺口断言:缺的是设备变更监听这一个具体行为。src-tauri/src/ 全部源码中没有任何 usbmuxd 事件订阅;设备掉线后 UI 停在旧状态,恢复只靠前端手动刷新(Ctrl+R,src/App.tsx 的refreshDevicesRef)。这是"没有实现层",不是有意不做——代码注释里也没有说明。
补丁入口:改 src-tauri/src/device.rs,在list_devices旁增加设备变更订阅并回调重选;usbmuxd 协议与事件暴露由外部 idevice crate(0.1.68)提供,本仓库只做胶水,协议层改动去上游仓库,属 owner 决策。
链路二:iOS 版本分叉与配对文件生成链路
| 能力项 | 品类基准预期 | 本项目现状 | 源码锚点 |
|---|---|---|---|
| 按版本选择配对链路 | 17.4 分叉 | ✅ 后端双链路并存 | pairing_file在 src-tauri/src/pairing.rs |
| 版本比较函数 | 真实版本解析 | ✅ major/minor 元组比较 | is_ios_version_below在 src-tauri/src/pairing.rs |
| 按版本裁剪错误建议 | 建议随版本过滤 | 🟡 正则剥标记 | getSuggestionBlock在 src/errors.tsx |
| 配对文件按设备缓存 | 按 udid 缓存 | ✅ 含失效重生成 | with_pairing_storage在 src-tauri/src/pairing.rs |
| 删除缓存后 host label 轮换 | 换标识重配对 | ✅ 生成新 label | delete_stored_rppairing在 src-tauri/src/pairing.rs |
这一行的 🟡 写在 src/errors.tsx 里,分叉是显式的:
.map((s) => s .replace(/^\[platform::.*?\]/, "") // TODO: actually check ios version .replace(/\[ios::.*?\]/g, "") .trim(), );这段证明了:错误建议的版本过滤未实现,现状是正则剥掉标记后继续展示。
缺口断言:后端is_ios_version_below是实有的,🟡 只落在前端错误建议这一侧——有"按版本裁剪"的通路(标记体系已建好),缺的是读取真实版本、按版本保留或剔除建议的核心行为。
补丁入口:改 src/errors.tsx 的getSuggestionBlock,数据源就是 src-tauri/src/device.rs 里DeviceInfo的version字段,纯前端改动,不涉上游。
链路三:keyring 存储与明文降级链路
| 能力项 | 品类基准预期 | 本项目现状 | 源码锚点 |
|---|---|---|---|
| 密码/Anisette 状态存储 | 系统钥匙串 | ✅ 默认 keyring | create_sideloading_storage在 src-tauri/src/secure_storage.rs |
| keyring 不可用降级 | 安全降级 | 🟡 明文文件仅 warn | create_sideloading_storage在 src-tauri/src/secure_storage.rs |
| 存储后端运行期重建 | 状态翻转时重建 | ✅ 检测翻转并重建 | with_pairing_storage在 src-tauri/src/pairing.rs |
| 降级事实对用户可见 | 设置页提示 | ❌ 仅一个强制关闭开关 | overrideKeyring在 src/pages/Settings.tsx |
| 跨机迁移 | 备份/恢复 | ❌ 无迁移机制 | — |
这一行的 🟡 写在 src-tauri/src/secure_storage.rs 里,降级是显式的:
if keyring_available() { Ok(Box::new(KeyringStorage::new("iloader".to_string()))) } else { warn!( "Keyring is not available, falling back to filesystem storage for sideloading data. This is insecure!" ); Ok(Box::new(FsStorage::new( app.path().app_data_dir().map_err(|e| { AppError::Misc(format!("Failed to get app data directory: {:?}", e)) })?, ))) }这段证明了:keyring 检测失败时静默降级为应用数据目录下的明文文件,唯一提示是一条 warn。
缺口断言:有通路但缺两个核心行为——降级路径不加密,且降级事实不通知前端,用户只能看到 src/pages/Settings.tsx 里一个overrideKeyring开关,看不到凭据实际落在哪。
补丁入口:改 src-tauri/src/secure_storage.rs 的降级分支,加本地加密或把"当前后端"经命令暴露给设置页;FsStorage/KeyringStorage实现在上游 isideload crate(0.5.2),本仓库只做调度与展示,属 owner 决策。
链路四:Apple 登录链与 Anisette 服务器列表
| 能力项 | 品类基准预期 | 本项目现状 | 源码锚点 |
|---|---|---|---|
| Apple ID 登录(含 2FA) | 主流程 | ✅ 120 秒验证码超时 | login在 src-tauri/src/account.rs |
| Anisette 服务器列表 | 动态发现 | 🟡 前端硬编码 4 个 | anisetteServers在 src/pages/Settings.tsx |
| 客户端指纹 | 由服务端下发 | 🟡 写死 macOS 27.0 覆盖 | login在 src-tauri/src/account.rs |
| Team 选择 | 多 Team 切换 | ❌ TODO 空壳 | login在 src-tauri/src/account.rs |
| Anisette 失败自动回退 | 自动换源 | ❌ 只能手动换源 | reset_anisette_state在 src-tauri/src/account.rs |
这一行的 🟡 写在 src/pages/Settings.tsx 顶部,注释自己承认了现状:
// TODO: Fetch anisette list from server // Old android servers // let anisetteServers = [ // ["ani.sidestore.io", "SideStore (.io)"], // ... ]; // new mac/windows servers let anisetteServers = [ ["mac-ani.jkcoxson.com", "Jkcoxson"], ["ani.iloader.app", "iloader"], ["new.ani.sidestore.io", "SideStore (.io)"], ["ani3.stikstore.app", "StikStore"], // works ~50% of the time, and doesn't work with SMS 2FA // ["ani.jkcoxson.com", "Jkcoxson (Windows)"], ];这段证明了:列表是静态数组,被注释掉的 Windows 服务器标注"works ~50% of the time",属估算,未经实测,本文不采信为事实。
缺口断言:动态发现这一层缺的是"从服务端拉取列表并做可用性回退"两个具体行为;Team 选择则连实现层都没有,只有// TODO: Team Selection一行注释,属没有实现层而非有意不做。
补丁入口:改 src/pages/Settings.tsx 把列表改为拉取;客户端指纹临时覆盖在 src-tauri/src/account.rs 的login里,等服务端上报 client info 后移除;Team 选择在 isideload 的会话层,本仓库只做胶水,跨仓库属 owner 决策。
链路五:IPA 安装进度回报链路
| 能力项 | 品类基准预期 | 本项目现状 | 源码锚点 |
|---|---|---|---|
| 任意 IPA 导入 | 选文件即装 | ✅ 委托 isideload | sideload在 src-tauri/src/sideload.rs |
| IPA 传输进度回报 | 百分比/速率 | 🟡 回调传 None | sideload在 src-tauri/src/sideload.rs |
| SideStore 三步编排 | 分阶段反馈 | ✅ download/install/pairing | install_sidestore_operation在 src-tauri/src/sideload.rs |
| 安装包缓存 | 不重复下载 | ❌ TODO 自证未做 | install_sidestore_operation在 src-tauri/src/sideload.rs |
| 失败步骤单步重试 | 从断点继续 | ❌ 失败即整体报错 | Operation在 src-tauri/src/operation.rs |
这一行的 🟡 写在 src-tauri/src/sideload.rs 里,缺口是一个参数:
let special = sideloader .get_mut() .install_app( &provider, app_path.into(), true, None::<fn(f32) -> std::future::Ready<()>>, Some(&device.pairing), ) .await?;这段证明了:install_app 的进度回调位置整个让给了 None,UI 拿不到百分比。
缺口断言:有通路但缺"进度值上行"这一个核心行为。src/components/operations.ts 里 sideload 操作只有install一个步骤,对照 SideStore 的 download/install/pairing 三步,IPA 导入在 UI 上只能看到"安装中"一个状态;下载侧download()先response.bytes()整体读进内存再写盘,无流式、无进度,上方// TODO: Cache & check version to avoid re-downloading自证缓存未做。
补丁入口:改 src-tauri/src/sideload.rs 把None换成Some回调,进度值经 src-tauri/src/operation.rs 的OperationUpdate新增载荷上行,前端 src/components/operations.ts 同步加字段;isideload 的install_app已预留回调签名,本仓库只做传值。
缺口拓扑:先补哪个,后补哪个
先补哪个由依赖顺序决定,不是由表格行数决定:
usbmuxd(宿主前提, alpha blocker) -> usbmuxd 枚举(list_devices) -> [A] Apple 登录链(isideload/idevice) -> Sideloader 安装 -> [B] 选中设备 + 配对文件(pairing.rs) -> 投放配对文件usbmuxd 是硬前提:宿主没有 usbmuxd,整条链断在get_usbmuxd,属 alpha blocker。CONTRIBUTING.md 写明核心侧载功能(认证、签名)在外部 isideload 仓库,修 A 链深层问题要跨仓库,属 owner 决策。普通缺口按依赖排:iOS 版本检测(链路二/五同形)、Anisette 动态列表(链路四)、IPA 进度回报(链路五)、Team 选择(链路四,isideload 侧,跨仓库)。
| 锚点 | 等级 | 对应源码改动 |
|---|---|---|
| v2.4.1(Cargo.toml 与 package.json 一致) | — | 全量现状基线 |
| 17.4 门(src-tauri/src/pairing.rs 注释 rppairing is 17.4+) | minor | RPPairing 缓存与 host label 轮换 |
| Anisette 临时覆盖(src-tauri/src/account.rs 注释 temporary) | trivial | 写死 macOS 27.0 客户端指纹 |
// TODO: Fetch anisette list from server(src/pages/Settings.tsx) | trivial | 静态列表未接入动态发现 |
仓库无 revision history 文档,上表是代码内可追溯的全部。
复现与排查:命令与失败链排序
bun i这条验证前端依赖可解析(脚本名取自 package.json)。
bunx tauri dev这条验证 Tauri 命令注册是否可通,对应 src-tauri/src/lib.rsgenerate_handler里的 22 个命令。
cargo build --manifest-path src-tauri/Cargo.toml这条验证 Rust 后端独立编译。
git clone https://gitcode.com/GitHub_Trending/iloa/iloader仅在你需要本地克隆时执行一次。
日志入口:设置页快捷键 Ctrl+L(macOS 为 Cmd+L,见 src/pages/Settings.tsx 的按键处理)打开 View Logs;文件日志按天轮转、保留 2 份(src-tauri/src/lib.rs 的RollingFileAppender),路径为%APPDATA%、~/Library/Application Support、~/.local/share下的me.nabdev.iloader/logs/(identifier 取自 tauri.conf.json)。
按源码失败链排序的排查清单:
- usbmuxd:宿主 usbmuxd 未安装或未运行,或设备物理未连接 → Windows 装 iTunes 或 Microsoft Store 的 Apple Devices,macOS 自带,Linux 确认 usbmuxd 在跑,再查物理连接 → 这是 src-tauri/src/device.rs
get_usbmuxd的第一条断链。 - device_coms / remote_pairing / lockdown_pairing:设备未点"信任这台电脑",或 17.4+ 设备首次配对未完成 → 解锁设备到主屏接受信任弹窗,确认设置了指纹密码,用 USB 而非无线 → 建议文案见 src/locales/en.json 的
trust与pairing键。 - keyring / storage:keyring 不可用已降级为明文文件 → 检查设置页 "Don't use keyring" 开关与系统钥匙串可用性 → 注意降级后不会自动回迁,需重启应用并恢复 keyring 才会重建存储后端(src-tauri/src/pairing.rs
with_pairing_storage)。 - anisette:服务器不可达 → 换设置页服务器,或点 Reset Anisette State(src-tauri/src/account.rs
reset_anisette_state)清本地状态再试 → 这是全链路唯一没有自动回退的环节。 - not_enough_app_ids / max_apps:免费账户 App ID 或设备注册数耗尽 → Apple 侧限制,工具无法绕过,只能删除旧 App ID 或旧设备注册。
- account_locked:Apple 锁号(-20209),需走 Apple 解锁流程;underage(1102)年龄不符,无解法。
引用须知
- 别被 ✅ 的密度误导。本仓库的 ✅ 集中在协议编排层——设备枚举、配对文件生成、错误分类、步骤状态机都是真的;真正的拖累在宿主前提(usbmuxd)、外部依赖(anisette 服务器可用性、isideload/idevice 协议层)与明确注释的 TODO(Team 选择、iOS 版本过滤、安装包缓存)。判断"能不能用"要看这三层,而不是数表格里的对勾。
- "未测量"不等于"不行"。仓库没有测试套件与基准,本文所有正确性结论都是代码可读证的推断,不是运行事实;唯一的量化口径(注释里 anisette 服务器 ~50% 可用率)属估算,未经实测,本文不采信为事实。引用时请把这两种表述分开。
- 本文的价值在于每一行都能落点到一个文件、一个函数、一条注释:src-tauri/src/pairing.rs 的 17.4 门、src-tauri/src/secure_storage.rs 的降级 warn、src/errors.tsx 的 TODO。下一个审计者不需要相信本文,只需要打开这些位置。
【免费下载链接】iloaderUser friendly sideloader项目地址: https://gitcode.com/GitHub_Trending/iloa/iloader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考