StikDebug/StikJIT/idevice/jit.m
2026-03-27 18:44:13 -04:00

219 lines
7.4 KiB
Objective-C

//
// jit.c
// StikJIT
//
// Created by Stephen on 3/27/25.
//
// Jackson Coxson
#include <arpa/inet.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include <limits.h>
#include "jit.h"
#import "JITEnableContext.h"
#import "JITEnableContextInternal.h"
// MARK: - Shared debug session
typedef struct {
RemoteServerHandle *remote_server;
DebugProxyHandle *debug_proxy;
} DebugSession;
static void debug_session_free(DebugSession *s) {
if (s->debug_proxy) { debug_proxy_free(s->debug_proxy); s->debug_proxy = NULL; }
if (s->remote_server) { remote_server_free(s->remote_server); s->remote_server = NULL; }
}
// Connects to the device using the existing adapter+handshake and sets up the debug proxy.
// Returns 0 on success; cleans up any partial state and returns 1 on failure.
static int connect_debug_session(AdapterHandle *adapter, RsdHandshakeHandle *handshake, DebugSession *out) {
memset(out, 0, sizeof(*out));
IdeviceFfiError *err = NULL;
err = remote_server_connect_rsd(adapter, handshake, &out->remote_server);
if (err) { idevice_error_free(err); return 1; }
err = debug_proxy_connect_rsd(adapter, handshake, &out->debug_proxy);
if (err) { idevice_error_free(err); debug_session_free(out); return 1; }
return 0;
}
// MARK: - Debug server commands
void runDebugServerCommand(int pid,
DebugProxyHandle* debug_proxy,
RemoteServerHandle* remote_server,
LogFuncC logger,
DebugAppCallback callback) {
// Enable QStartNoAckMode
char *disableResponse = NULL;
debug_proxy_send_ack(debug_proxy);
debug_proxy_send_ack(debug_proxy);
DebugserverCommandHandle *disableAckCommand = debugserver_command_new("QStartNoAckMode", NULL, 0);
IdeviceFfiError* err = debug_proxy_send_command(debug_proxy, disableAckCommand, &disableResponse);
debugserver_command_free(disableAckCommand);
logger("QStartNoAckMode result = %s, err = %d", disableResponse, err);
idevice_string_free(disableResponse);
debug_proxy_set_ack_mode(debug_proxy, false);
if (callback) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
callback(pid, debug_proxy, remote_server, semaphore);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
err = debug_proxy_send_raw(debug_proxy, "\x03", 1);
usleep(500);
} else {
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");
return;
}
char *attach_response = NULL;
err = debug_proxy_send_command(debug_proxy, attach_cmd, &attach_response);
debugserver_command_free(attach_cmd);
if (err) {
logger("Failed to attach to process: %d", err->code);
idevice_error_free(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);
debugserver_command_free(detach_cmd);
if (err) {
logger("Failed to detach from process: %d", err->code);
idevice_error_free(err);
} else if (detach_response != NULL) {
logger("Detach response: %s", detach_response);
idevice_string_free(detach_response);
}
}
}
// MARK: - Public entry points
int debug_app(AdapterHandle* adapter, RsdHandshakeHandle* handshake, const char *bundle_id, LogFuncC logger, DebugAppCallback callback) {
DebugSession session;
if (connect_debug_session(adapter, handshake, &session) != 0) return 1;
ProcessControlHandle *process_control = NULL;
IdeviceFfiError *err = process_control_new(session.remote_server, &process_control);
if (err) {
idevice_error_free(err);
debug_session_free(&session);
return 1;
}
uint64_t pid = 0;
err = process_control_launch_app(process_control, bundle_id, NULL, 0, NULL, 0, true, false, &pid);
if (err) {
idevice_error_free(err);
process_control_free(process_control);
debug_session_free(&session);
return 1;
}
runDebugServerCommand((int)pid, session.debug_proxy, session.remote_server, logger, callback);
process_control_free(process_control);
debug_session_free(&session);
logger("Debug session completed");
return 0;
}
int debug_app_pid(AdapterHandle* adapter, RsdHandshakeHandle* handshake, int pid, LogFuncC logger, DebugAppCallback callback) {
DebugSession session;
if (connect_debug_session(adapter, handshake, &session) != 0) return 1;
runDebugServerCommand(pid, session.debug_proxy, session.remote_server, logger, callback);
debug_session_free(&session);
logger("Debug session completed");
return 0;
}
int launch_app_via_proxy(AdapterHandle* adapter, RsdHandshakeHandle* handshake, const char *bundle_id, LogFuncC logger) {
IdeviceFfiError* err = NULL;
RemoteServerHandle *remote_server = NULL;
ProcessControlHandle *process_control = NULL;
uint64_t pid = 0;
int result = 1;
err = remote_server_connect_rsd(adapter, handshake, &remote_server);
if (err) { idevice_error_free(err); goto cleanup; }
err = process_control_new(remote_server, &process_control);
if (err) { idevice_error_free(err); goto cleanup; }
err = process_control_launch_app(process_control, bundle_id, NULL, 0, NULL, 0, false, true, &pid);
if (err) {
idevice_error_free(err);
if (logger) logger("Failed to launch app: %s", bundle_id);
goto cleanup;
}
if (logger) logger("Launched app (PID %llu)", pid);
result = 0;
cleanup:
if (process_control) process_control_free(process_control);
if (remote_server) remote_server_free(remote_server);
return result;
}
@implementation JITEnableContext(JIT)
- (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger jsCallback:(DebugAppCallback)jsCallback {
NSError* err = nil;
[self ensureTunnelWithError:&err];
if (err) {
logger(err.localizedDescription);
return NO;
}
return debug_app(adapter, handshake, [bundleID UTF8String], [self createCLogger:logger], jsCallback) == 0;
}
- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger jsCallback:(DebugAppCallback)jsCallback {
NSError* err = nil;
[self ensureTunnelWithError:&err];
if (err) {
logger(err.localizedDescription);
return NO;
}
return debug_app_pid(adapter, handshake, pid, [self createCLogger:logger], jsCallback) == 0;
}
- (BOOL)launchAppWithoutDebug:(NSString*)bundleID logger:(LogFunc)logger {
NSError* err = nil;
[self ensureTunnelWithError:&err];
if (err) {
logger(err.localizedDescription);
return NO;
}
return launch_app_via_proxy(adapter, handshake, [bundleID UTF8String], [self createCLogger:logger]) == 0;
}
@end