Files
Pocket-Poster/Pocket Poster/Controllers/SymHandler.swift
T
admin 4c7d968d49
Build IPA / Build unsigned IPA (push) Canceled after 0s
Release IPA / Build and release IPA (push) Canceled after 0s
Pocket Poster 3.3.0: worse_query port for iOS 26-27.2
- Replace bad_query with forcequitOS/worse_query (App Group based)
- Read the App Group from the code signature entitlements
- Add pathleak container enumeration and iOS 27 system container lookup
- Route descriptor/CarPlay writes through worse_query
- Add sign_personal.sh and POCKET_POSTER_SELFTEST diagnostics
- Keep bad_query as an iOS 26 fallback
2026-11-10 15:51:27 +08:00

121 lines
4.6 KiB
Swift

//
// SymHandler.swift
// Pocket Poster
//
// Created by lemin on 5/31/25.
//
import Foundation
class SymHandler {
// MARK: URL Getter Operations
static func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
static func getLCDocumentsDirectory() -> URL {
let lcPath = ProcessInfo.processInfo.environment["LC_HOME_PATH"]
if let lcPath = lcPath {
return URL(fileURLWithPath: "\(lcPath)/Documents")
}
return getDocumentsDirectory()
}
static func getPosterBoardHashURL() -> URL {
return getLCDocumentsDirectory().appendingPathComponent("NuggetPosterBoardHash")
}
static func getCarPlayHashURL() -> URL {
return getLCDocumentsDirectory().appendingPathComponent("NuggetCarPlayWallpaperHash")
}
private static func getSymlinkURL() -> URL {
return getLCDocumentsDirectory().appendingPathComponent(".Trash", conformingTo: .symbolicLink)
}
/// Prefer worse_query (iOS 26/27 sandbox escape); fall back to .Trash symlink exploit.
static var prefersSandboxEscape: Bool {
SandboxEscape.isAvailable
}
// MARK: Symlink Creation (legacy exploit for older iOS)
static func createSymlink(to path: String) throws -> URL {
// returns the url of the symlink
let symURL = getSymlinkURL()
cleanup()
// create the symlink to the hashed app folder
try FileManager.default.createSymbolicLink(at: symURL, withDestinationURL: URL(fileURLWithPath: path, isDirectory: true))
return symURL
}
static func createAppSymlink(for appHash: String) throws -> URL {
return try createSymlink(to: "/var/mobile/Containers/Data/Application/\(appHash)")
}
static func getExtensionVersion() -> String {
if #available(iOS 17.0, *) {
return "61"
}
return "59"
}
static func createDescriptorsSymlink(appHash: String, ext: String) throws -> URL {
// create a symlink directly to the descriptors
let extVer = SymHandler.getExtensionVersion()
print("linking to \(appHash)/Library/Application Support/PRBPosterExtensionDataStore/\(extVer)/Extensions/\(ext)/descriptors")
return try createAppSymlink(for: "\(appHash)/Library/Application Support/PRBPosterExtensionDataStore/\(extVer)/Extensions/\(ext)/descriptors")
}
// MARK: Direct write via worse_query
/// Copy descriptor folders into PosterBoard descriptors using the sandbox escape.
static func writeDescriptorsViaEscape(appHash: String, ext: String, descriptorFolders: [URL]) throws {
let destPath = SandboxEscape.descriptorsPath(appHash: appHash, ext: ext)
print("worse_query writing to \(destPath)")
// Make sure the extension data store exists (PosterBoard creates it on boot).
let containerRoot = SandboxEscape.applicationContainerPath(appHash: appHash)
try SandboxEscape.ensureDirectory(at: destPath, containerRoot: containerRoot)
let handle = try SandboxEscape.consume(path: destPath, create: true)
defer { handle.release() }
let fm = FileManager.default
for descr in descriptorFolders {
guard descr.lastPathComponent != "__MACOSX" else { continue }
let destName = UUID().uuidString
let destURL = URL(fileURLWithPath: destPath).appendingPathComponent(destName)
if fm.fileExists(atPath: destURL.path) {
try fm.removeItem(at: destURL)
}
try fm.copyItem(at: descr, to: destURL)
}
}
/// Copy files into an absolute directory under an app container via worse_query.
static func writeFilesViaEscape(toDirectory destPath: String, files: [URL]) throws {
try SandboxEscape.ensureDirectory(at: destPath)
let handle = try SandboxEscape.consume(path: destPath, create: true)
defer { handle.release() }
let fm = FileManager.default
for file in files {
let destURL = URL(fileURLWithPath: destPath).appendingPathComponent(file.lastPathComponent)
if fm.fileExists(atPath: destURL.path) {
try fm.removeItem(at: destURL)
}
try fm.copyItem(at: file, to: destURL)
}
}
static func cleanup() {
// remove the symlink if it exists
let symURL = getSymlinkURL()
// remove existing symlink
try? FileManager.default.removeItem(at: symURL)
}
}