diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj index e699139d..8450ee36 100644 --- a/StikDebug.xcodeproj/project.pbxproj +++ b/StikDebug.xcodeproj/project.pbxproj @@ -50,7 +50,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - DC5716242D9564760099302E /* em_proxy.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = em_proxy.xcframework; sourceTree = ""; }; DC6F1D372D94EADD0071B2B6 /* StikDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StikDebug.app; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D482D94EADF0071B2B6 /* StikDebugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -62,6 +61,7 @@ 1775D3612D9644FD00DFA8E0 /* Exceptions for "StikJIT" folder in "StikDebug" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( + idevice/libem_proxy.a, Info.plist, ); target = DC6F1D362D94EADD0071B2B6 /* StikDebug */; @@ -163,7 +163,6 @@ DC6F1D752D94EB620071B2B6 /* Frameworks */ = { isa = PBXGroup; children = ( - DC5716242D9564760099302E /* em_proxy.xcframework */, DCA690112DAF660E007C91A8 /* NetworkExtension.framework */, ); name = Frameworks; diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index eb9d54fc..ae58cf7e 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -520,39 +520,32 @@ struct HeartbeatApp: App { LoadingView(showAlert: $show_alert, alertTitle: $alert_title, alertMessage: $alert_string) .onAppear { dnsChecker.checkDNS() - - startProxy() { result, error in + checkVPNConnection() { result, vpn_error in if result { - checkVPNConnection() { result, vpn_error in - if result { - if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) { - Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in - if pubHeartBeat { - isLoading2 = false - timer.invalidate() + if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) { + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in + if pubHeartBeat { + isLoading2 = false + timer.invalidate() + } else { + if let error { + if error == -9 { // InvalidHostID is -9 + isPairing = true } else { - if let error { - if error == -9 { // InvalidHostID is -9 - isPairing = true - } else { - startHeartbeatInBackground() - } - self.error = nil - } + startHeartbeatInBackground() } + self.error = nil } - startHeartbeatInBackground() - } else { - isLoading2 = false - } - } else if let vpn_error { - showAlert(title: "Error", message: "EM Proxy failed to connect: \(vpn_error)", showOk: true) { _ in - exit(0) } } + startHeartbeatInBackground() + } else { + isLoading2 = false + } + } else if let vpn_error { + showAlert(title: "Error", message: "EM Proxy failed to connect: \(vpn_error)", showOk: true) { _ in + exit(0) } - } else if let error { - showAlert(title: "Error", message: "EM Proxy Failed to start \(error)", showOk: true) { _ in } } } } @@ -658,24 +651,6 @@ struct HeartbeatApp: App { } } - func startProxy(callback: @escaping (Bool, Int?) -> Void) { - let port = 51820 - let bindAddr = "127.0.0.1:\(port)" - - DispatchQueue.global(qos: .background).async { - let result = start_emotional_damage(bindAddr) - DispatchQueue.main.async { - if result == 0 { - print("DEBUG: em_proxy started successfully on port \(port)") - callback(true, nil) - } else { - print("DEBUG: Failed to start em_proxy") - callback(false, Int(result)) - } - } - } - } - private func checkVPNConnection(callback: @escaping (Bool, String?) -> Void) { let host = NWEndpoint.Host("10.7.0.1") let port = NWEndpoint.Port(rawValue: 62078)! @@ -948,10 +923,12 @@ struct LoadingView: View { } } -public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, primaryButtonText: String? = nil, completion: @escaping (Bool) -> Void) { +public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, primaryButtonText: String? = nil, messageType: MessageType = .error, completion: @escaping (Bool) -> Void) { DispatchQueue.main.async { - let rootViewController = UIApplication.shared.windows.last?.rootViewController - + guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { + return + } + let rootViewController = scene.windows.first?.rootViewController if showTryAgain { let customErrorView = CustomErrorView( title: title, @@ -964,7 +941,8 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain primaryButtonText: primaryButtonText ?? "Try Again", onPrimaryButtonTap: { completion(true) - } + }, + messageType: messageType ) let hostingController = UIHostingController(rootView: customErrorView) hostingController.modalPresentationStyle = .overFullScreen @@ -984,7 +962,8 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain onPrimaryButtonTap: { rootViewController?.presentedViewController?.dismiss(animated: true) completion(true) - } + }, + messageType: messageType ) let hostingController = UIHostingController(rootView: customErrorView) hostingController.modalPresentationStyle = .overFullScreen @@ -999,7 +978,8 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain rootViewController?.presentedViewController?.dismiss(animated: true) completion(false) }, - showButton: false + showButton: false, + messageType: messageType ) let hostingController = UIHostingController(rootView: customErrorView) hostingController.modalPresentationStyle = .overFullScreen diff --git a/StikJIT/Views/Custom Error View.swift b/StikJIT/Views/Custom Error View.swift index 08ded9d4..ec2ee773 100644 --- a/StikJIT/Views/Custom Error View.swift +++ b/StikJIT/Views/Custom Error View.swift @@ -7,6 +7,12 @@ import SwiftUI +public enum MessageType { + case error + case success + case info +} + struct CustomErrorView: View { var title: String var message: String @@ -32,11 +38,6 @@ struct CustomErrorView: View { } } - enum MessageType { - case error - case success - case info - } var body: some View { ZStack { diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index ad793b22..4705bfd5 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -33,8 +33,12 @@ struct HomeView: View { @State private var showingConsoleLogsView = false @State private var importProgress: Float = 0.0 + @State private var pidTextAlertShow = false + @State private var pidStr = "" + @State private var viewDidAppeared = false @State private var pendingBundleIdToEnableJIT : String? = nil + @State private var pendingPIDToEnableJIT : Int? = nil private var accentColor: Color { if customAccentColorHex.isEmpty { @@ -99,6 +103,27 @@ struct HomeView: View { } .padding(.horizontal, 20) + if pairingFileExists { + Button(action: { + pidTextAlertShow = true + }) { + HStack { + Image(systemName: "cable.connector.horizontal") + .font(.system(size: 20)) + Text("Connect by PID") + .font(.system(.title3, design: .rounded)) + .fontWeight(.semibold) + } + .frame(maxWidth: .infinity) + .padding() + .background(accentColor) + .foregroundColor(accentColor.contrastText()) + .cornerRadius(16) + .shadow(color: accentColor.opacity(0.3), radius: 8, x: 0, y: 4) + } + .padding(.horizontal, 20) + } + Button(action: { showingConsoleLogsView = true }) { @@ -271,6 +296,28 @@ struct HomeView: View { startJITInBackground(with: selectedBundle) } } + .textFieldAlert( + isPresented: $pidTextAlertShow, + title: "Please enter the PID of the process you want to connect to", + text: $pidStr, + placeholder: "", + action: { newText in + + guard let pidStr = newText, pidStr != "" else { + return + } + + guard let pid = Int(pidStr) else { + showAlert(title: "", message: "Invalid PID", showOk: true, completion: { _ in }) + return + } + startJITInBackground(with: pid) + + }, + actionCancel: {_ in + pidStr = "" + } + ) .onOpenURL { url in print(url.path()) if url.host() != "enable-jit" { @@ -284,6 +331,12 @@ struct HomeView: View { } else { pendingBundleIdToEnableJIT = bundleId } + } else if let pidStr = components?.queryItems?.first(where: { $0.name == "pid" })?.value, let pid = Int(pidStr) { + if viewDidAppeared { + startJITInBackground(with: pid) + } else { + pendingPIDToEnableJIT = pid + } } } @@ -293,6 +346,10 @@ struct HomeView: View { startJITInBackground(with: pendingBundleIdToEnableJIT) self.pendingBundleIdToEnableJIT = nil } + if let pendingPIDToEnableJIT { + startJITInBackground(with: pendingPIDToEnableJIT) + self.pendingPIDToEnableJIT = nil + } } } @@ -342,6 +399,34 @@ struct HomeView: View { } } } + + private func startJITInBackground(with pid: Int) { + isProcessing = true + + // Add log message + LogManager.shared.addInfoLog("Starting JIT for pid \(pid)") + + DispatchQueue.global(qos: .background).async { + + let success = JITEnableContext.shared.debugApp(withPID: Int32(pid), logger: { message in + + if let message = message { + // Log messages from the JIT process + LogManager.shared.addInfoLog(message) + } + }) + + DispatchQueue.main.async { + LogManager.shared.addInfoLog("JIT process completed for \(pid)") + showAlert(title: "Success", message: "JIT has been enabled for pid \(pid).", showOk: true, messageType: .success, completion: { _ in }) + isProcessing = false + + if success && doAutoQuitAfterEnablingJIT { + exit(0) + } + } + } + } } class InstalledAppsViewModel: ObservableObject { diff --git a/StikJIT/Views/InstalledAppsListView.swift b/StikJIT/Views/InstalledAppsListView.swift index 376b4a90..744740b1 100644 --- a/StikJIT/Views/InstalledAppsListView.swift +++ b/StikJIT/Views/InstalledAppsListView.swift @@ -15,47 +15,80 @@ struct InstalledAppsListView: View { var onSelectApp: (String) -> Void var body: some View { - NavigationView { - List { - if !recentApps.isEmpty { - Section { - ForEach(recentApps, id: \.self) { bundleID in - AppButton(bundleID: bundleID, appName: viewModel.apps[bundleID] ?? "", recentApps: $recentApps, appIcons: $appIcons, onSelectApp: onSelectApp) - .swipeActions(edge: .trailing) { - Button(role: .destructive) { - withAnimation { - recentApps.removeAll(where: { $0 == bundleID }) + if viewModel.apps.isEmpty { + NavigationView { + VStack(spacing: 16) { + Image(systemName: "magnifyingglass") + .resizable() + .scaledToFit() + .frame(width: 60, height: 60) + .foregroundColor(.gray) + + Text("No Debuggable App Found") + .font(.title2) + .fontWeight(.semibold) + .foregroundColor(.primary) + + Text("StikDebug can only connect to apps with the \"**get-task-allow**\" entitlement. Please check if the app you want to connect to is signed with a **development** certificate.") + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal) + } + .padding() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.systemBackground)) + .toolbar(content: { + Button("Done") { + dismiss() + } + }) + } + + } else { + + NavigationView { + List { + if !recentApps.isEmpty { + Section { + ForEach(recentApps, id: \.self) { bundleID in + AppButton(bundleID: bundleID, appName: viewModel.apps[bundleID] ?? "", recentApps: $recentApps, appIcons: $appIcons, onSelectApp: onSelectApp) + .swipeActions(edge: .trailing) { + Button(role: .destructive) { + withAnimation { + recentApps.removeAll(where: { $0 == bundleID }) + } + } label: { + Label("Delete", systemImage: "trash") } - } label: { - Label("Delete", systemImage: "trash") } - } + } + } header: { + Text("Recents") + } + } + Section { + ForEach(viewModel.apps.sorted(by: { $0.key < $1.key }), id: \.key) { bundleID, appName in + AppButton(bundleID: bundleID, appName: appName, recentApps: $recentApps, appIcons: $appIcons, onSelectApp: onSelectApp) } } header: { - Text("Recents") + if !recentApps.isEmpty { + Text("All applications") + } else { + EmptyView() + } } } - Section { - ForEach(viewModel.apps.sorted(by: { $0.key < $1.key }), id: \.key) { bundleID, appName in - AppButton(bundleID: bundleID, appName: appName, recentApps: $recentApps, appIcons: $appIcons, onSelectApp: onSelectApp) - } - } header: { - if !recentApps.isEmpty { - Text("All applications") - } else { - EmptyView() + .listStyle(.plain) + .navigationTitle("Installed Apps") + .toolbar(content: { + Button("Done") { + dismiss() } - } + }) } - .listStyle(.plain) - .navigationTitle("Installed Apps") - .toolbar(content: { - Button("Done") { - dismiss() - } - }) + .background(Color(UIColor.systemGroupedBackground).edgesIgnoringSafeArea(.all)) } - .background(Color(UIColor.systemGroupedBackground).edgesIgnoringSafeArea(.all)) } } diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 8d5ea76a..05a9ff11 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -12,7 +12,7 @@ struct SettingsView: View { @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("selectedAppIcon") private var selectedAppIcon: String = "AppIcon" @AppStorage("autoQuitAfterEnablingJIT") private var doAutoQuitAfterEnablingJIT = false - @AppStorage("skipGetTaskAllowCheck") private var doSkipGetTaskAllowCheck = true + @State private var isShowingPairingFilePicker = false @Environment(\.colorScheme) private var colorScheme @@ -119,21 +119,6 @@ struct SettingsView: View { .padding(.horizontal, 16) } - // Behavior section - SettingsCard { - VStack(alignment: .leading, spacing: 20) { - Text("Behavior") - .font(.headline) - .foregroundColor(.primary) - .padding(.bottom, 4) - Toggle("Skip get-task-allow Check", isOn: $doSkipGetTaskAllowCheck) - .foregroundColor(.primary) - .padding(.vertical, 6) - } - .padding(.vertical, 20) - .padding(.horizontal, 16) - } - // Pairing File section SettingsCard { VStack(alignment: .leading, spacing: 20) { diff --git a/StikJIT/Views/TextFieldAlert.swift b/StikJIT/Views/TextFieldAlert.swift new file mode 100644 index 00000000..c555e38d --- /dev/null +++ b/StikJIT/Views/TextFieldAlert.swift @@ -0,0 +1,77 @@ +// +// TextAlert.swift +// StikJIT +// +// Created by s s on 2025/5/11. +// +import SwiftUI +import UIKit + +public struct TextFieldAlertModifier: ViewModifier { + + @State private var alertController: UIAlertController? + + @Binding var isPresented: Bool + + let title: String + let text: Binding + let placeholder: String + let action: (String?) -> Void + let actionCancel: (String?) -> Void + + public func body(content: Content) -> some View { + content.onChange(of: isPresented) { isPresented in + if isPresented, alertController == nil { + let alertController = makeAlertController() + self.alertController = alertController + + guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { + return + } + scene.windows.first?.rootViewController?.present(alertController, animated: true) + } else if !isPresented, let alertController = alertController { + alertController.dismiss(animated: true) + self.alertController = nil + } + } + } + + private func makeAlertController() -> UIAlertController { + let controller = UIAlertController(title: title, message: nil, preferredStyle: .alert) + controller.addTextField { + $0.placeholder = self.placeholder + $0.text = self.text.wrappedValue + $0.clearButtonMode = .always + } + controller.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in + self.actionCancel(nil) + shutdown() + }) + controller.addAction(UIAlertAction(title: "OK", style: .default) { _ in + self.action(controller.textFields?.first?.text) + shutdown() + }) + return controller + } + + private func shutdown() { + isPresented = false + alertController = nil + } + +} + + +extension View { + + public func textFieldAlert( + isPresented: Binding, + title: String, + text: Binding, + placeholder: String = "", + action: @escaping (String?) -> Void, + actionCancel: @escaping (String?) -> Void + ) -> some View { + self.modifier(TextFieldAlertModifier(isPresented: isPresented, title: title, text: text, placeholder: placeholder, action: action, actionCancel: actionCancel)) + } +} diff --git a/StikJIT/idevice/JITEnableContext.h b/StikJIT/idevice/JITEnableContext.h index a3d1870d..28d47246 100644 --- a/StikJIT/idevice/JITEnableContext.h +++ b/StikJIT/idevice/JITEnableContext.h @@ -17,6 +17,7 @@ typedef void (^LogFunc)(NSString *message); - (IdevicePairingFile*)getPairingFileWithError:(NSError**)error; - (void)startHeartbeatWithCompletionHandler:(HeartbeatCompletionHandler)completionHandler logger:(LogFunc)logger; - (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger; +- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger; - (NSDictionary*)getAppListWithError:(NSError**)error; - (UIImage*)getAppIconWithBundleId:(NSString*)bundleId error:(NSError**)error; @end diff --git a/StikJIT/idevice/JITEnableContext.m b/StikJIT/idevice/JITEnableContext.m index 807f8451..f97f8758 100644 --- a/StikJIT/idevice/JITEnableContext.m +++ b/StikJIT/idevice/JITEnableContext.m @@ -4,7 +4,6 @@ // // Created by s s on 2025/3/28. // - #include "idevice.h" #include #include @@ -134,6 +133,19 @@ - (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger { [self createCLogger:logger]) == 0; } +- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger { + if (!provider) { + if (logger) { + logger(@"Provider not initialized!"); + } + NSLog(@"Provider not initialized!"); + return NO; + } + return debug_app_pid(provider, + pid, + [self createCLogger:logger]) == 0; +} + - (NSDictionary*)getAppListWithError:(NSError**)error { if (!provider) { NSLog(@"Provider not initialized!"); diff --git a/StikJIT/idevice/jit.c b/StikJIT/idevice/jit.c index b657b348..218efd26 100644 --- a/StikJIT/idevice/jit.c +++ b/StikJIT/idevice/jit.c @@ -263,3 +263,229 @@ int debug_app(TcpProviderHandle* tcp_provider, const char *bundle_id, LogFuncC l logger("Debug session completed"); return 0; } + + +int debug_app_pid(TcpProviderHandle* tcp_provider, int pid, LogFuncC logger) { + // Connect to CoreDeviceProxy + CoreDeviceProxyHandle *core_device = NULL; + IdeviceErrorCode err = core_device_proxy_connect_tcp(tcp_provider, &core_device); + if (err != IdeviceSuccess) { + logger("Failed to connect to CoreDeviceProxy: %d", err); + return 1; + } + + // Get server RSD port + uint16_t rsd_port; + err = core_device_proxy_get_server_rsd_port(core_device, &rsd_port); + if (err != IdeviceSuccess) { + logger("Failed to get server RSD port: %d", err); + core_device_proxy_free(core_device); + return 1; + } + logger("Server RSD Port: %d", rsd_port); + + /***************************************************************** + * Create TCP Tunnel Adapter + *****************************************************************/ + logger("=== Creating TCP Tunnel Adapter ==="); + + AdapterHandle *adapter = NULL; + err = core_device_proxy_create_tcp_adapter(core_device, &adapter); + if (err != IdeviceSuccess) { + logger("Failed to create TCP adapter: %d", err); + core_device_proxy_free(core_device); + return 1; + } + + // Connect to RSD port + err = adapter_connect(adapter, rsd_port); + if (err != IdeviceSuccess) { + logger("Failed to connect to RSD port: %d", err); + adapter_free(adapter); + return 1; + } + logger("Successfully connected to RSD port"); + + /***************************************************************** + * XPC Device Setup + *****************************************************************/ + logger("=== Setting up XPC Device ==="); + + XPCDeviceAdapterHandle *xpc_device = NULL; + err = xpc_device_new(adapter, &xpc_device); + if (err != IdeviceSuccess) { + logger("Failed to create XPC device: %d", err); + adapter_free(adapter); + return 1; + } + + // Get DebugProxy service + XPCServiceHandle *debug_service = NULL; + err = xpc_device_get_service(xpc_device, "com.apple.internal.dt.remote.debugproxy", &debug_service); + if (err != IdeviceSuccess) { + logger("Failed to get debug proxy service: %d", err); + xpc_device_free(xpc_device); + return 1; + } + + // Get ProcessControl service + XPCServiceHandle *pc_service = NULL; + err = xpc_device_get_service(xpc_device, "com.apple.instruments.dtservicehub", &pc_service); + if (err != IdeviceSuccess) { + logger("Failed to get process control service: %d", err); + xpc_device_free(xpc_device); + return 1; + } + + /***************************************************************** + * Process Control - Launch App + *****************************************************************/ + logger("=== Launching App ==="); + + // Get the adapter back from the XPC device + + AdapterHandle *pc_adapter = NULL; + err = xpc_device_adapter_into_inner(xpc_device, &pc_adapter); + if (err != IdeviceSuccess) { + logger("Failed to extract adapter: %d", err); + xpc_device_free(xpc_device); + return 1; + } + + // Connect to process control port + err = adapter_connect(pc_adapter, pc_service->port); + if (err != IdeviceSuccess) { + logger("Failed to connect to process control port: %d", err); + adapter_free(pc_adapter); + xpc_service_free(pc_service); + xpc_service_free(debug_service); + return 1; + } + logger("Successfully connected to process control port"); + + // Create RemoteServerClient + RemoteServerAdapterHandle *remote_server = NULL; + err = remote_server_adapter_new(pc_adapter, &remote_server); + if (err != IdeviceSuccess) { + logger("Failed to create remote server: %d", err); + adapter_free(pc_adapter); + xpc_service_free(pc_service); + xpc_service_free(debug_service); + return 1; + } + + // Create ProcessControlClient + ProcessControlAdapterHandle *process_control = NULL; + err = process_control_new(remote_server, &process_control); + if (err != IdeviceSuccess) { + logger("Failed to create process control client: %d", err); + remote_server_free(remote_server); + xpc_service_free(pc_service); + xpc_service_free(debug_service); + return 1; + } + + // Disable memory limit for PID + err = process_control_disable_memory_limit(process_control, pid); + if (err != IdeviceSuccess) { + logger("failed to disable memory limit: %d", err); + } + + /***************************************************************** + * Debug Proxy - Attach to Process + *****************************************************************/ + logger("=== Attaching Debugger ==="); + + // Get the adapter back from the remote server + AdapterHandle *debug_adapter = NULL; + err = remote_server_adapter_into_inner(remote_server, &debug_adapter); + if (err != IdeviceSuccess) { + logger("Failed to extract adapter: %d", err); + xpc_service_free(debug_service); + process_control_free(process_control); + remote_server_free(remote_server); + xpc_service_free(pc_service); + return 1; + } + + // Connect to debug proxy port + err = adapter_connect(debug_adapter, debug_service->port); + if (err != IdeviceSuccess) { + logger("Failed to connect to debug proxy port: %d", err); + adapter_free(debug_adapter); + xpc_service_free(debug_service); + process_control_free(process_control); + remote_server_free(remote_server); + xpc_service_free(pc_service); + return 1; + } + logger("Successfully connected to debug proxy port"); + + // Create DebugProxyClient + DebugProxyAdapterHandle *debug_proxy = NULL; + err = debug_proxy_adapter_new(debug_adapter, &debug_proxy); + if (err != IdeviceSuccess) { + logger("Failed to create debug proxy client: %d", err); + adapter_free(debug_adapter); + xpc_service_free(debug_service); + process_control_free(process_control); + remote_server_free(remote_server); + xpc_service_free(pc_service); + return 1; + } + + // Send vAttach command with PID in hex + char attach_command[64]; + snprintf(attach_command, sizeof(attach_command), "vAttach;%" PRIx64, pid); + + DebugserverCommandHandle *attach_cmd = debugserver_command_new(attach_command, NULL, 0); + if (attach_cmd == NULL) { + logger("Failed to create attach command"); + debug_proxy_free(debug_proxy); + xpc_service_free(debug_service); + process_control_free(process_control); + remote_server_free(remote_server); + xpc_service_free(pc_service); + return 1; + } + + char *attach_response = NULL; + err = debug_proxy_send_command(debug_proxy, attach_cmd, &attach_response); + debugserver_command_free(attach_cmd); + + if (err != IdeviceSuccess) { + logger("Failed to attach to process: %d", err); + } else if (attach_response != NULL) { + logger("Attach response: %s", attach_response); + idevice_string_free(attach_response); + } + + // Send detach command + DebugserverCommandHandle *detach_cmd = debugserver_command_new("D", NULL, 0); + if (detach_cmd == NULL) { + logger("Failed to create detach command"); + } else { + char *detach_response = NULL; + err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); + err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); + err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); + debugserver_command_free(detach_cmd); + + if (err != IdeviceSuccess) { + logger("Failed to detach from process: %d", err); + } else if (detach_response != NULL) { + logger("Detach response: %s", detach_response); + idevice_string_free(detach_response); + } + } + + /***************************************************************** + * Cleanup + *****************************************************************/ + debug_proxy_free(debug_proxy); + xpc_service_free(debug_service); + xpc_service_free(pc_service); + + logger("Debug session completed"); + return 0; +} diff --git a/StikJIT/idevice/jit.h b/StikJIT/idevice/jit.h index c42f2fc2..eb12442c 100644 --- a/StikJIT/idevice/jit.h +++ b/StikJIT/idevice/jit.h @@ -12,5 +12,6 @@ typedef void (^LogFuncC)(const char* message, ...); int debug_app(TcpProviderHandle* provider, const char *bundle_id, LogFuncC logger); +int debug_app_pid(TcpProviderHandle* provider, int pid, LogFuncC logger); #endif /* JIT_H */ diff --git a/em_proxy.xcframework/Info.plist b/em_proxy.xcframework/Info.plist deleted file mode 100644 index 76133576..00000000 --- a/em_proxy.xcframework/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - AvailableLibraries - - - BinaryPath - libem_proxy.a - HeadersPath - Headers - LibraryIdentifier - ios-arm64 - LibraryPath - libem_proxy.a - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - - - BinaryPath - libem_proxy.a - HeadersPath - Headers - LibraryIdentifier - ios-arm64-simulator - LibraryPath - libem_proxy.a - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - SupportedPlatformVariant - simulator - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/em_proxy.xcframework/ios-arm64-simulator/Headers/em_proxy/em_proxy.h b/em_proxy.xcframework/ios-arm64-simulator/Headers/em_proxy/em_proxy.h deleted file mode 100644 index fd09f4fa..00000000 --- a/em_proxy.xcframework/ios-arm64-simulator/Headers/em_proxy/em_proxy.h +++ /dev/null @@ -1,40 +0,0 @@ -// Jackson Coxson - -#include -#include -#include -#include -#include - - -/** - * Starts your emotional damage - * # Arguments - * * `bind_addr` - The UDP socket to listen to - * # Returns - * A handle to stop further emotional damage. - * Null on failure - * # Safety - * Don't be stupid - */ -int start_emotional_damage(const char *bind_addr); - -/** - * Stops further emotional damage - * # Arguments - * * `handle` - The coping mechanism generated by start_emotional_damage - * # Returns - * The knowledge of knowing that you couldn't handle failure - * # Safety - * Don't be stupid - */ -void stop_emotional_damage(void); - -/** - * Blocks until Wireguard is ready - * # Arguments - * * `timeout` - The timeout in miliseconds to wait for Wireguard - * # Returns - * 0 on success, -1 on failure - */ -int test_emotional_damage(int timeout); diff --git a/em_proxy.xcframework/ios-arm64-simulator/Headers/em_proxy/module.modulemap b/em_proxy.xcframework/ios-arm64-simulator/Headers/em_proxy/module.modulemap deleted file mode 100644 index 22cb95f4..00000000 --- a/em_proxy.xcframework/ios-arm64-simulator/Headers/em_proxy/module.modulemap +++ /dev/null @@ -1,5 +0,0 @@ -module em_proxy { - header "em_proxy.h" - - export * -} diff --git a/em_proxy.xcframework/ios-arm64-simulator/libem_proxy.a b/em_proxy.xcframework/ios-arm64-simulator/libem_proxy.a deleted file mode 100644 index eef9c154..00000000 Binary files a/em_proxy.xcframework/ios-arm64-simulator/libem_proxy.a and /dev/null differ diff --git a/em_proxy.xcframework/ios-arm64/Headers/em_proxy/em_proxy.h b/em_proxy.xcframework/ios-arm64/Headers/em_proxy/em_proxy.h deleted file mode 100644 index fd09f4fa..00000000 --- a/em_proxy.xcframework/ios-arm64/Headers/em_proxy/em_proxy.h +++ /dev/null @@ -1,40 +0,0 @@ -// Jackson Coxson - -#include -#include -#include -#include -#include - - -/** - * Starts your emotional damage - * # Arguments - * * `bind_addr` - The UDP socket to listen to - * # Returns - * A handle to stop further emotional damage. - * Null on failure - * # Safety - * Don't be stupid - */ -int start_emotional_damage(const char *bind_addr); - -/** - * Stops further emotional damage - * # Arguments - * * `handle` - The coping mechanism generated by start_emotional_damage - * # Returns - * The knowledge of knowing that you couldn't handle failure - * # Safety - * Don't be stupid - */ -void stop_emotional_damage(void); - -/** - * Blocks until Wireguard is ready - * # Arguments - * * `timeout` - The timeout in miliseconds to wait for Wireguard - * # Returns - * 0 on success, -1 on failure - */ -int test_emotional_damage(int timeout); diff --git a/em_proxy.xcframework/ios-arm64/Headers/em_proxy/module.modulemap b/em_proxy.xcframework/ios-arm64/Headers/em_proxy/module.modulemap deleted file mode 100644 index 22cb95f4..00000000 --- a/em_proxy.xcframework/ios-arm64/Headers/em_proxy/module.modulemap +++ /dev/null @@ -1,5 +0,0 @@ -module em_proxy { - header "em_proxy.h" - - export * -} diff --git a/em_proxy.xcframework/ios-arm64/libem_proxy.a b/em_proxy.xcframework/ios-arm64/libem_proxy.a deleted file mode 100644 index afa7b5e1..00000000 Binary files a/em_proxy.xcframework/ios-arm64/libem_proxy.a and /dev/null differ