/* Cydia Substrate - Powerful Code Insertion Platform * Copyright (C) 2008-2010 Jay Freeman (saurik) */ /* GNU Lesser General Public License, Version 3 {{{ */ /* * Substrate is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * Substrate is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Substrate. If not, see . **/ /* }}} */ #include #include #include #include #include #include #include #include #include #include "CydiaSubstrate.h" #define ForSaurik 0 static char MSWatch[PATH_MAX]; static void MSAction(int sig, siginfo_t *info, void *uap) { open(MSWatch, O_CREAT | O_RDWR, 0644); raise(sig); } #define Libraries_ "/Library/MobileSubstrate/DynamicLibraries" #define Safety_ "/Library/Frameworks/CydiaSubstrate.framework/MobileSafety.dylib" extern "C" char ***_NSGetArgv(void); MSInitialize { #ifndef __arm__ if (dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY | RTLD_NOLOAD) == NULL) return; #endif #if 1 CFBundleRef bundle(CFBundleGetMainBundle()); CFStringRef identifier(bundle == NULL ? NULL : CFBundleGetIdentifier(bundle)); #else CFBundleRef bundle; CFStringRef identifier; if ( dlopen("/usr/sbin/mediaserverd", RTLD_LAZY | RTLD_NOLOAD) != NULL || dlopen("/System/Library/PrivateFrameworks/CoreTelephony.framework/Support/CommCenter", RTLD_LAZY | RTLD_NOLOAD) != NULL ) { bundle = NULL; identifier = NULL; } else if (dlopen(Foundation_f, RTLD_LAZY | RTLD_NOLOAD) == NULL) return; else { bundle = CFBundleGetMainBundle(); identifier = bundle == NULL ? NULL : CFBundleGetIdentifier(bundle); if (identifier == NULL) return; } #endif char *argv0(**_NSGetArgv()); char *slash(strrchr(argv0, '/')); slash = slash == NULL ? argv0 : slash + 1; Class (*NSClassFromString)(CFStringRef) = reinterpret_cast(dlsym(RTLD_DEFAULT, "NSClassFromString")); CFLog(kCFLogLevelNotice, CFSTR("MS:Notice: Installing: %@ [%s] (%.2f)"), identifier, slash, kCFCoreFoundationVersionNumber); const char *dat(NULL); if (identifier != NULL && CFEqual(identifier, CFSTR("com.apple.springboard"))) dat = "com.saurik.mobilesubstrate.dat"; if (identifier == NULL && strcmp(slash, "CommCenter") == 0) dat = "com.saurik.MobileSubstrate.CommCenter.dat"; if (dat != NULL) { CFURLRef home(CFCopyHomeDirectoryURLForUser(NULL)); CFURLGetFileSystemRepresentation(home, TRUE, reinterpret_cast(MSWatch), sizeof(MSWatch)); CFRelease(home); strcat(MSWatch, "/Library/Preferences/"); strcat(MSWatch, dat); if (access(MSWatch, R_OK) == 0) { if (unlink(MSWatch) == -1) CFLog(kCFLogLevelError, CFSTR("MS:Error: Cannot Clear: %s"), strerror(errno)); void *handle(dlopen(Safety_, RTLD_LAZY | RTLD_GLOBAL)); if (handle == NULL) CFLog(kCFLogLevelError, CFSTR("MS:Error: Cannot Load: %s"), dlerror()); return; } stack_t stack; stack.ss_size = 8*1024; stack.ss_flags = 0; stack.ss_sp = malloc(stack.ss_size); bool stacked = false; if (stack.ss_sp != NULL) { if (sigaltstack(&stack, NULL) != -1) stacked = true; else CFLog(kCFLogLevelWarning, CFSTR("MS:Error: Cannot Stack: %s"), strerror(errno)); } struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_sigaction = &MSAction; action.sa_flags = SA_SIGINFO | SA_RESETHAND; if (stacked) action.sa_flags |= SA_ONSTACK; sigemptyset(&action.sa_mask); struct sigaction old; #define HookSignal(signum) \ sigaction(signum, NULL, &old); { \ sigaction(signum, &action, NULL); \ } HookSignal(SIGTRAP) HookSignal(SIGABRT) HookSignal(SIGILL) HookSignal(SIGBUS) HookSignal(SIGSEGV) HookSignal(SIGSYS) } CFURLRef libraries(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, reinterpret_cast(Libraries_), sizeof(Libraries_) - 1, TRUE)); CFBundleRef folder(CFBundleCreate(kCFAllocatorDefault, libraries)); CFRelease(libraries); if (folder == NULL) return; CFArrayRef dylibs(CFBundleCopyResourceURLsOfType(folder, CFSTR("dylib"), NULL)); CFRelease(folder); for (CFIndex i(0), count(CFArrayGetCount(dylibs)); i != count; ++i) { CFURLRef dylib(reinterpret_cast(CFArrayGetValueAtIndex(dylibs, i))); char path[PATH_MAX]; CFURLGetFileSystemRepresentation(dylib, TRUE, reinterpret_cast(path), sizeof(path)); size_t length(strlen(path)); memcpy(path + length - 5, "plist", 5); CFURLRef plist(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, reinterpret_cast(path), length, FALSE)); CFDataRef data; if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, plist, &data, NULL, NULL, NULL)) data = NULL; CFRelease(plist); CFDictionaryRef meta(NULL); if (data != NULL) { CFStringRef error; meta = reinterpret_cast(CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListImmutable, &error)); } bool load = true; if (meta != NULL) { if (CFDictionaryRef filter = reinterpret_cast(CFDictionaryGetValue(meta, CFSTR("Filter")))) { if (CFArrayRef version = reinterpret_cast(CFDictionaryGetValue(filter, CFSTR("CoreFoundationVersion")))) { load = false; if (CFIndex count = CFArrayGetCount(version)) { if (count > 2) { CFLog(kCFLogLevelError, CFSTR("MS:Error: Invalid CoreFoundationVersion: %@"), version); goto release; } CFNumberRef number; double value; number = reinterpret_cast(CFArrayGetValueAtIndex(version, 0)); CFNumberGetValue(number, kCFNumberDoubleType, &value); if (value > kCFCoreFoundationVersionNumber) goto release; if (count != 1) { number = reinterpret_cast(CFArrayGetValueAtIndex(version, 1)); CFNumberGetValue(number, kCFNumberDoubleType, &value); if (value <= kCFCoreFoundationVersionNumber) goto release; } } load = true; } bool any; if (CFStringRef mode = reinterpret_cast(CFDictionaryGetValue(filter, CFSTR("Mode")))) any = CFEqual(mode, CFSTR("Any")); else any = false; if (any) load = false; if (CFArrayRef executables = reinterpret_cast(CFDictionaryGetValue(filter, CFSTR("Executables")))) { if (!any) load = false; CFStringRef name(CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, slash, kCFStringEncodingUTF8, kCFAllocatorNull)); for (CFIndex i(0), count(CFArrayGetCount(executables)); i != count; ++i) { CFStringRef executable(reinterpret_cast(CFArrayGetValueAtIndex(executables, i))); if (CFEqual(executable, name)) { if (ForSaurik) CFLog(kCFLogLevelNotice, CFSTR("MS:Notice: Found: %@"), name); load = true; break; } } CFRelease(name); if (!any && !load) goto release; } if (CFArrayRef bundles = reinterpret_cast(CFDictionaryGetValue(filter, CFSTR("Bundles")))) { if (!any) load = false; for (CFIndex i(0), count(CFArrayGetCount(bundles)); i != count; ++i) { CFStringRef bundle(reinterpret_cast(CFArrayGetValueAtIndex(bundles, i))); if (CFBundleGetBundleWithIdentifier(bundle) != NULL) { if (ForSaurik) CFLog(kCFLogLevelNotice, CFSTR("MS:Notice: Found: %@"), bundle); load = true; break; } } if (!any && !load) goto release; } if (CFArrayRef classes = reinterpret_cast(CFDictionaryGetValue(filter, CFSTR("Classes")))) { if (!any) load = false; if (NSClassFromString != NULL) for (CFIndex i(0), count(CFArrayGetCount(classes)); i != count; ++i) { CFStringRef _class(reinterpret_cast(CFArrayGetValueAtIndex(classes, i))); if (NSClassFromString(_class) != NULL) { if (ForSaurik) CFLog(kCFLogLevelNotice, CFSTR("MS:Notice: Found: %@"), _class); load = true; break; } } if (!any && !load) goto release; } } release: CFRelease(meta); } if (!load) continue; memcpy(path + length - 5, "dylib", 5); CFLog(kCFLogLevelNotice, CFSTR("MS:Notice: Loading: %s"), path); if (MSWatch[0] != '\0') { int fd(open(MSWatch, O_CREAT | O_RDWR, 0644)); if (fd == -1) CFLog(kCFLogLevelError, CFSTR("MS:Error: Cannot Set: %s"), strerror(errno)); else if (close(fd) == -1) CFLog(kCFLogLevelError, CFSTR("MS:Error: Cannot Close: %s"), strerror(errno)); } void *handle(dlopen(path, RTLD_LAZY | RTLD_GLOBAL)); if (MSWatch[0] != '\0') if (unlink(MSWatch) == -1) CFLog(kCFLogLevelError, CFSTR("MS:Error: Cannot Reset: %s"), strerror(errno)); if (handle == NULL) { CFLog(kCFLogLevelError, CFSTR("MS:Error: %s"), dlerror()); continue; } } if (false) { CFLog(kCFLogLevelNotice, CFSTR("MobileSubstrate fell asleep... I'll wake him up in 10 seconds ;P")); sleep(10); } }