mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-04-05 04:59:05 +08:00
Run SwiftFormat on firmware patcher Remove legacy Python firmware patchers Fix compare pipeline pyimg4 PATH handling Restore Python patchers and prefer fresh restore Update BinaryBuffer.swift Avoid double scanning in patcher apply Prefer Python TXM site before fallback Retarget TXM trustcache finder for 26.1 Remove legacy Python firmware patchers Fail fast on nested virtualization hosts Return nonzero on fatal boot startup Add amfidont helper for signed boot binary Stage AMFI boot args for next host reboot Add host preflight for boot entitlements Fail fast when boot entitlements are unavailable Switch firmware patch targets to Swift CLI Record real Swift firmware parity results Verify Swift firmware pipeline end-to-end parity Fix Swift firmware pipeline JB dry-run
163 lines
6.6 KiB
Swift
163 lines
6.6 KiB
Swift
// KernelJBPatchTaskConversion.swift — JB kernel patch: Task conversion eval bypass
|
|
//
|
|
// Historical note: derived from the legacy Python firmware patcher during the Swift migration.
|
|
//
|
|
// Strategy (fast raw scanner):
|
|
// Locate the unique guard site in _task_conversion_eval_internal that performs:
|
|
// ADRP Xn, <global> ; [off - 8] — loads global task-conversion table
|
|
// LDR Xn, [Xn] ; [off - 4] — dereferences it
|
|
// CMP Xn, X0 ; [off + 0] — compare task pointer against X0
|
|
// B.EQ <skip1> ; [off + 4]
|
|
// CMP Xn, X1 ; [off + 8] — compare against X1
|
|
// B.EQ <skip2> ; [off + 12]
|
|
// MOV X19, X0 ; [off + 16]
|
|
// MOV X0, X1 ; [off + 20]
|
|
// BL <callee> ; [off + 24]
|
|
// CBZ/CBNZ W0, ... ; [off + 28]
|
|
// Patch: replace CMP Xn, X0 with CMP XZR, XZR so the equality check always passes.
|
|
|
|
import Foundation
|
|
|
|
extension KernelJBPatcher {
|
|
/// Task conversion eval bypass: patch the guard CMP to always be equal.
|
|
@discardableResult
|
|
func patchTaskConversionEvalInternal() -> Bool {
|
|
log("\n[JB] task_conversion_eval_internal: cmp xzr,xzr")
|
|
|
|
guard let range = kernTextRange ?? codeRanges.first.map({ ($0.start, $0.end) }) else {
|
|
return false
|
|
}
|
|
let (ks, ke) = range
|
|
|
|
let candidates = collectTaskConversionCandidates(start: ks, end: ke)
|
|
|
|
guard candidates.count == 1 else {
|
|
log(" [-] expected 1 task-conversion guard site, found \(candidates.count)")
|
|
return false
|
|
}
|
|
|
|
let site = candidates[0]
|
|
let va = fileOffsetToVA(site)
|
|
emit(site, ARM64.cmpXzrXzr,
|
|
patchID: "task_conversion_eval",
|
|
virtualAddress: va,
|
|
description: "cmp xzr,xzr [_task_conversion_eval_internal]")
|
|
return true
|
|
}
|
|
|
|
// MARK: - Private scanner
|
|
|
|
private func collectTaskConversionCandidates(start: Int, end: Int) -> [Int] {
|
|
// Derived masks — no hardcoded opcode bytes:
|
|
// CMP Xn, X0 = SUBS XZR, Xn, X0 → bits [31:21]=1110_1011_000, [20:16]=X0=00000,
|
|
// [15:10]=000000, [9:5]=Rn, [4:0]=11111(XZR)
|
|
// Mask covers the fixed opcode and X0 operand; leaves Rn free.
|
|
let cmpXnX0Mask: UInt32 = 0xFFFF_FC1F
|
|
let cmpXnX0Val: UInt32 = 0xEB00_001F // cmp Xn, X0 — Rn wildcard
|
|
|
|
// CMP Xn, X1 = SUBS XZR, Xn, X1 → Rm=X1=00001
|
|
let cmpXnX1Mask: UInt32 = 0xFFFF_FC1F
|
|
let cmpXnX1Val: UInt32 = 0xEB01_001F // cmp Xn, X1 — Rn wildcard
|
|
|
|
// B.EQ #offset → bits[31:24]=0101_0100, bit[4]=0, bits[3:0]=0000 (EQ cond)
|
|
let beqMask: UInt32 = 0xFF00_001F
|
|
let beqVal: UInt32 = 0x5400_0000 // b.eq with any imm19
|
|
|
|
// LDR Xd, [Xn] (unsigned offset, size=3):
|
|
// bits [31:22] fixed = 0xF94 (size=11, V=0, opc=01, class=01);
|
|
// bits [21:10] = imm12, bits [9:5] = Rn, bits [4:0] = Rt — all variable.
|
|
let ldrXUnsignedMask: UInt32 = 0xFFC0_0000 // leaves imm12, Rn, Rt free
|
|
let ldrXUnsignedVal: UInt32 = 0xF940_0000
|
|
|
|
// ADRP: bit[31]=1, bits[28:24]=10000
|
|
let adrpMask: UInt32 = 0x9F00_0000
|
|
let adrpVal: UInt32 = 0x9000_0000
|
|
|
|
// MOV X19, X0 = ORR X19, XZR, X0
|
|
let movX19X0: UInt32 = 0xAA00_03F3
|
|
// MOV X0, X1 = ORR X0, XZR, X1
|
|
let movX0X1: UInt32 = 0xAA01_03E0
|
|
|
|
// BL mask
|
|
let blMask: UInt32 = 0xFC00_0000
|
|
let blVal: UInt32 = 0x9400_0000
|
|
|
|
// CBZ/CBNZ W (32-bit): bits[31]=0, bits[30:25]=011010 / 011011
|
|
let cbzWMask: UInt32 = 0x7F00_0000
|
|
let cbzWVal: UInt32 = 0x3400_0000 // CBZ W
|
|
let cbnzWVal: UInt32 = 0x3500_0000 // CBNZ W
|
|
|
|
var out: [Int] = []
|
|
|
|
var off = start + 8
|
|
while off < end - 28 {
|
|
defer { off += 4 }
|
|
|
|
// [off]: CMP Xn, X0
|
|
let i0 = buffer.readU32(at: off)
|
|
guard (i0 & cmpXnX0Mask) == cmpXnX0Val else { continue }
|
|
let cmpRn = (i0 >> 5) & 0x1F // the register being compared
|
|
|
|
// [off - 4]: LDR Xn, [Xn] (load into cmpRn from cmpRn)
|
|
let prev = buffer.readU32(at: off - 4)
|
|
guard (prev & ldrXUnsignedMask) == ldrXUnsignedVal else { continue }
|
|
let pRt = prev & 0x1F
|
|
let pRn = (prev >> 5) & 0x1F
|
|
guard pRt == cmpRn, pRn == cmpRn else { continue }
|
|
|
|
// [off + 4]: B.EQ
|
|
let i1 = buffer.readU32(at: off + 4)
|
|
guard (i1 & beqMask) == beqVal else { continue }
|
|
|
|
// [off + 8]: CMP Xn, X1 (same register)
|
|
let i2 = buffer.readU32(at: off + 8)
|
|
guard (i2 & cmpXnX1Mask) == cmpXnX1Val else { continue }
|
|
guard ((i2 >> 5) & 0x1F) == cmpRn else { continue }
|
|
|
|
// [off + 12]: B.EQ
|
|
let i3 = buffer.readU32(at: off + 12)
|
|
guard (i3 & beqMask) == beqVal else { continue }
|
|
|
|
// Context safety: ADRP at [off - 8] for same register
|
|
let p2 = buffer.readU32(at: off - 8)
|
|
guard (p2 & adrpMask) == adrpVal else { continue }
|
|
guard (p2 & 0x1F) == cmpRn else { continue }
|
|
|
|
// [off + 16]: MOV X19, X0
|
|
guard buffer.readU32(at: off + 16) == movX19X0 else { continue }
|
|
|
|
// [off + 20]: MOV X0, X1
|
|
guard buffer.readU32(at: off + 20) == movX0X1 else { continue }
|
|
|
|
// [off + 24]: BL
|
|
let i6 = buffer.readU32(at: off + 24)
|
|
guard (i6 & blMask) == blVal else { continue }
|
|
|
|
// [off + 28]: CBZ or CBNZ W0
|
|
let i7 = buffer.readU32(at: off + 28)
|
|
let op7 = i7 & cbzWMask
|
|
guard op7 == cbzWVal || op7 == cbnzWVal else { continue }
|
|
guard (i7 & 0x1F) == 0 else { continue } // must be W0
|
|
|
|
// B.EQ targets must be forward and nearby (within same function)
|
|
guard let t1 = decodeBEQTarget(insn: i1, at: off + 4) else { continue }
|
|
guard let t2 = decodeBEQTarget(insn: i3, at: off + 12) else { continue }
|
|
guard t1 > off, t2 > off else { continue }
|
|
guard (t1 - off) <= 0x200, (t2 - off) <= 0x200 else { continue }
|
|
|
|
out.append(off)
|
|
}
|
|
return out
|
|
}
|
|
|
|
/// Decode a B.cond target offset from an instruction at `pc`.
|
|
private func decodeBEQTarget(insn: UInt32, at pc: Int) -> Int? {
|
|
// B.cond: bits[31:24] = 0x54, bits[23:5] = imm19, bits[4] = 0, bits[3:0] = cond
|
|
guard (insn & 0xFF00_001E) == 0x5400_0000 else { return nil }
|
|
let imm19 = (insn >> 5) & 0x7FFFF
|
|
// Sign-extend 19 bits
|
|
let signedImm = Int32(bitPattern: imm19 << 13) >> 13
|
|
return pc + Int(signedImm) * 4
|
|
}
|
|
}
|