Repository: utmapp/UTM
File: UTM/main.m (Objective-C)

Code before:
#import "AppDelegate.h"

extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *);
extern int ptrace(int request, pid_t pid, caddr_t addr, int data);

#define PT_TRACE_ME 0
#define PT_SIGEXC 12
#define PT_DENY_ATTACH 31

kern_return_t catch_exception_raise(mach_port_t exception_port,
                                    mach_port_t thread,
                                    mach_port_t task,
                                    exception_type_t exception,
                                    exception_data_t code,
                                    mach_msg_type_number_t code_count) {
    NSLog(@"Caught exception %d (this should be EXC_SOFTWARE). Forcing suicide.", exception);
    // _exit doesn't seem to work, but this does. ¯\_(ツ)_/¯
    return KERN_FAILURE;
}

void *exception_handler(void *argument) {
    mach_port_t port = *(mach_port_t *)argument;
    mach_msg_server(exc_server, 2048, port, 0);
    return NULL;
}

static BOOL am_i_being_debugged() {
    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
    struct kinfo_proc info = {};
    size_t size = sizeof(info);
    return !sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) && !!(info.kp_proc.p_flag & P_TRACED);
}

int main(int argc, char * argv[]) {
    BOOL debugged = am_i_being_debugged();
    
    // Thanks to this comment: https://news.ycombinator.com/item?id=18431524
    // We use this hack to allow mmap with PROT_EXEC which requires
    // dynamic_codesign entitlement and the process to be tricked into thinking
    // that Xcode is debugging it. We abuse the fact that JIT is needed to
    // debug the process.
    ptrace(PT_TRACE_ME, 0, NULL, 0);
    
    // ptracing ourselves confuses the kernel and will cause bad things to
    // happen to the system (hangs…) if an exception or signal occurs. Setup
    // some "safety nets" so we can cause the process to exit in a somewhat sane
    // state. We only need to do this if the debugger isn't attached. (It'll do
    // this itself, and if we do it we'll interfere with its normal operation
    // anyways.)
    if (!debugged) {
        // First, ensure that signals are delivered as Mach software exceptions…

Review comment (bug):
better to use csops to check for CS_DEBUGGED here, in case it was set by a jailbreak already (as jailbreaks can set the codesigning flag without having to call ptrace)

Code after:
#import "AppDelegate.h"

extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *);
extern int ptrace(int request, pid_t pid, caddr_t addr, int data);

#define PT_TRACE_ME 0
#define PT_SIGEXC 12

kern_return_t catch_exception_raise(mach_port_t exception_port,
                                    mach_port_t thread,
                                    mach_port_t task,
                                    exception_type_t exception,
                                    exception_data_t code,
                                    mach_msg_type_number_t code_count) {
    NSLog(@"Caught exception %d (this should be EXC_SOFTWARE), with code 0x%x (this should be EXC_SOFT_SIGNAL) and subcode %d. Forcing suicide.", exception, *code, code[1]);
    // _exit doesn't seem to work, but this does. ¯\_(ツ)_/¯
    return KERN_FAILURE;
}

void *exception_handler(void *argument) {
    mach_port_t port = *(mach_port_t *)argument;
    mach_msg_server(exc_server, 2048, port, 0);
    return NULL;
}

static BOOL am_i_being_debugged() {
    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
    struct kinfo_proc info = {};
    size_t size = sizeof(info);
    return !sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) && !!(info.kp_proc.p_flag & P_TRACED);
}

int main(int argc, char * argv[]) {
    BOOL debugged = am_i_being_debugged();
    
    // Thanks to this comment: https://news.ycombinator.com/item?id=18431524
    // We use this hack to allow mmap with PROT_EXEC (which usually requires the
    // dynamic-codesigning entitlement) by tricking the process into thinking
    // that Xcode is debugging it. We abuse the fact that JIT is needed to
    // debug the process.
    ptrace(PT_TRACE_ME, 0, NULL, 0);
    
    // ptracing ourselves confuses the kernel and will cause bad things to
    // happen to the system (hangs…) if an exception or signal occurs. Setup
    // some "safety nets" so we can cause the process to exit in a somewhat sane
    // state. We only need to do this if the debugger isn't attached. (It'll do
    // this itself, and if we do it we'll interfere with its normal operation
    // anyways.)
    if (!debugged) {
        // First, ensure that signals are delivered as Mach software exceptions…
        ptrace(PT_SIGEXC, 0, NULL, 0);