mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-16 04:20:30 +00:00
feat: migrate to cryptex DDI
This commit is contained in:
parent
94bc9e8cf3
commit
f76672838b
7 changed files with 3740 additions and 173 deletions
|
|
@ -44,35 +44,6 @@ private enum IdeviceBridge {
|
|||
return makeError(domain: domain, code: code, message: message)
|
||||
}
|
||||
|
||||
static func mappedFileData(atPath path: String, description: String) throws -> Data {
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: url, options: .mappedIfSafe)
|
||||
guard !data.isEmpty else {
|
||||
throw makeError(message: "\(description) is empty")
|
||||
}
|
||||
return data
|
||||
} catch let error as NSError {
|
||||
throw makeError(code: error.code, message: "Failed to read \(description): \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
static func uint64Value(from plist: plist_t?, fieldName: String) throws -> UInt64 {
|
||||
guard let plist else {
|
||||
throw makeError(message: "\(fieldName) was not returned by lockdownd")
|
||||
}
|
||||
|
||||
var value: UInt64 = 0
|
||||
plist_get_uint_val(plist, &value)
|
||||
|
||||
guard value != 0 else {
|
||||
throw makeError(message: "Failed to decode \(fieldName)")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
static func withTunnelHandles<T>(
|
||||
for context: JITEnableContext,
|
||||
_ body: (OpaquePointer, OpaquePointer) throws -> T
|
||||
|
|
@ -262,92 +233,32 @@ private enum IdeviceBridge {
|
|||
}
|
||||
|
||||
extension JITEnableContext {
|
||||
func getMountedDeviceCount() throws -> Int {
|
||||
func isCryptexDDIInstalled() throws -> Bool {
|
||||
try IdeviceBridge.withTunnelHandles(for: self) { adapter, handshake in
|
||||
try IdeviceBridge.withConnectedClient(
|
||||
fallback: "Failed to connect to image mounter",
|
||||
missingClientMessage: "Image mounter client was not created",
|
||||
connect: { image_mounter_connect_rsd(adapter, handshake, $0) },
|
||||
cleanup: { image_mounter_free($0) }
|
||||
) { client in
|
||||
var devices: UnsafeMutablePointer<plist_t?>?
|
||||
var deviceCount = 0
|
||||
if let ffiError = image_mounter_copy_devices(client, &devices, &deviceCount) {
|
||||
throw IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to fetch mounted devices")
|
||||
}
|
||||
|
||||
if let devices {
|
||||
for index in 0..<deviceCount {
|
||||
plist_free(devices[index])
|
||||
}
|
||||
idevice_data_free(
|
||||
UnsafeMutableRawPointer(devices).assumingMemoryBound(to: UInt8.self),
|
||||
UInt(deviceCount * MemoryLayout<plist_t?>.stride)
|
||||
)
|
||||
}
|
||||
|
||||
return deviceCount
|
||||
var installed: UnsafeMutablePointer<InstalledCryptexC>?
|
||||
if let ffiError = cryptexd_installed_ddi(adapter, handshake, &installed) {
|
||||
throw IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to query installed DDI cryptex")
|
||||
}
|
||||
|
||||
defer { cryptexd_free_installed_cryptex(installed) }
|
||||
return installed != nil
|
||||
}
|
||||
}
|
||||
|
||||
func mountPersonalDDI(withImagePath imagePath: String, trustcachePath: String, manifestPath: String) throws {
|
||||
let imageData = try IdeviceBridge.mappedFileData(atPath: imagePath, description: "developer disk image")
|
||||
let trustcacheData = try IdeviceBridge.mappedFileData(atPath: trustcachePath, description: "developer disk image trust cache")
|
||||
let manifestData = try IdeviceBridge.mappedFileData(atPath: manifestPath, description: "developer disk image manifest")
|
||||
func installCryptexDDI(from directoryPath: String) throws {
|
||||
var assets: OpaquePointer?
|
||||
if let ffiError = cryptex1_assets_load(directoryPath, &assets) {
|
||||
throw IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to load DDI cryptex assets")
|
||||
}
|
||||
|
||||
guard let assets else {
|
||||
throw IdeviceBridge.makeError(message: "DDI cryptex assets were not loaded")
|
||||
}
|
||||
defer { cryptex1_assets_free(assets) }
|
||||
|
||||
try IdeviceBridge.withTunnelHandles(for: self) { adapter, handshake in
|
||||
let uniqueChipID = try IdeviceBridge.withConnectedClient(
|
||||
fallback: "Failed to connect to lockdownd",
|
||||
missingClientMessage: "Lockdownd client was not created",
|
||||
connect: { lockdownd_connect_rsd(adapter, handshake, $0) },
|
||||
cleanup: { lockdownd_client_free($0) }
|
||||
) { lockdownClient in
|
||||
var uniqueChipIDPlist: plist_t?
|
||||
if let ffiError = lockdownd_get_value(lockdownClient, "UniqueChipID", nil, &uniqueChipIDPlist) {
|
||||
throw IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to query UniqueChipID")
|
||||
}
|
||||
|
||||
defer {
|
||||
if let uniqueChipIDPlist {
|
||||
plist_free(uniqueChipIDPlist)
|
||||
}
|
||||
}
|
||||
|
||||
return try IdeviceBridge.uint64Value(from: uniqueChipIDPlist, fieldName: "UniqueChipID")
|
||||
}
|
||||
|
||||
try IdeviceBridge.withConnectedClient(
|
||||
fallback: "Failed to connect to image mounter",
|
||||
missingClientMessage: "Image mounter client was not created",
|
||||
connect: { image_mounter_connect_rsd(adapter, handshake, $0) },
|
||||
cleanup: { image_mounter_free($0) }
|
||||
) { imageMounterClient in
|
||||
let ffiError = imageData.withUnsafeBytes { imageBuffer -> UnsafeMutablePointer<IdeviceFfiError>? in
|
||||
trustcacheData.withUnsafeBytes { trustcacheBuffer -> UnsafeMutablePointer<IdeviceFfiError>? in
|
||||
manifestData.withUnsafeBytes { manifestBuffer -> UnsafeMutablePointer<IdeviceFfiError>? in
|
||||
image_mounter_mount_personalized_with_callback_rsd(
|
||||
imageMounterClient,
|
||||
adapter,
|
||||
handshake,
|
||||
imageBuffer.bindMemory(to: UInt8.self).baseAddress,
|
||||
imageData.count,
|
||||
trustcacheBuffer.bindMemory(to: UInt8.self).baseAddress,
|
||||
trustcacheData.count,
|
||||
manifestBuffer.bindMemory(to: UInt8.self).baseAddress,
|
||||
manifestData.count,
|
||||
nil,
|
||||
uniqueChipID,
|
||||
progressCallback,
|
||||
nil
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let ffiError {
|
||||
throw IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to mount personalized DDI")
|
||||
}
|
||||
if let ffiError = cryptexd_install_ddi(adapter, handshake, assets, nil) {
|
||||
throw IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to install DDI cryptex")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import idevice
|
|||
final class MountingProgress: ObservableObject {
|
||||
static let shared = MountingProgress()
|
||||
|
||||
@Published private(set) var mountProgress: Double = 0.0
|
||||
@Published private(set) var mountingThread: Thread?
|
||||
@Published private(set) var coolisMounted: Bool = false
|
||||
|
||||
|
|
@ -42,13 +41,6 @@ final class MountingProgress: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func progressCallback(progress: size_t, total: size_t, context: UnsafeMutableRawPointer?) {
|
||||
let percentage = Double(progress) / Double(total) * 100.0
|
||||
DispatchQueue.main.async {
|
||||
self.mountProgress = percentage
|
||||
}
|
||||
}
|
||||
|
||||
func pubMount() {
|
||||
guard TunnelManager.shared.isConnected else { return }
|
||||
|
||||
|
|
@ -74,10 +66,8 @@ final class MountingProgress: ObservableObject {
|
|||
|
||||
let thread = Thread { [weak self] in
|
||||
guard let self else { return }
|
||||
let mountError = mountPersonalDDI(
|
||||
imagePath: URL.documentsDirectory.appendingPathComponent("DDI/Image.dmg").path,
|
||||
trustcachePath: URL.documentsDirectory.appendingPathComponent("DDI/Image.dmg.trustcache").path,
|
||||
manifestPath: URL.documentsDirectory.appendingPathComponent("DDI/BuildManifest.plist").path
|
||||
let mountError = installCryptexDDI(
|
||||
from: URL.documentsDirectory.appendingPathComponent("DDI_Cryptex").path
|
||||
)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ typealias RsdHandshakeHandle = OpaquePointer
|
|||
typealias ImageMounterHandle = OpaquePointer
|
||||
typealias LockdowndClientHandle = OpaquePointer
|
||||
|
||||
func progressCallback(progress: size_t, total: size_t, context: UnsafeMutableRawPointer?) {
|
||||
MountingProgress.shared.progressCallback(progress: progress, total: total, context: context)
|
||||
}
|
||||
|
||||
enum MountCheckResult {
|
||||
case mounted
|
||||
case notMounted
|
||||
|
|
@ -29,18 +25,17 @@ func isMounted() -> Bool {
|
|||
|
||||
func checkMountStatus() -> MountCheckResult {
|
||||
do {
|
||||
let result = try JITEnableContext.shared.getMountedDeviceCount()
|
||||
return result > 0 ? .mounted : .notMounted
|
||||
return try JITEnableContext.shared.isCryptexDDIInstalled() ? .mounted : .notMounted
|
||||
} catch {
|
||||
return .unreachable
|
||||
}
|
||||
}
|
||||
|
||||
func mountPersonalDDI(imagePath: String, trustcachePath: String, manifestPath: String) -> String? {
|
||||
func installCryptexDDI(from directoryPath: String) -> String? {
|
||||
do {
|
||||
try JITEnableContext.shared.mountPersonalDDI(withImagePath: imagePath, trustcachePath: trustcachePath, manifestPath: manifestPath)
|
||||
try JITEnableContext.shared.installCryptexDDI(from: directoryPath)
|
||||
} catch {
|
||||
LogManager.shared.addErrorLog("Failed to mount DDI: \(error.localizedDescription)")
|
||||
LogManager.shared.addErrorLog("Failed to install DDI cryptex: \(error.localizedDescription)")
|
||||
return error.localizedDescription
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -80,18 +80,28 @@ final class DeveloperDiskImageService {
|
|||
private static let downloadItems: [DDIDownloadItem] = [
|
||||
.init(
|
||||
name: "Build Manifest",
|
||||
relativePath: "DDI/BuildManifest.plist",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/BuildManifest.plist"
|
||||
relativePath: "DDI_Cryptex/BuildManifest.plist",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Cryptex/BuildManifest.plist"
|
||||
),
|
||||
.init(
|
||||
name: "Image",
|
||||
relativePath: "DDI/Image.dmg",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg"
|
||||
relativePath: "DDI_Cryptex/Image.dmg",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Cryptex/Image.dmg"
|
||||
),
|
||||
.init(
|
||||
name: "TrustCache",
|
||||
relativePath: "DDI/Image.dmg.trustcache",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg.trustcache"
|
||||
relativePath: "DDI_Cryptex/Image.dmg.trustcache",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Cryptex/Image.dmg.trustcache"
|
||||
),
|
||||
.init(
|
||||
name: "Cryptex Info",
|
||||
relativePath: "DDI_Cryptex/Image.dmg.cryptex_info",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Cryptex/Image.dmg.cryptex_info"
|
||||
),
|
||||
.init(
|
||||
name: "Root Hash",
|
||||
relativePath: "DDI_Cryptex/Image.dmg.root_hash",
|
||||
urlString: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Cryptex/Image.dmg.root_hash"
|
||||
)
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ final class TunnelManager: ObservableObject {
|
|||
}
|
||||
|
||||
private func mountDeveloperDiskImageIfNeeded() {
|
||||
let trustcachePath = URL.documentsDirectory.appendingPathComponent("DDI/Image.dmg.trustcache").path
|
||||
guard FileManager.default.fileExists(atPath: trustcachePath),
|
||||
let manifestPath = URL.documentsDirectory.appendingPathComponent("DDI_Cryptex/BuildManifest.plist").path
|
||||
guard FileManager.default.fileExists(atPath: manifestPath),
|
||||
!MountingProgress.shared.coolisMounted,
|
||||
MountingProgress.shared.mountingThread == nil else {
|
||||
return
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue