mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-15 20:10:30 +00:00
Remove em_proxy, add pid support, fix alert position issue when app is not in fullscreen mode, remove get-task-allow alert and replace with "No Debuggable App Found" screen (#184)
This commit is contained in:
parent
61754b60cc
commit
1efae6001c
18 changed files with 508 additions and 245 deletions
|
|
@ -50,7 +50,6 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
DC5716242D9564760099302E /* em_proxy.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = em_proxy.xcframework; sourceTree = "<group>"; };
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
} else {
|
||||
if let error {
|
||||
if error == -9 { // InvalidHostID is -9
|
||||
isPairing = true
|
||||
} else {
|
||||
startHeartbeatInBackground()
|
||||
}
|
||||
self.error = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
startHeartbeatInBackground()
|
||||
} else {
|
||||
if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) {
|
||||
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
|
||||
if pubHeartBeat {
|
||||
isLoading2 = false
|
||||
}
|
||||
} else if let vpn_error {
|
||||
showAlert(title: "Error", message: "EM Proxy failed to connect: \(vpn_error)", showOk: true) { _ in
|
||||
exit(0)
|
||||
timer.invalidate()
|
||||
} else {
|
||||
if let error {
|
||||
if error == -9 { // InvalidHostID is -9
|
||||
isPairing = true
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
} 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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("Installed Apps")
|
||||
.toolbar(content: {
|
||||
Button("Done") {
|
||||
dismiss()
|
||||
}
|
||||
} header: {
|
||||
if !recentApps.isEmpty {
|
||||
Text("All applications")
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("Installed Apps")
|
||||
.toolbar(content: {
|
||||
Button("Done") {
|
||||
dismiss()
|
||||
}
|
||||
})
|
||||
.background(Color(UIColor.systemGroupedBackground).edgesIgnoringSafeArea(.all))
|
||||
}
|
||||
.background(Color(UIColor.systemGroupedBackground).edgesIgnoringSafeArea(.all))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
77
StikJIT/Views/TextFieldAlert.swift
Normal file
77
StikJIT/Views/TextFieldAlert.swift
Normal file
|
|
@ -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<String>
|
||||
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<Bool>,
|
||||
title: String,
|
||||
text: Binding<String>,
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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<NSString*, NSString*>*)getAppListWithError:(NSError**)error;
|
||||
- (UIImage*)getAppIconWithBundleId:(NSString*)bundleId error:(NSError**)error;
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
//
|
||||
// Created by s s on 2025/3/28.
|
||||
//
|
||||
|
||||
#include "idevice.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -134,6 +133,19 @@ JITEnableContext* sharedJITContext = nil;
|
|||
[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<NSString*, NSString*>*)getAppListWithError:(NSError**)error {
|
||||
if (!provider) {
|
||||
NSLog(@"Provider not initialized!");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>libem_proxy.a</string>
|
||||
<key>HeadersPath</key>
|
||||
<string>Headers</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>libem_proxy.a</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>libem_proxy.a</string>
|
||||
<key>HeadersPath</key>
|
||||
<string>Headers</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>libem_proxy.a</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// Jackson Coxson
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module em_proxy {
|
||||
header "em_proxy.h"
|
||||
|
||||
export *
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,40 +0,0 @@
|
|||
// Jackson Coxson
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module em_proxy {
|
||||
header "em_proxy.h"
|
||||
|
||||
export *
|
||||
}
|
||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue