ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

vphone-cli 内核补丁深度解析:JB-23b 清除 thread_set_state 权限标志位,为 Frida Stalker 追踪现有线程铺路

vphone-cli 内核补丁深度解析:JB-23b 清除 thread_set_state 权限标志位,为 Frida Stalker 追踪现有线程铺路 vphone-cli 内核补丁深度解析JB-23b 清除 thread_set_state 权限标志位为 Frida Stalker 追踪现有线程铺路【免费下载链接】vphone-cli项目地址: https://gitcode.com/GitHub_Trending/vp/vphone-cli导读本文以 vphone-cli 项目中research/kernel_patch_jb/patch_thread_set_state.md文档为主线深入讲解可选的 Frida Stalker 内核补丁JB-23bpatchThreadSetStateEntitlementFlag它通过清除用户态 setter 传入thread_set_state_internal()的标志位中的TSSF_CHECK_ENTITLEMENTbit 90x200使 Frida Stalker 能够通过thread_set_state重写现有线程的核心寄存器而不再触发GUARD_TYPE_MACH_PORT致命异常。读完本文你将掌握该补丁的问题背景、设计取舍、语义化定位流程、Swift 源码级实现细节、静态验证方法以及如何通过--frida开关在 vphone-cli 固件流水线中启用它。一、补丁定位可选的 Frida Stalker 内核放宽ScopeJB-23b 属于 vphone-cli JB 内核补丁集中按需opt-in启用的一类。它在固件修补阶段仅在显式传入--frida时才会被发射对应 KernelJBPatcher.swift 中的applyFrida开关基线 JB/EXP 固件在不开启时与关闭状态逐字节一致26.4 固件在不带--frida时发射 83 条 kernel-jb 记录带上--frida后为 85 条83 条基线 2 条thread_set_state 2 条vm_map_delete后者即配套补丁 JB-25c。从源码结构可以确认findAll()中 Frida 相关的两条补丁被显式包裹在if applyFrida分支内KernelJBPatcher.swift// Opt-in Frida Stalker support (--frida): existing-thread follow // (thread_set_state) repeated VM_PROT_COPY overwrite (vm_map_delete). if applyFrida { patchThreadSetStateEntitlementFlag() patchVmMapDeleteImmutableCode() }该开关在命令行层面对应patch-firmware与patch-component两个子命令的--frida标志VPhoneCLI.swift 与 VPhoneCLI.swift其帮助文本明确指出适用范围为 jb/exp only。二、问题根源thread_set_state的 entitlement 检查如何杀死被 Frida 跟随的线程Frida Stalker 的跟随已有线程existing-thread follow能力本质上是通过 MIG 例程thread_set_state重写该线程的核心 CPU 寄存器来实现的。这个 MIG 例程最终会落到 XNU 内核的thread_set_state_from_user()源码位于osfmk/kern/thread_act.c其调用链为thread_set_state_from_user(...) - thread_set_state_internal(..., TSSF_TRANSLATE_TO_USER | TSSF_CHECK_ENTITLEMENT); // 0x1 | 0x200 0x201关键点在于第二个标志TSSF_CHECK_ENTITLEMENT 0x200thread_set_state_internal()其中内联了thread_set_state_allowed()只要发现传入的标志位携带TSSF_CHECK_ENTITLEMENT就会要求发起调用的 task 持有 entitlementcom.apple.private.thread-set-state。而 Frida 的目标进程通常并不具备该 entitlement于是内核以GUARD_TYPE_MACH_PORT / THREAD_SET_STATE为原因抛出致命保护异常并终止该进程。也就是说Stalker 想动的线程一旦被内核判定没有资格改写自己就会被直接杀掉这正是需要内核层面干预的根因。三、设计取舍清除标志位而不是 NOP 掉检查分支面对这个 entitlement 检查直觉上的做法是在thread_set_state_allowed()内部把entitlement 校验失败即拒绝的分支 NOP 掉。但 JB-23b 选择了更窄、更贴合源码语义的方案——不去动检查逻辑本身而是清除用户态 setter 传入的标志位中的TSSF_CHECK_ENTITLEMENTmov w6, #0x201 // before (TSSF_TRANSLATE_TO_USER | TSSF_CHECK_ENTITLEMENT) mov w6, #0x1 // after (TSSF_TRANSLATE_TO_USER only)按 AArch64 调用约定w6是传给thread_set_state_internal的第 7 个参数即flags。只清除 bit 90x200带来的效果是精确的保留TSSF_TRANSLATE_TO_USER0x1from_user路径上的用户指针翻译行为完全不变两个受 entitlement 门控的分支都放行thread_set_state_allowed()内核对核心寄存器、致命 PAC 调试两条子句都做flags TSSF_CHECK_ENTITLEMENT判断而该函数的第一条测试指令正是tbnz w6, #9——现在这个分支不再被触发非 mach-exception 的线程会直接以 allowed 返回TH_IN_MACH_EXCEPTION防护依然生效这个独立于该标志位的保护不会被误伤安全性得以保留。相比直接改写检查分支这种在请求检查的精确调用点上关闭检查需求的做法修改面最小也更符合源代码的语义结构。四、语义化定位流程Reveal Procedure该补丁最大的特点是零硬编码——不写死任何文件偏移、VA、寄存器编号或预汇编字节全部依赖语义特征在目标内核中定位。完整流程如下对应 patch_thread_set_state.md 的 Reveal Procedure 一节锚定 entitlement 字符串findString(com.apple.private.thread-set-state)找到字符串在文件中的偏移收集所有引用并确认单一宿主函数findStringRefs返回全部 ADRPADD 交叉引用按findFunctionStart分组后要求它们全部归结到同一个函数——即thread_set_state_internalentitlement 检查被内联在其中再通过findFuncEnd恢复[fnStart, fnEnd)边界扫描直达分支在代码区间内寻找目标落在[fnStart - 0x10, fnEnd)内的直接b/bl- 0x10是为内部函数入口预留的少量前置跳板空间回扫找 setter对每个这样的调用点向前最多回扫 8 条指令寻找mov w6, #0x201w6即 flags若w6在此之前被其他指令先写过则放弃该候选重编码并验证用ARM64Encoder.encodeMovzW将每个 setter 改写为mov w6, #0x1再用 Capstone 反汇编验证重编码结果确实是mov/movz w6, #1才发射。这套流程在 KernelJBPatchThreadSetState.swift 中有完整的一一对应实现。五、源码级实现剖析KernelJBPatchThreadSetState.swift补丁入口为patchThreadSetStateEntitlementFlag()KernelJBPatchThreadSetState.swift。我们按关键段落拆解其内部逻辑。5.1 常量定义与 fail-open 前置检查private static let tssEntitlement com.apple.private.thread-set-state // TSSF_TRANSLATE_TO_USER (0x1) | TSSF_CHECK_ENTITLEMENT (0x200). private static let tssFlagsFromUser: Int64 0x201 private static let tssFlagsCleared: UInt16 0x1tssFlagsFromUser 0x201setter 原本传入的标志位tssFlagsCleared 0x1清除 bit 9 后的目标值只保留TSSF_TRANSLATE_TO_USER。实现遵循**失败即跳过fail-open no-op**原则字符串缺失、引用未收敛到单一函数、找不到 setter 等任何异常情况下都只是记录日志并return true跳过该补丁绝不修改任何字节。这保证了在不具备该形状的内核上运行是完全无害的。5.2 定位宿主函数与边界恢复let refs findStringRefs(strOff) let starts Set(refs.compactMap { findFunctionStart($0.adrpOff) }) guard starts.count 1, let fnStart starts.first else { ... return true } let fnEnd findFuncEnd(fnStart, maxSize: 0x1000)findFunctionStartKernelPatcherBase.swift向后扫描PACIBSP或STP x29, x30, [sp, #imm]序言指令来恢复函数起点findFuncEndKernelJBPatcherBase.swift向前扫描下一个PACIBSP边界作为函数终点。5.3 扫描直达分支并回找 setterguard let branch disasAt(off), branch.mnemonic b || branch.mnemonic bl, let target branchTargetFileOffset(branch), target fnStart - 0x10, target fnEnd else { continue } if let setter findFlagSetterBefore(off, funcFloor: range.start) { setterOffsets.append(setter) }其中findFlagSetterBefore从分支指令向前回扫最多 8 条指令KernelJBPatchThreadSetState.swiftvar off branchOff - 4 var steps 0 while off funcFloor, steps 8 { defer { off - 4; steps 1 } guard let insn disasAt(off) else { continue } guard insn.mnemonic mov || insn.mnemonic movz else { continue } guard let ops insn.aarch64?.operands, ops.count 2, ops[0].type AARCH64_OP_REG, ops[1].type AARCH64_OP_IMM, disasm.firstRegisterName(insn) w6 else { continue } return ops[1].imm Self.tssFlagsFromUser ? off : nil } return nil注意这里的三重约束指令必须是mov/movz、目的寄存器必须是w6、立即数必须恰好等于0x201。如果w6在这 8 条指令窗口内被其他指令先写过立即返回 nil 放弃候选避免误判。5.4 重编码、Capstone 回环验证与发射for setterOff in unique { guard let orig disasAt(setterOff), let rd wRegisterNumber(orig), let bytes ARM64Encoder.encodeMovzW(rd: rd, imm16: Self.tssFlagsCleared), let check disasm.disassembleOne(bytes, at: UInt64(setterOff)), (check.mnemonic mov || check.mnemonic movz), let ops check.aarch64?.operands, ops.count 2, ops[1].type AARCH64_OP_IMM, ops[1].imm Int64(Self.tssFlagsCleared) else { ... return false } emit(setterOff, bytes, ...) }发射前的验证链条非常严谨先通过ARM64Encoder.encodeMovzWARM64Encoder.swift编码格式[31]0(sf)、[30:23]0b10100101、hw、imm16、Rd生成新字节再用 Capstone 反汇编回环校验——助记符必须是mov/movz、第二操作数必须是立即数且值等于0x1。任何一步失败都会返回falsefail closed而不是带病发射。每条记录通过emit()KernelPatcherBase.swift落盘为PatchRecord包含 before/after 反汇编、文件偏移、虚拟地址与描述补丁 ID 为kernelcache_frida.thread_set_state_entitlement_flag。六、静态验证26.4 research 内核上的确定性输出文档给出了明确的验证基准。内核为ipsws/c0ecdb4b…/kernelcache.research.vphone600UUIDBCD06230-CCBE-8E48-50FF-D9C166D83CD5。执行patch-component --component kernel-jb --target-os 26.4 --frida该子命令为测试/诊断用途生产链路走patch-firmware --variant jb见 VPhoneCLI.swift后恰好发射两条kernelcache_frida.thread_set_state_entitlement_flag记录0x01D95720: mov w6, #0x201 - mov w6, #0x1 0x01D9594C: mov w6, #0x201 - mov w6, #0x1对应的虚拟地址为0xfffffe0008d99720/0xfffffe0008d9994c分别是thread_set_state_from_user的 setter 与内联的act_set_state_from_usersetter两者共同喂给位于0xfffffe0008d5c170的同一个thread_set_state_internal。而不带--frida时这类记录的发射数为零与基线逐字节一致的设计承诺吻合。从源码结构还可以确认patch-component的--frida通过patcher.applyFrida fridaVPhoneCLI.swift直接传导到 KernelJBPatcher.swift 的 Frida 分支从而复现流水线行为。七、配套补丁JB-25cpatchVmMapDeleteImmutableCodeJB-23b 并非孤立的改动。文档明确指出Frida Stalker 除了需要跟随现有线程其反复改写代码区的行为还依赖VM_PROT_COPY覆盖路径因此必须同时应用 JB-25cpatch_vm_map_delete_immutable_code详见 patch_vm_map_delete_immutable_code.md。两者在--frida下成对生效KernelJBPatcher.swiftJB-23b本文解决thread_set_state的 entitlement 门控实现 existing-thread followJB-25c解决 Stalker 写入后翻转write-then-flip在 CSM 设备上留下的永久vm_map_entry导致KERN_PROTECTION_FAILURE的问题——将不可变代码测试从当前保护bit 9重定向到最大保护bit 13即允许调试器撤销一个具备执行能力的映射从而支持重复插桩re-instrumentation。记录计数同样印证了这一点--frida下 87 条 83 条基线 2 条 thread_set_state 2 条 vm_map_delete。八、补充说明与版本依赖Notes文档还记录了一个值得注意的版本事实26.4 research 内核编译后的thread_set_state_allowed()没有tss_should_crash提前退出路径而是直接进入tbnz w6, #9测试。这意味着 XNU DEVELOPMENT 构建通常提供的 boot-arg 绕过手段在此内核上不可用必须依赖代码补丁——这正是 JB-23b 存在的必要性。此外补丁整体遵循语义定位、零硬编码的工程原则没有文件偏移、VA、寄存器编号或预汇编字节被硬编码这一点也在源码注释与文档 Reveal Procedure 中被反复强调遇到形状不符的内核会 fail-open 跳过且不改字节并且仅在--frida下运行最大化降低了基线固件的改动面。九、如何查看与验证仓库中与本补丁直接相关的可引用材料设计文档patch_thread_set_state.md本文主体实现源码KernelJBPatchThreadSetState.swift编排与开关KernelJBPatcher.swift、VPhoneCLI.swift、FirmwarePipeline.swift基础设施KernelPatcherBase.swift、KernelJBPatcherBase.swift、ARM64Encoder.swift配套补丁patch_vm_map_delete_immutable_code.md。验证补丁发射情况时可在 26.4 内核上运行patch-component --component kernel-jb --target-os 26.4 --frida观察是否恰好输出两条thread_set_state_entitlement_flag记录同一内核不带--frida应输出零条。由于该子命令定位为测试/诊断工具生产环境启用 Frida 内核放宽应通过patch-firmware --variant jb --frida完成且仅适用于 jb/exp 变体。【免费下载链接】vphone-cli项目地址: https://gitcode.com/GitHub_Trending/vp/vphone-cli创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表