/* 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);
}
}