From 10fd0e0c7fc4459f65c1e6029d44cd522e18becb Mon Sep 17 00:00:00 2001
From: "Jay Freeman (saurik)" <saurik@saurik.com>
Date: Thu, 25 Jun 2015 10:19:08 +0000
Subject: [PATCH] On iOS 8.3, allow symbolic links as /Applications.

---
 .gitignore    |   1 +
 control       |   5 ++-
 extrainst_.mm | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++
 libuicache.mm |  27 +++++++++++++
 makefile      |  10 ++++-
 5 files changed, 143 insertions(+), 3 deletions(-)
 create mode 100644 libuicache.mm

diff --git a/.gitignore b/.gitignore
index f891275..cfeb3bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ uicache
 uiduid
 uiopen
 uishoot
+libuicache.dylib
diff --git a/control b/control
index 328d4f5..7fbeadb 100644
--- a/control
+++ b/control
@@ -4,9 +4,12 @@ Section: Utilities
 Maintainer: Jay Freeman (saurik) <saurik@saurik.com>
 Architecture: iphoneos-arm
 Version: 
+Essential: yes
 Description: UIKit/GraphicsServices command line access
 Name: UIKit Tools
 Author: Jay Freeman (saurik) <saurik@saurik.com>
 Depiction: http://cydia.saurik.com/info/uikittools/
-Depends: coreutils-bin
+Depends: coreutils-bin, firmware (<< 8.2) | ldid
+Replaces: taiguicache
+Conflicts: taiguicache
 Tag: purpose::console, role::hacker
diff --git a/extrainst_.mm b/extrainst_.mm
index a938b04..8d4d15b 100644
--- a/extrainst_.mm
+++ b/extrainst_.mm
@@ -44,6 +44,9 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <dlfcn.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
 
 #include "csstore.hpp"
 
@@ -129,6 +132,99 @@ void FixCache(NSString *home, NSString *plist) {
         printf("you must reboot to finalize your cache.\n");
 }
 
