Skip to content

Instantly share code, notes, and snippets.

@EthanArbuckle
Created February 2, 2025 01:12
Show Gist options
  • Save EthanArbuckle/372f97a92b681aaf58b69d47f30d9e6d to your computer and use it in GitHub Desktop.
Save EthanArbuckle/372f97a92b681aaf58b69d47f30d9e6d to your computer and use it in GitHub Desktop.
Make Music.app run in iOS Simulator
//
// sim_dylib_loader.m
// objsee
//
// Created by Ethan Arbuckle on 1/31/25.
//
#include <Foundation/Foundation.h>
#include <objc/runtime.h>
#include <dlfcn.h>
#define WHILE_IGNORING_SIGNALS(code) ({ \
int __result = 0; \
struct sigaction __old_sa_segv, __old_sa_bus; \
if (!g_sig_ignoring) { \
g_sig_ignoring = 1; \
install_signal_handlers(&__old_sa_segv, &__old_sa_bus); \
if ((__result = sigsetjmp(g_sig_ignore_jmpbuf, 1)) == 0) { \
code; \
} \
restore_signal_handlers(&__old_sa_segv, &__old_sa_bus); \
g_sig_ignoring = 0; \
} \
__result; \
})
static __thread sigjmp_buf g_sig_ignore_jmpbuf;
static __thread volatile sig_atomic_t g_sig_ignoring = 0;
static void _sig_ignoring_handler(int signo) {
if (g_sig_ignoring) {
siglongjmp(g_sig_ignore_jmpbuf, signo);
}
}
static inline void install_signal_handlers(struct sigaction * _Nonnull old_sa_segv, struct sigaction * _Nonnull old_sa_bus) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = _sig_ignoring_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, old_sa_segv);
sigaction(SIGBUS, &sa, old_sa_bus);
}
static inline void restore_signal_handlers(const struct sigaction * _Nonnull old_sa_segv, const struct sigaction * _Nonnull old_sa_bus) {
sigaction(SIGSEGV, old_sa_segv, NULL);
sigaction(SIGBUS, old_sa_bus, NULL);
}
__attribute__((constructor)) static void launch_apple_music(void) {
NSString *processName = [[NSProcessInfo processInfo] processName];
if (![processName isEqualToString:@"Music"]) {
return;
}
// Codesigning limitations make it such that Music possesses no entitlements when installed under Simulator.
// Missing entitlements cause several issues that are worked around here with method swizzling
dlopen("/System/Library/Frameworks/CloudKit.framework/CloudKit", RTLD_NOW);
// CloudKit raises an exception about missing container ID. Fix by hardcoding a container ID in `-[CKContainerID initWithContainerIdentifier:environment:]`
SEL initWithContainerIdentifier = sel_registerName("initWithContainerIdentifier:environment:");
Method initWithContainerIdentifier_method = class_getInstanceMethod(objc_getClass("CKContainerID"), initWithContainerIdentifier);
IMP orig_initWithContainerIdentifier_imp = method_getImplementation(initWithContainerIdentifier_method);
IMP newImp = imp_implementationWithBlock(^(__unsafe_unretained id self, NSString *containerID, id environment) {
return ((id (*)(id, SEL, id, id))orig_initWithContainerIdentifier_imp)(self, initWithContainerIdentifier, @"fake", environment);
});
method_setImplementation(initWithContainerIdentifier_method, newImp);
// Mock in-process entitlement checks
SEL hasBoolEntitlement = sel_registerName("hasBoolEntitlement:shouldLogForMissingEntitlement:");
Method hasBoolEntitlementMethod = class_getClassMethod(objc_getClass("NSObject"), hasBoolEntitlement);
IMP newHasBoolEntitlement = imp_implementationWithBlock(^BOOL(Class self, NSString *entitlement, BOOL shouldLog) {
return YES;
});
method_setImplementation(hasBoolEntitlementMethod, newHasBoolEntitlement);
// Crashes are expected, so kick off UIApplicationMain in a signal-ignoring context.
// On success, run the runloop to keep the bootstrapped app alive.
// On failure, the launch will be attempted again by the app's usual entry point
WHILE_IGNORING_SIGNALS({
void *_UIApplicationMain = dlsym(dlopen(NULL, 0), "UIApplicationMain");
if (_UIApplicationMain) {
((void (*)(int, char **, id, id))_UIApplicationMain)(0, NULL, nil, @"_TtC5Music19ApplicationDelegate");
CFRunLoopRun();
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment