mirror of
https://github.com/StikDebug/StikJIT.git
synced 2026-10-15 20:10:31 +00:00
123 lines
4.6 KiB
Swift
123 lines
4.6 KiB
Swift
import Foundation
|
|
@_implementationOnly import idevice
|
|
|
|
final class DDISession {
|
|
|
|
private struct Tunnel {
|
|
var adapter: OpaquePointer?
|
|
var handshake: OpaquePointer?
|
|
func free() {
|
|
if let handshake { rsd_handshake_free(handshake) }
|
|
if let adapter { adapter_free(adapter) }
|
|
}
|
|
}
|
|
|
|
private let pairingFilePath: String
|
|
private let configuration: StikJIT.Configuration
|
|
|
|
init(pairingFilePath: String, configuration: StikJIT.Configuration) {
|
|
self.pairingFilePath = pairingFilePath
|
|
self.configuration = configuration
|
|
}
|
|
|
|
func isMounted() throws -> Bool {
|
|
let tunnel = try makeTunnel()
|
|
defer { tunnel.free() }
|
|
|
|
var installed: UnsafeMutablePointer<InstalledCryptexC>?
|
|
let cryptexError = cryptexd_installed_ddi(tunnel.adapter, tunnel.handshake, &installed)
|
|
defer { cryptexd_free_installed_cryptex(installed) }
|
|
if installed != nil {
|
|
if let cryptexError { idevice_error_free(cryptexError) }
|
|
return true
|
|
}
|
|
|
|
if (try? isLegacyDDIMounted(over: tunnel)) == true {
|
|
if let cryptexError { idevice_error_free(cryptexError) }
|
|
return true
|
|
}
|
|
if let cryptexError {
|
|
try IdeviceFFI.check("failed to query installed DDI cryptex") { cryptexError }
|
|
}
|
|
return false
|
|
}
|
|
|
|
private func isLegacyDDIMounted(over tunnel: Tunnel) throws -> Bool {
|
|
var client: OpaquePointer?
|
|
try IdeviceFFI.check("failed to connect to image mounter") {
|
|
image_mounter_connect_rsd(tunnel.adapter, tunnel.handshake, &client)
|
|
}
|
|
guard let client else { throw StikJITError.debugProxyUnavailable }
|
|
defer { image_mounter_free(client) }
|
|
|
|
var devices: UnsafeMutablePointer<plist_t?>?
|
|
var deviceCount = 0
|
|
try IdeviceFFI.check("failed to fetch mounted devices") {
|
|
image_mounter_copy_devices(client, &devices, &deviceCount)
|
|
}
|
|
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 > 0
|
|
}
|
|
|
|
func mountDDI(from directoryPath: String, progress: @escaping (Double) -> Void) throws {
|
|
progress(0)
|
|
var assets: OpaquePointer?
|
|
try IdeviceFFI.check("failed to load DDI cryptex assets") {
|
|
directoryPath.withCString { cryptex1_assets_load($0, &assets) }
|
|
}
|
|
guard let assets else {
|
|
throw StikJITError.ddiFilesMissing("DDI cryptex assets could not be loaded from \(directoryPath)")
|
|
}
|
|
defer { cryptex1_assets_free(assets) }
|
|
|
|
let tunnel = try makeTunnel()
|
|
defer { tunnel.free() }
|
|
|
|
try IdeviceFFI.check("failed to install DDI cryptex") {
|
|
cryptexd_install_ddi(tunnel.adapter, tunnel.handshake, assets, nil)
|
|
}
|
|
progress(1)
|
|
}
|
|
|
|
private func openPairingFile() throws -> OpaquePointer {
|
|
guard !pairingFilePath.isEmpty, FileManager.default.fileExists(atPath: pairingFilePath) else {
|
|
throw StikJITError.pairingFile("not found at \(pairingFilePath)")
|
|
}
|
|
var handle: OpaquePointer?
|
|
try IdeviceFFI.check("failed to read pairing file") {
|
|
pairingFilePath.withCString { rp_pairing_file_read($0, &handle) }
|
|
}
|
|
guard let handle else { throw StikJITError.pairingFile("unreadable at \(pairingFilePath)") }
|
|
return handle
|
|
}
|
|
|
|
private func makeTunnel() throws -> Tunnel {
|
|
let pairing = try openPairingFile()
|
|
defer { rp_pairing_file_free(pairing) }
|
|
|
|
var address = sockaddr_in()
|
|
address.sin_family = sa_family_t(AF_INET)
|
|
address.sin_port = configuration.rsdPort.bigEndian
|
|
_ = configuration.deviceAddress.withCString { inet_pton(AF_INET, $0, &address.sin_addr) }
|
|
|
|
var tunnel = Tunnel()
|
|
try IdeviceFFI.check("failed to create RSD tunnel") {
|
|
"StikJIT".withCString { hostname in
|
|
withUnsafePointer(to: &address) { pointer in
|
|
pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sa in
|
|
tunnel_create_rppairing(
|
|
sa, socklen_t(MemoryLayout<sockaddr_in>.stride),
|
|
hostname, pairing, nil, nil,
|
|
&tunnel.adapter, &tunnel.handshake)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return tunnel
|
|
}
|
|
}
|