diff --git a/README.md b/README.md index da13e86..1afcea2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ StikJIT **does not collect or store any user data.** - [X] Integrate [`em_proxy`](https://github.com/SideStore/em_proxy) - [X] Compile [`idevice`](https://github.com/jkcoxson/idevice) - [X] Implement heartbeat -- [ ] Mount the developer image +- [X] Mount the developer image - [X] Retrieve and filter installed apps by `get-task-allow` - [X] Enable JIT for selected apps - [X] Design and implement a user-friendly UI *(Done for now)* diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index 8d495d0..3bad8e3 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -15,6 +15,21 @@ struct HeartbeatApp: App { @State private var isPairing = false @State private var heartBeat = false @State private var error: Int32? = nil + @StateObject private var mount = MountingProgress.shared + + let urls: [String] = [ + "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/BuildManifest.plist", + "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg", + "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg.trustcache" + ] + + let outputDir: String = "DDI" + + let outputFiles: [String] = [ + "DDI/BuildManifest.plist", + "DDI/Image.dmg", + "DDI/Image.dmg.trustcache" + ] init() { let fixMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.fix_init(forOpeningContentTypes:asCopy:)))! @@ -30,7 +45,7 @@ struct HeartbeatApp: App { startProxy() if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) { Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in - if heartBeat { + if pubHeartBeat { isLoading = false timer.invalidate() } else { @@ -82,6 +97,17 @@ struct HeartbeatApp: App { } } else { MainTabView() + .onAppear() { + let fileManager = FileManager.default + for (index, urlString) in urls.enumerated() { + let destinationURL = URL.documentsDirectory.appendingPathComponent(outputFiles[index]) + if !fileManager.fileExists(atPath: destinationURL.path) { + downloadFile(from: urlString, to: destinationURL) + } + } + + mount.pubMount() + } } } } @@ -103,41 +129,86 @@ struct HeartbeatApp: App { } } } - - func startHeartbeatInBackground() { - let heartBeat = Thread { - let completionHandler: @convention(block) (Int32, String?) -> Void = { result, message in +} - if result == 0 { - print("Heartbeat started successfully: \(message ?? "")") - - self.heartBeat = true +var pubHeartBeat = false + +actor FunctionGuard { + private var runningTask: Task? + + func execute(_ work: @escaping @Sendable () -> T) async -> T { + if let task = runningTask { + return await task.value // If already running, wait for the existing result + } + + let task = Task.detached { work() } // Run in the background + runningTask = task + let result = await task.value + runningTask = nil + return result + } +} + + + +class MountingProgress: ObservableObject { + static var shared = MountingProgress() + @Published var mountProgress: Double = 0.0 + @Published var mountingThread: Thread? + @Published var coolisMounted: Bool = false + + func checkforMounted() { + DispatchQueue.main.async { + self.coolisMounted = isMounted() + } + } + + + func pubMount() { mount() } + + private func mount() { + self.coolisMounted = isMounted() + + let fileManager = FileManager.default + let pairingpath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path + + if pubHeartBeat, fileManager.fileExists(atPath: pairingpath), !isMounted() { + if let mountingThread { + mountingThread.cancel() + self.mountingThread = nil + } + + mountingThread = Thread { + let mount = 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, pairingFilePath: pairingpath) + + if !mount { + showAlert(title: "Error", message: "An Error Occured when Mounting the DDI", showOk: true, showTryAgain: true) { cool in + if cool { + self.mount() + } + } } else { - print("Error: \(result == InvalidHostID.rawValue ? "Invalid host ID, Please Selecr New Pairing File" : message ?? "") (Code: \(result))") - - showAlert(title: "HeartBeat Error", message: "\(message ?? "") (\(result))", showOk: true) { _ in - self.error = result + DispatchQueue.main.async { + self.coolisMounted = isMounted() } } } - JITEnableContext.shared().startHeartbeat(completionHandler: completionHandler, logger: nil) + mountingThread!.qualityOfService = .background + mountingThread!.name = "mounting" + mountingThread!.start() } - - heartBeat.qualityOfService = .background - heartBeat.name = "HeartBeat" - heartBeat.start() } - } - func startHeartbeatInBackground() { let heartBeat = Thread { let completionHandler: @convention(block) (Int32, String?) -> Void = { result, message in if result == 0 { print("Heartbeat started successfully: \(message ?? "")") + + pubHeartBeat = true } else { print("Error: \(message ?? "") (Code: \(result))") @@ -199,22 +270,65 @@ struct LoadingView: View { } } -public func showAlert(title: String, message: String, showOk: Bool, completion: @escaping (Bool) -> Void) { +public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, completion: @escaping (Bool) -> Void) { DispatchQueue.main.async { if let mainWindow = UIApplication.shared.windows.last { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - if showOk { + if showOk, !showTryAgain { let okAction = UIAlertAction(title: "OK", style: .default) { _ in completion(true) } alert.addAction(okAction) - } else { + } else if !showTryAgain { completion(false) } + if showTryAgain { + let tryAgainAction = UIAlertAction(title: "Try Again", style: .default) { _ in + completion(true) + } + + alert.addAction(tryAgainAction) + + let okAction = UIAlertAction(title: "OK", style: .cancel) { _ in + completion(false) + } + + alert.addAction(okAction) + } + mainWindow.rootViewController?.present(alert, animated: true, completion: nil) } } } + +func downloadFile(from urlString: String, to destinationURL: URL) { + let fileManager = FileManager.default + let documentsDirectory = URL.documentsDirectory + + + guard let url = URL(string: urlString) else { + print("Invalid URL: \(urlString)") + return + } + + let task = URLSession.shared.downloadTask(with: url) { (tempLocalUrl, response, error) in + guard let tempLocalUrl = tempLocalUrl, error == nil else { + print("Error downloading file from \(urlString): \(String(describing: error))") + return + } + + do { + // Move the downloaded file to the destination + try fileManager.createDirectory(at: destinationURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) + try fileManager.moveItem(at: tempLocalUrl, to: destinationURL) + print("Downloaded \(urlString) to \(destinationURL.path)") + } catch { + print("Error saving file: \(error)") + } + } + + task.resume() +} diff --git a/StikJIT/Utilities/mountDDI.swift b/StikJIT/Utilities/mountDDI.swift new file mode 100644 index 0000000..319003a --- /dev/null +++ b/StikJIT/Utilities/mountDDI.swift @@ -0,0 +1,225 @@ +// +// mountDDI.swift +// StikJIT +// +// Created by Stossy11 on 29/03/2025. +// + +import Foundation + +typealias IdevicePairingFile = OpaquePointer +typealias TcpProviderHandle = OpaquePointer +typealias CoreDeviceProxyHandle = OpaquePointer +typealias AdapterHandle = OpaquePointer +typealias ImageMounterHandle = OpaquePointer +typealias LockdowndClientHandle = OpaquePointer + +func progressCallback(progress: size_t, total: size_t, context: UnsafeMutableRawPointer?) { + let percentage = Double(progress) / Double(total) * 100.0 + print("Mounting progress: \(percentage)%") + + MountingProgress.shared.mountProgress = percentage +} + +func readFile(path: String) -> Data? { + guard let file = fopen(path, "rb") else { + perror("Failed to open file") + return nil + } + + fseek(file, 0, SEEK_END) + let fileSize = ftell(file) + fseek(file, 0, SEEK_SET) + + guard fileSize > 0 else { + fclose(file) + return nil + } + + var buffer = Data(count: fileSize) + buffer.withUnsafeMutableBytes { ptr in + fread(ptr.baseAddress, 1, fileSize, file) + } + + fclose(file) + return buffer +} + +func htons(_ value: UInt16) -> UInt16 { + return CFSwapInt16HostToBig(value) +} + +func isMounted() -> Bool { + var addr = sockaddr_in() + memset(&addr, 0, MemoryLayout.size) + addr.sin_family = sa_family_t(AF_INET) + addr.sin_port = htons(UInt16(LOCKDOWN_PORT)) + let sockaddrPointer = UnsafeRawPointer(&addr).bindMemory(to: sockaddr.self, capacity: 1) + + let pairingFilePath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path + + guard inet_pton(AF_INET, "10.7.0.1", &addr.sin_addr) == 1 else { + print("Invalid IP address") + return false + } + + // Read pairing file + var pairingFile: IdevicePairingFile? + let err = idevice_pairing_file_read(pairingFilePath, &pairingFile) + if err != IdeviceSuccess { + print("Failed to read pairing file: \(err)") + return false + } + + // Create TCP provider + var provider: TcpProviderHandle? + let providerError = idevice_tcp_provider_new(sockaddrPointer, pairingFile, "ImageMounterTest", &provider) + if providerError != IdeviceSuccess { + print("Failed to create TCP provider: \(providerError)") + return false + } + + // Connect to image mounter + var client: ImageMounterHandle? + let connectError = image_mounter_connect_tcp(provider, &client) + if connectError != IdeviceSuccess { + print("Failed to connect to image mounter: \(connectError)") + return false + } + tcp_provider_free(provider) + + print("wow") + + var devices: UnsafeMutableRawPointer? + var devicesLen: size_t = 0 + let listError = image_mounter_copy_devices(client, &devices, &devicesLen) + if listError == IdeviceSuccess { + let deviceList = devices?.assumingMemoryBound(to: plist_t.self) + var devices: [String] = [] + for i in 0..? + var xmlLength: Int32 = 0 + + // Use libplist function to convert to XML + plist_to_xml(device, &xmlData, &xmlLength) + if let xml = xmlData { + devices.append("\(xml)") + } + plist_mem_free(xmlData) + plist_free(device) + } + + image_mounter_free(client) + return devices.count != 0 + } else { + print("Failed to get device list: \(listError)") + return false + } +} + +func mountPersonalDDI(deviceIP: String = "10.7.0.1", imagePath: String, trustcachePath: String, manifestPath: String, pairingFilePath: String) -> Bool { + idevice_init_logger(Debug, Disabled, nil) + + print("Mounting \(imagePath) \(trustcachePath) \(manifestPath)") + + guard let image = readFile(path: imagePath), + let trustcache = readFile(path: trustcachePath), + let buildManifest = readFile(path: manifestPath) else { + print("Failed to read one or more files") + return false + } + + var addr = sockaddr_in() + memset(&addr, 0, MemoryLayout.size) + addr.sin_family = sa_family_t(AF_INET) + addr.sin_port = htons(UInt16(LOCKDOWN_PORT)) + let sockaddrPointer = UnsafeRawPointer(&addr).bindMemory(to: sockaddr.self, capacity: 1) + + guard inet_pton(AF_INET, deviceIP, &addr.sin_addr) == 1 else { + print("Invalid IP address") + return false + } + + var pairingFile: IdevicePairingFile? + let err = idevice_pairing_file_read(pairingFilePath.cString(using: .utf8), &pairingFile) + if err != IdeviceSuccess { + print("Failed to read pairing file: \(err)") + return false + } + + + var provider: TcpProviderHandle? + let providerError = idevice_tcp_provider_new(sockaddrPointer, pairingFile, "ImageMounterTest".cString(using: .utf8), &provider) + if providerError != IdeviceSuccess { + print("Failed to create TCP provider: \(providerError)") + return false + } + + + var pairingFile2: IdevicePairingFile? + let P2err = idevice_pairing_file_read(pairingFilePath.cString(using: .utf8), &pairingFile2) + if P2err != IdeviceSuccess { + print("Failed to read pairing file: \(err)") + return false + } + + var lockdownClient: LockdowndClientHandle? + guard lockdownd_connect_tcp(provider, &lockdownClient) == IdeviceSuccess else { + print("Failed to connect to lockdownd") + return false + } + + guard lockdownd_start_session(lockdownClient, pairingFile2) == IdeviceSuccess else { + print("Failed to start session") + return false + } + + var uniqueChipIDPlist: plist_t? + guard lockdownd_get_value(lockdownClient, "UniqueChipID".cString(using: .utf8), &uniqueChipIDPlist) == IdeviceSuccess else { + print("Failed to get UniqueChipID") + return false + } + + var uniqueChipID: UInt64 = 0 + plist_get_uint_val(uniqueChipIDPlist, &uniqueChipID) + plist_free(uniqueChipIDPlist) + print(uniqueChipID) + + + var mounterClient: ImageMounterHandle? + guard image_mounter_connect_tcp(provider, &mounterClient) == IdeviceSuccess else { + print("Failed to connect to image mounter") + return false + } + + let result = image.withUnsafeBytes { imagePtr in + trustcache.withUnsafeBytes { trustcachePtr in + buildManifest.withUnsafeBytes { manifestPtr in + image_mounter_mount_personalized_tcp_with_callback( + mounterClient, + provider, + imagePtr.baseAddress?.assumingMemoryBound(to: UInt8.self), + image.count, + trustcachePtr.baseAddress?.assumingMemoryBound(to: UInt8.self), + trustcache.count, + manifestPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), + buildManifest.count, + nil, + uniqueChipID, + progressCallback, + nil + ) + } + } + } + + if result != IdeviceSuccess { + print(result) + print("Failed to mount personalized image") + return false + } else { + print("Successfully mounted personalized image!") + return true + } +} diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index 2b0d5e9..efeb51e 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -52,6 +52,14 @@ struct HomeView: View { // Main action button - changes based on whether we have a pairing file Button(action: { + + if !isMounted() { + showAlert(title: "Device Not Mounted", message: "The Developer Disk Image has not been mounted yet. Check in settings for more information.", showOk: true) { cool in + // No Need + } + } + + if pairingFileExists { // Got a pairing file, show apps isShowingInstalledApps = true diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index ee77841..26fbd99 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -19,6 +19,8 @@ struct SettingsView: View { @State private var isImportingFile = false @State private var importProgress: Float = 0.0 + @StateObject private var mountProg = MountingProgress.shared + // Developer profile image URLs private let developerProfiles: [String: String] = [ "Blu": "https://github.com/0-Blu.png", @@ -169,6 +171,61 @@ struct SettingsView: View { .padding(.horizontal, 16) } + SettingsCard { + VStack(alignment: .leading, spacing: 20) { + Text("Developer Disk Image") + .font(.headline) + .foregroundColor(.primary) + .padding(.bottom, 4) + + if mountProg.coolisMounted { + HStack { + Spacer() + Text("✓ Developer Disk Image Mounted Sucessfully!") + .font(.system(.callout, design: .rounded)) + .foregroundColor(.green) + .padding(.vertical, 10) + .padding(.horizontal, 18) + .background(Color.green.opacity(0.1)) + .cornerRadius(10) + Spacer() + } + .padding(.top, 6) + } else { + HStack { + Text("Mounting Developer Disk Image...") + .font(.system(.caption, design: .rounded)) + .foregroundColor(.secondary) + Spacer() + Text("\(Int(MountingProgress.shared.mountProgress))%") + .font(.system(.caption, design: .rounded)) + .foregroundColor(.secondary) + } + + GeometryReader { geometry in + ZStack(alignment: .leading) { + RoundedRectangle(cornerRadius: 6) + .fill(Color(UIColor.tertiarySystemFill)) + .frame(height: 10) + + RoundedRectangle(cornerRadius: 6) + .fill(Color.green) + .frame(width: geometry.size.width * CGFloat(MountingProgress.shared.mountProgress), height: 10) + .animation(.linear(duration: 0.3), value: MountingProgress.shared.mountProgress) + } + } + .frame(height: 10) + } + + } + .padding(.vertical, 20) + .padding(.horizontal, 16) + .onAppear() { + mountProg.checkforMounted() + } + } + + // About section SettingsCard { VStack(alignment: .leading, spacing: 20) { diff --git a/StikJIT/idevice/idevice.h b/StikJIT/idevice/idevice.h index 27b8ab7..92a4e92 100644 --- a/StikJIT/idevice/idevice.h +++ b/StikJIT/idevice/idevice.h @@ -1,6 +1,10 @@ // Jackson Coxson // Bindings to idevice - https://github.com/jkcoxson/idevice + +#ifndef IDEVICE_H +#define IDEVICE_H + #include #include #include @@ -8,11 +12,9 @@ #include #include "plist.h" -#ifndef IDEVICE_H -#define IDEVICE_H - #define LOCKDOWN_PORT 62078 + typedef enum IdeviceErrorCode { IdeviceSuccess = 0, Socket = -1, @@ -102,6 +104,8 @@ typedef struct ImageMounterHandle ImageMounterHandle; typedef struct InstallationProxyClientHandle InstallationProxyClientHandle; +typedef struct LockdowndClientHandle LockdowndClientHandle; + /** * Opaque handle to a ProcessControlClient */ @@ -915,6 +919,145 @@ enum IdeviceErrorCode installation_proxy_get_apps(struct InstallationProxyClient */ void installation_proxy_client_free(struct InstallationProxyClientHandle *handle); +/** + * Connects to lockdownd service using TCP provider + * + * # Arguments + * * [`provider`] - A TcpProvider + * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +enum IdeviceErrorCode lockdownd_connect_tcp(struct TcpProviderHandle *provider, + struct LockdowndClientHandle **client); + +/** + * Connects to lockdownd service using Usbmuxd provider + * + * # Arguments + * * [`provider`] - A UsbmuxdProvider + * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +enum IdeviceErrorCode lockdownd_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, + struct LockdowndClientHandle **client); + +/** + * Creates a new LockdowndClient from an existing Idevice connection + * + * # Arguments + * * [`socket`] - An IdeviceSocket handle + * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +enum IdeviceErrorCode lockdownd_new(struct IdeviceHandle *socket, + struct LockdowndClientHandle **client); + +/** + * Starts a session with lockdownd + * + * # Arguments + * * `client` - A valid LockdowndClient handle + * * `pairing_file` - An IdevicePairingFile alocated by this library + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `pairing_file` must be a valid plist_t containing a pairing file + */ +enum IdeviceErrorCode lockdownd_start_session(struct LockdowndClientHandle *client, + struct IdevicePairingFile *pairing_file); + +/** + * Starts a service through lockdownd + * + * # Arguments + * * `client` - A valid LockdowndClient handle + * * `identifier` - The service identifier to start (null-terminated string) + * * `port` - Pointer to store the returned port number + * * `ssl` - Pointer to store whether SSL should be enabled + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `identifier` must be a valid null-terminated string + * `port` and `ssl` must be valid pointers + */ +enum IdeviceErrorCode lockdownd_start_service(struct LockdowndClientHandle *client, + const char *identifier, + uint16_t *port, + bool *ssl); + +/** + * Gets a value from lockdownd + * + * # Arguments + * * `client` - A valid LockdowndClient handle + * * `value` - The value to get (null-terminated string) + * * `out_plist` - Pointer to store the returned plist value + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `value` must be a valid null-terminated string + * `out_plist` must be a valid pointer to store the plist + */ +enum IdeviceErrorCode lockdownd_get_value(struct LockdowndClientHandle *client, + const char *value, + void **out_plist); + +/** + * Gets all values from lockdownd + * + * # Arguments + * * `client` - A valid LockdowndClient handle + * * `out_plist` - Pointer to store the returned plist dictionary + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `out_plist` must be a valid pointer to store the plist + */ +enum IdeviceErrorCode lockdownd_get_all_values(struct LockdowndClientHandle *client, + void **out_plist); + +/** + * Frees a LockdowndClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void lockdownd_client_free(struct LockdowndClientHandle *handle); + /** * Initializes the logger * @@ -1153,6 +1296,239 @@ enum IdeviceErrorCode image_mounter_mount_developer(struct ImageMounterHandle *c const uint8_t *signature, size_t signature_len); +/** + * Queries the personalization manifest from the device + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`image_type`] - The type of image to query + * * [`signature`] - Pointer to the signature data + * * [`signature_len`] - Length of the signature data + * * [`manifest`] - Will be set to point to the manifest data on success + * * [`manifest_len`] - Will be set to the length of the manifest data + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * All pointers must be valid and non-null + * `image_type` must be a valid null-terminated C string + */ +enum IdeviceErrorCode image_mounter_query_personalization_manifest(struct ImageMounterHandle *client, + const char *image_type, + const uint8_t *signature, + size_t signature_len, + uint8_t **manifest, + size_t *manifest_len); + +/** + * Queries the nonce from the device + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`personalized_image_type`] - The type of image to query (optional) + * * [`nonce`] - Will be set to point to the nonce data on success + * * [`nonce_len`] - Will be set to the length of the nonce data + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client`, `nonce`, and `nonce_len` must be valid pointers + * `personalized_image_type` can be NULL + */ +enum IdeviceErrorCode image_mounter_query_nonce(struct ImageMounterHandle *client, + const char *personalized_image_type, + uint8_t **nonce, + size_t *nonce_len); + +/** + * Queries personalization identifiers from the device + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`image_type`] - The type of image to query (optional) + * * [`identifiers`] - Will be set to point to the identifiers plist on success + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` and `identifiers` must be valid pointers + * `image_type` can be NULL + */ +enum IdeviceErrorCode image_mounter_query_personalization_identifiers(struct ImageMounterHandle *client, + const char *image_type, + void **identifiers); + +/** + * Rolls the personalization nonce + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +enum IdeviceErrorCode image_mounter_roll_personalization_nonce(struct ImageMounterHandle *client); + +/** + * Rolls the cryptex nonce + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +enum IdeviceErrorCode image_mounter_roll_cryptex_nonce(struct ImageMounterHandle *client); + +/** + * Mounts a personalized developer image + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`provider`] - A valid provider handle + * * [`image`] - Pointer to the image data + * * [`image_len`] - Length of the image data + * * [`trust_cache`] - Pointer to the trust cache data + * * [`trust_cache_len`] - Length of the trust cache data + * * [`build_manifest`] - Pointer to the build manifest data + * * [`build_manifest_len`] - Length of the build manifest data + * * [`info_plist`] - Pointer to info plist (optional) + * * [`unique_chip_id`] - The device's unique chip ID + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * All pointers must be valid (except optional ones which can be null) + */ +enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd(struct ImageMounterHandle *client, + struct UsbmuxdProviderHandle *provider, + const uint8_t *image, + size_t image_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const uint8_t *build_manifest, + size_t build_manifest_len, + const void *info_plist, + uint64_t unique_chip_id); + +/** + * Mounts a personalized developer image + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`provider`] - A valid provider handle + * * [`image`] - Pointer to the image data + * * [`image_len`] - Length of the image data + * * [`trust_cache`] - Pointer to the trust cache data + * * [`trust_cache_len`] - Length of the trust cache data + * * [`build_manifest`] - Pointer to the build manifest data + * * [`build_manifest_len`] - Length of the build manifest data + * * [`info_plist`] - Pointer to info plist (optional) + * * [`unique_chip_id`] - The device's unique chip ID + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * All pointers must be valid (except optional ones which can be null) + */ +enum IdeviceErrorCode image_mounter_mount_personalized_tcp(struct ImageMounterHandle *client, + struct TcpProviderHandle *provider, + const uint8_t *image, + size_t image_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const uint8_t *build_manifest, + size_t build_manifest_len, + const void *info_plist, + uint64_t unique_chip_id); + +/** + * Mounts a personalized developer image with progress callback + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`provider`] - A valid provider handle + * * [`image`] - Pointer to the image data + * * [`image_len`] - Length of the image data + * * [`trust_cache`] - Pointer to the trust cache data + * * [`trust_cache_len`] - Length of the trust cache data + * * [`build_manifest`] - Pointer to the build manifest data + * * [`build_manifest_len`] - Length of the build manifest data + * * [`info_plist`] - Pointer to info plist (optional) + * * [`unique_chip_id`] - The device's unique chip ID + * * [`callback`] - Progress callback function + * * [`context`] - User context to pass to callback + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * All pointers must be valid (except optional ones which can be null) + */ +enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd_with_callback(struct ImageMounterHandle *client, + struct UsbmuxdProviderHandle *provider, + const uint8_t *image, + size_t image_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const uint8_t *build_manifest, + size_t build_manifest_len, + const void *info_plist, + uint64_t unique_chip_id, + void (*callback)(size_t progress, + size_t total, + void *context), + void *context); + +/** + * Mounts a personalized developer image with progress callback + * + * # Arguments + * * [`client`] - A valid ImageMounter handle + * * [`provider`] - A valid provider handle + * * [`image`] - Pointer to the image data + * * [`image_len`] - Length of the image data + * * [`trust_cache`] - Pointer to the trust cache data + * * [`trust_cache_len`] - Length of the trust cache data + * * [`build_manifest`] - Pointer to the build manifest data + * * [`build_manifest_len`] - Length of the build manifest data + * * [`info_plist`] - Pointer to info plist (optional) + * * [`unique_chip_id`] - The device's unique chip ID + * * [`callback`] - Progress callback function + * * [`context`] - User context to pass to callback + * + * # Returns + * An error code indicating success or failure + * + * # Safety + * All pointers must be valid (except optional ones which can be null) + */ +enum IdeviceErrorCode image_mounter_mount_personalized_tcp_with_callback(struct ImageMounterHandle *client, + struct TcpProviderHandle *provider, + const uint8_t *image, + size_t image_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const uint8_t *build_manifest, + size_t build_manifest_len, + const void *info_plist, + uint64_t unique_chip_id, + void (*callback)(size_t progress, + size_t total, + void *context), + void *context); + /** * Reads a pairing file from the specified path * @@ -1633,4 +2009,4 @@ enum IdeviceErrorCode idevice_usbmuxd_unix_addr_new(const char *addr, */ void idevice_usbmuxd_addr_free(struct UsbmuxdAddrHandle *usbmuxd_addr); -#endif /* IDEVICE_H */ +#endif diff --git a/StikJIT/idevice/libidevice_ffi.a b/StikJIT/idevice/libidevice_ffi.a index c8d1df2..731fc15 100644 Binary files a/StikJIT/idevice/libidevice_ffi.a and b/StikJIT/idevice/libidevice_ffi.a differ