+#define INSTALLD "/usr/libexec/installd"
+
+static void *(*$memmem)(const void *, size_t, const void *, size_t);
+
+static bool PatchInstall() {
+    int fd(open(INSTALLD, O_RDWR));
+    if (fd == -1)
+        return false;
+
+    struct stat stat;
+    if (fstat(fd, &stat) == -1) {
+        close(fd);
+        return false;
+    }
+
+    size_t size(stat.st_size);
+    void *data(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+    close(fd);
+    if (data == MAP_FAILED)
+        return false;
+
+    bool changed(false);
+    for (;;) {
+        void *name($memmem(data, size, "__restrict", 10));
+        if (name == NULL)
+            break;
+        else {
+            memcpy(name, "__uicache_", 10);
+            changed = true;
+        }
+    }
+
+    munmap(data, size);
+
+    if (changed) {
+        system("ldid -s "INSTALLD"");
+        system("cp -af "INSTALLD" "INSTALLD"_");
+        system("mv -f "INSTALLD"_ "INSTALLD"");
+    }
+
+    return true;
+}
+
+#define SubstrateLaunchDaemons_ "/Library/LaunchDaemons"
+#define SubstrateVariable_ "DYLD_INSERT_LIBRARIES"
+#define SubstrateLibrary_ "/usr/lib/libuicache.dylib"
+
+static bool HookInstall() {
+    NSString *file([NSString stringWithFormat:@"%@/%s.plist", @ SubstrateLaunchDaemons_, "com.apple.mobile.installd"]);
+    if (file == nil)
+        return false;
+
+    NSMutableDictionary *root([NSMutableDictionary dictionaryWithContentsOfFile:file]);
+    if (root == nil)
+        return false;
+
+    NSMutableDictionary *environment([root objectForKey:@"EnvironmentVariables"]);
+    if (environment == nil) {
+        environment = [NSMutableDictionary dictionaryWithCapacity:1];
+        if (environment == nil)
+            return false;
+
+        [root setObject:environment forKey:@"EnvironmentVariables"];
+    }
+
+    NSString *variable([environment objectForKey:@ SubstrateVariable_]);
+    if (variable == nil || [variable length] == 0)
+        [environment setObject:@ SubstrateLibrary_ forKey:@ SubstrateVariable_];
+    else {
+        NSArray *dylibs([variable componentsSeparatedByString:@":"]);
+        if (dylibs == nil)
+            return false;
+
+        NSUInteger index([dylibs indexOfObject:@ SubstrateLibrary_]);
+        if (index != NSNotFound)
+            return false;
+
+        [environment setObject:[NSString stringWithFormat:@"%@:%@", variable, @ SubstrateLibrary_] forKey:@ SubstrateVariable_];
+    }
+
+    NSString *error;
+    NSData *data([NSPropertyListSerialization dataFromPropertyList:root format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]);
+    if (data == nil)
+        return false;
+
+    if (![data writeToFile:file atomically:YES])
+        return false;
+
+    system("launchctl unload /Library/LaunchDaemons/com.apple.mobile.installd.plist");
+    system("launchctl load /Library/LaunchDaemons/com.apple.mobile.installd.plist");
+    return true;
+}
+
 int main(int argc, const char *argv[]) {
     if (argc < 2 || (
         strcmp(argv[1], "install") != 0 &&
@@ -137,6 +233,13 @@ int main(int argc, const char *argv[]) {
 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
+    $memmem = reinterpret_cast<void *(*)(const void *, size_t, const void *, size_t)>(dlsym(RTLD_DEFAULT, "memmem"));
+
+    if (kCFCoreFoundationVersionNumber >= 1143) // XXX: iOS 8.3+
+        if (PatchInstall())
+            if (HookInstall())
+                FinishCydia("reboot");
+
     if (kCFCoreFoundationVersionNumber >= 700 && kCFCoreFoundationVersionNumber < 800) { // XXX: iOS 6.x
         NSString *home(@"/var/mobile");
         NSString *plist([NSString stringWithFormat:@"%@/Library/Caches/com.apple.mobile.installation.plist", home]);
diff --git a/libuicache.mm b/libuicache.mm
new file mode 100644
index 0000000..800beb9
--- /dev/null
+++ b/libuicache.mm
@@ -0,0 +1,27 @@
+#include <objc/runtime.h>
+#include <Foundation/Foundation.h>
+
+@interface MIFileManager
++ (MIFileManager *) defaultManager;
+- (NSURL *) destinationOfSymbolicLinkAtURL:(NSURL *)url error:(NSError *)error;
+@end
+
+static Class $MIFileManager;
+
+static NSArray *(*_MIFileManager$urlsForItemsInDirectoryAtURL$ignoringSymlinks$error$)(MIFileManager *self, SEL _cmd, NSURL *url, BOOL ignoring, NSError *error);
+
+static NSArray *$MIFileManager$urlsForItemsInDirectoryAtURL$ignoringSymlinks$error$(MIFileManager *self, SEL _cmd, NSURL *url, BOOL ignoring, NSError *error) {
+    MIFileManager *manager(reinterpret_cast<MIFileManager *>([$MIFileManager defaultManager]));
+    if (NSURL *destiny = [manager destinationOfSymbolicLinkAtURL:url error:NULL])
+        url = destiny;
+    return _MIFileManager$urlsForItemsInDirectoryAtURL$ignoringSymlinks$error$(self, _cmd, url, NO, error);
+}
+
+__attribute__((__constructor__))
+static void initialize() {
+    $MIFileManager = objc_getClass("MIFileManager");
+    SEL sel(@selector(urlsForItemsInDirectoryAtURL:ignoringSymlinks:error:));
+    Method method(class_getInstanceMethod($MIFileManager, sel));
+    _MIFileManager$urlsForItemsInDirectoryAtURL$ignoringSymlinks$error$ = reinterpret_cast<NSArray *(*)(MIFileManager *, SEL, NSURL *, BOOL, NSError *)>(method_getImplementation(method));
+    method_setImplementation(method, reinterpret_cast<IMP>(&$MIFileManager$urlsForItemsInDirectoryAtURL$ignoringSymlinks$error$));
+}
diff --git a/makefile b/makefile
index 27410ef..7ba5ac8 100644
--- a/makefile
+++ b/makefile
@@ -1,4 +1,4 @@
-uikittools = uiduid uicache uiopen gssc sbdidlaunch sbreload cfversion iomfsetgamma
+uikittools = uiduid uicache uiopen gssc sbdidlaunch sbreload cfversion iomfsetgamma libuicache.dylib
 
 all: $(uikittools)
 
@@ -19,6 +19,10 @@ extrainst_ := -framework CoreFoundation -framework Foundation
 uicache: csstore.cpp
 extrainst_: csstore.cpp
 
+%.dylib: %.mm
+	$${PKG_TARG}-g++ -Wall -Werror -dynamiclib -o $@ $^ $($@) -F"$${PKG_ROOT}"/System/Library/PrivateFrameworks -lobjc -framework CoreFoundation -framework Foundation
+	ldid -S $@
+
 %: %.mm
 	$${PKG_TARG}-g++ -Wall -Werror -o $@ $^ $($@) -F"$${PKG_ROOT}"/System/Library/PrivateFrameworks -lobjc
 	ldid -S$(wildcard $@.xml) $@
@@ -33,8 +37,10 @@ iomfsetgamma: iomfsetgamma.c
 
 package: all extrainst_
 	rm -rf _
+	mkdir -p _/usr/lib
+	cp -a $(filter %.dylib,$(uikittools)) _/usr/lib
 	mkdir -p _/usr/bin
-	cp -a $(uikittools) _/usr/bin
+	cp -a $(filter-out %.dylib,$(uikittools)) _/usr/bin
 	mkdir -p _/DEBIAN
 	./control.sh _ >_/DEBIAN/control
 	cp -a extrainst_ _/DEBIAN/
-- 
2.45.2