From c7954e363c466587f29cf8b22ccdbd1ad6d0a7f4 Mon Sep 17 00:00:00 2001
From: hrtowii <68852354+hrtowii@users.noreply.github.com>
Date: Wed, 24 Jul 2024 19:07:21 +0800
Subject: [PATCH] work on jitterd, im gonna sleep

---
 Makefile                                      |  2 +-
 RootHelperSample/TSUtil.m                     | 53 +------------
 RootHelperSample/jbroot.h                     |  2 +
 RootHelperSample/jbroot.m                     | 73 ++++++++++++++++++
 .../launchdhook/jbserver/jbclient_xpc.c       | 76 ++++++++++++++++++
 .../launchdhook/jbserver/jbclient_xpc.h       |  1 +
 .../jbserver/jbdomain_systemwide.c            | 43 +++++++----
 .../launchdshim/launchdhook/jitter/jitter.m   |  7 +-
 .../launchdhook/jitter/jitterd.plist          |  6 +-
 .../launchdshim/launchdhook/main.m            |  8 +-
 RootHelperSample/main.m                       | 77 +------------------
 Serotonin.xcodeproj/project.pbxproj           |  4 +
 12 files changed, 202 insertions(+), 150 deletions(-)
 create mode 100644 RootHelperSample/jbroot.h
 create mode 100644 RootHelperSample/jbroot.m

diff --git a/Makefile b/Makefile
index 8ae1528..f05be83 100644
--- a/Makefile
+++ b/Makefile
@@ -67,7 +67,7 @@ Serotonin.tipa: $(wildcard **/*.c **/*.m **/*.swift **/*.plist **/*.xml)
 	install -m755 RootHelperSample/launchdshim/launchdhook/launchdhooksigned.dylib Payload/Serotonin.app/launchdhooksigned.dylib
 	install -m755 RootHelperSample/launchdshim/generalhook/generalhook.dylib Payload/Serotonin.app/generalhooksigned.dylib
 	install -m755 RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.dylib Payload/Serotonin.app/xpcproxyhooksigned.dylib
-	install -m755 RootHelperSample/launchdshim/launchdhook/jitter/jitter Payload/Serotonin.app/jitter
+	install -m755 RootHelperSample/launchdshim/launchdhook/jitter/jitter Payload/Serotonin.app/jitterd
 	$(LDID) -S./RootHelperSample/entitlements.plist -Cadhoc Payload/Serotonin.app/{fastPathSign,ldid,serotoninroothelper}
 	$(LDID) -Sent.plist -Cadhoc Payload/Serotonin.app/Serotonin
 	zip -vr9 Serotonin.tipa Payload/ -x "*.DS_Store"
diff --git a/RootHelperSample/TSUtil.m b/RootHelperSample/TSUtil.m
index 99efd8e..1a89940 100644
--- a/RootHelperSample/TSUtil.m
+++ b/RootHelperSample/TSUtil.m
@@ -3,7 +3,7 @@
 #import <Foundation/Foundation.h>
 #import <spawn.h>
 #import <sys/sysctl.h>
-#include <IOKit/IOKitLib.h>
+#import "jbroot.h"
 
 @interface PSAppDataUsagePolicyCache : NSObject
 + (instancetype)sharedInstance;
@@ -215,54 +215,3 @@ void respring(void)
 	killall(@"SpringBoard");
 	exit(0);
 }
-
-int get_boot_manifest_hash(char hash[97])
-{
-  const UInt8 *bytes;
-  CFIndex length;
-  io_registry_entry_t chosen = IORegistryEntryFromPath(0, "IODeviceTree:/chosen");
-  if (!MACH_PORT_VALID(chosen)) return 1;
-  CFDataRef manifestHash = (CFDataRef)IORegistryEntryCreateCFProperty(chosen, CFSTR("boot-manifest-hash"), kCFAllocatorDefault, 0);
-  IOObjectRelease(chosen);
-  if (manifestHash == NULL || CFGetTypeID(manifestHash) != CFDataGetTypeID())
-  {
-    if (manifestHash != NULL) CFRelease(manifestHash);
-    return 1;
-  }
-  length = CFDataGetLength(manifestHash);
-  bytes = CFDataGetBytePtr(manifestHash);
-  for (int i = 0; i < length; i++)
-  {
-    snprintf(&hash[i * 2], 3, "%02X", bytes[i]);
-  }
-  CFRelease(manifestHash);
-  return 0;
-}
-
-char* return_boot_manifest_hash_main(void) {
-  static char hash[97];
-  int ret = get_boot_manifest_hash(hash);
-  if (ret != 0) {
-    fprintf(stderr, "could not get boot manifest hash\n");
-    return "lmao";
-  }
-    static char result[115];
-    sprintf(result, "/private/preboot/%s", hash);
-    return result;
-}
-
-char* getPatchedLaunchdCopy(void) {
-    char* prebootpath = return_boot_manifest_hash_main();
-    static char originallaunchd[256];
-    sprintf(originallaunchd, "%s/%s", prebootpath, "patchedlaunchd");
-    NSLog(@"patchedlaunchd: %s", originallaunchd);
-    return originallaunchd;
-}
-
-char* getOriginalLaunchdCopy(void) {
-    char* prebootpath = return_boot_manifest_hash_main();
-    static char originallaunchd[256];
-    sprintf(originallaunchd, "%s/%s", prebootpath, "patchedlaunchd");
-    NSLog(@"patchedlaunchd: %s", originallaunchd);
-    return originallaunchd;
-}
diff --git a/RootHelperSample/jbroot.h b/RootHelperSample/jbroot.h
new file mode 100644
index 0000000..4844ebb
--- /dev/null
+++ b/RootHelperSample/jbroot.h
@@ -0,0 +1,2 @@
+#include <Foundation/Foundation.h>
+NSString *jbroot(NSString *path);
\ No newline at end of file
diff --git a/RootHelperSample/jbroot.m b/RootHelperSample/jbroot.m
new file mode 100644
index 0000000..72e6d56
--- /dev/null
+++ b/RootHelperSample/jbroot.m
@@ -0,0 +1,73 @@
+#import <stdio.h>
+#include <sys/types.h>
+#import <Foundation/Foundation.h>
+
+#define JB_ROOT_PREFIX ".jbroot-"
+#define JB_RAND_LENGTH  (sizeof(uint64_t)*sizeof(char)*2)
+
+int is_jbrand_value(uint64_t value)
+{
+   uint8_t check = value>>8 ^ value >> 16 ^ value>>24 ^ value>>32 ^ value>>40 ^ value>>48 ^ value>>56;
+   return check == (uint8_t)value;
+}
+
+int is_jbroot_name(const char* name)
+{
+    if(strlen(name) != (sizeof(JB_ROOT_PREFIX)-1+JB_RAND_LENGTH))
+        return 0;
+    
+    if(strncmp(name, JB_ROOT_PREFIX, sizeof(JB_ROOT_PREFIX)-1) != 0)
+        return 0;
+    
+    char* endp=NULL;
+    uint64_t value = strtoull(name+sizeof(JB_ROOT_PREFIX)-1, &endp, 16);
+    if(!endp || *endp!='\0')
+        return 0;
+    
+    if(!is_jbrand_value(value))
+        return 0;
+    
+    return 1;
+}
+
+uint64_t resolve_jbrand_value(const char* name)
+{
+    if(strlen(name) != (sizeof(JB_ROOT_PREFIX)-1+JB_RAND_LENGTH))
+        return 0;
+    
+    if(strncmp(name, JB_ROOT_PREFIX, sizeof(JB_ROOT_PREFIX)-1) != 0)
+        return 0;
+    
+    char* endp=NULL;
+    uint64_t value = strtoull(name+sizeof(JB_ROOT_PREFIX)-1, &endp, 16);
+    if(!endp || *endp!='\0')
+        return 0;
+    
+    if(!is_jbrand_value(value))
+        return 0;
+    
+    return value;
+}
+
+
+NSString* find_jbroot()
+{
+    //jbroot path may change when re-randomize it
+    NSString * jbroot = nil;
+    NSArray *subItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/var/containers/Bundle/Application/" error:nil];
+    for (NSString *subItem in subItems) {
+        if (is_jbroot_name(subItem.UTF8String))
+        {
+            NSString* path = [@"/var/containers/Bundle/Application/" stringByAppendingPathComponent:subItem];
+            jbroot = path;
+            break;
+        }
+    }
+    return jbroot;
+}
+
+NSString *jbroot(NSString *path)
+{
+    NSString* jbroot = find_jbroot();
+    return [jbroot stringByAppendingPathComponent:path];
+}
\ No newline at end of file
diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c
index 19556f6..73bf97c 100644
--- a/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c
+++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c
@@ -6,6 +6,7 @@
 #include <pthread.h>
 #include <mach-o/dyld.h>
 #include <dlfcn.h>
+#include "sandbox.h"
 
 #define OS_ALLOC_ONCE_KEY_MAX    100
 
@@ -252,6 +253,81 @@ int jbclient_process_checkin(char **rootPathOut, char **bootUUIDOut, char **sand
 	return -1;
 }
 
+extern char **environ;
+kern_return_t bootstrap_look_up(mach_port_t port, const char *service, mach_port_t *server_port);
+
+bool jitterdSystemWideIsReachable(void)
+{
+	int sbc = sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT, "com.hrtowii.jitterd.systemwide");
+	return sbc == 0;
+}
+
+mach_port_t jitterdSystemWideMachPort(void)
+{
+	mach_port_t outPort = MACH_PORT_NULL;
+	kern_return_t kr = KERN_SUCCESS;
+
+	if (getpid() == 1) {
+		mach_port_t self_host = mach_host_self();
+		kr = host_get_special_port(self_host, HOST_LOCAL_NODE, 16, &outPort);
+		mach_port_deallocate(mach_task_self(), self_host);
+	}
+	else {
+		kr = bootstrap_look_up(bootstrap_port, "com.hrtowii.jitterd.systemwide", &outPort);
+	}
+
+	if (kr != KERN_SUCCESS) return MACH_PORT_NULL;
+	return outPort;
+}
+
+xpc_object_t sendLaunchdMessageFallback(xpc_object_t xdict)
+{
+	xpc_dictionary_set_bool(xdict, "jailbreak", true);
+	xpc_dictionary_set_bool(xdict, "jailbreak-systemwide", true);
+
+	void* pipePtr = NULL;
+	if(_os_alloc_once_table[1].once == -1)
+	{
+		pipePtr = _os_alloc_once_table[1].ptr;
+	}
+	else
+	{
+		pipePtr = _os_alloc_once(&_os_alloc_once_table[1], 472, NULL);
+		if (!pipePtr) _os_alloc_once_table[1].once = -1;
+	}
+
+	xpc_object_t xreply = NULL;
+	if (pipePtr) {
+		struct xpc_global_data* globalData = pipePtr;
+		xpc_object_t pipe = globalData->xpc_bootstrap_pipe;
+		if (pipe) {
+			int err = xpc_pipe_routine_with_flags(pipe, xdict, &xreply, 0);
+			if (err != 0) {
+				return NULL;
+			}
+		}
+	}
+	return xreply;
+}
+
+xpc_object_t sendjitterdMessageSystemWide(xpc_object_t xdict)
+{
+	xpc_object_t jitterd_xreply = NULL;
+	if (jitterdSystemWideIsReachable()) {
+		mach_port_t jitterdPort = jitterdSystemWideMachPort();
+		if (jitterdPort != -1) {
+			xpc_object_t pipe = xpc_pipe_create_from_port(jitterdPort, 0);
+			if (pipe) {
+				int err = xpc_pipe_routine(pipe, xdict, &jitterd_xreply);
+				if (err != 0) jitterd_xreply = NULL;
+				xpc_release(pipe);
+			}
+			mach_port_deallocate(mach_task_self(), jitterdPort);
+		}
+	}
+	return jitterd_xreply;
+}
+
 // int jbclient_fork_fix(uint64_t childPid)
 // {
 // 	xpc_object_t xargs = xpc_dictionary_create_empty();
diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h
index 12ea1a8..ad2e264 100644
--- a/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h
+++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h
@@ -21,6 +21,7 @@ int jbclient_cs_drop_get_task_allow(void);
 int jbclient_patch_spawn(int pid, bool resume);
 int jbclient_patch_exec_add(const char* exec_path, bool resume);
 int jbclient_patch_exec_del(const char* exec_path);
+xpc_object_t sendjitterdMessageSystemWide(xpc_object_t xdict);
 // int jbclient_platform_set_process_debugged(uint64_t pid, bool fullyDebugged);
 // int jbclient_platform_stage_jailbreak_update(const char *updateTar);
 // int jbclient_platform_jbsettings_get(const char *key, xpc_object_t *valueOut);
diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c b/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c
index f69e7f8..d751c8a 100644
--- a/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c
+++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c
@@ -18,17 +18,31 @@
 #include "spawnRoot.h"
 #include <roothide.h>
 #include "../fun/memoryControl.h"
-
+#include "jbclient_xpc.h"
+#define JBD_MSG_PROC_SET_DEBUGGED 23
 #define PT_DETACH       11      /* stop tracing a process */
 #define PT_ATTACHEXC    14      /* attach to running process with signal exception */
 int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
 
 #include <signal.h>
 
-int enableJIT(pid_t pid)
+// int enableJIT(pid_t pid)
+// {
+// 	int ret = spawnRoot(jbroot("/jitter"), pid, NULL, NULL);
+// 	return ret;
+// }
+int64_t jitterd(pid_t pid)
 {
-	int ret = spawnRoot(jbroot("/jitter"), pid, NULL, NULL);
-	return ret;
+	xpc_object_t message = xpc_dictionary_create_empty();
+	xpc_dictionary_set_int64(message, "id", JBD_MSG_PROC_SET_DEBUGGED);
+	xpc_dictionary_set_int64(message, "pid", pid);
+	xpc_object_t reply = sendjitterdMessageSystemWide(message);
+	int64_t result = -1;
+	if (reply) {
+		result  = xpc_dictionary_get_int64(reply, "result");
+		xpc_release(reply);
+	}
+	return result;
 }
 
 // extern bool stringStartsWith(const char *str, const char* prefix);
@@ -171,19 +185,18 @@ static int systemwide_process_checkin(audit_token_t *processToken, char **rootPa
 
 	// Generate sandbox extensions for the requesting process
 	*sandboxExtensionsOut = generate_sandbox_extensions(processToken, isPlatformProcess);
-
+	jitterd(pid);
 	// Allow invalid pages with ptrace instead :trol:
 	// terrible solution but ideally jitter would become a daemon later. temp fix to see if it works
-	memorystatus_memlimit_properties2_t mmprops;
-	int32_t old_memory_limit = 0;
-	uint32_t new_memory_limit = (uint32_t)(getPhysicalMemorySize() / UINT64_C(1048576)) * 2;
-    int ret = memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &mmprops, sizeof(mmprops));
-    if (ret == 0)
-    old_memory_limit = mmprops.v1.memlimit_active;
-    ret = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, pid, new_memory_limit, NULL, 0);
-	enableJIT(pid);
-	// set it back because yeah
-	ret = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, pid, old_memory_limit, NULL, 0);
+	// memorystatus_memlimit_properties2_t mmprops;
+	// int32_t old_memory_limit = 0;
+	// uint32_t new_memory_limit = (uint32_t)(getPhysicalMemorySize() / UINT64_C(1048576)) * 2;
+    // int ret = memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &mmprops, sizeof(mmprops));
+    // if (ret == 0)
+    // old_memory_limit = mmprops.v1.memlimit_active;
+    // ret = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, pid, new_memory_limit, NULL, 0);
+	// enableJIT(pid);
+	// ret = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, pid, old_memory_limit, NULL, 0);
 	
 	// bool fullyDebugged = true;
 	// if (is_app_path(procPath) || is_sub_path(JBRootPath("/Applications"), procPath)) {
diff --git a/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m b/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m
index 4893d70..f5e8eb0 100644
--- a/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m
+++ b/RootHelperSample/launchdshim/launchdhook/jitter/jitter.m
@@ -12,11 +12,12 @@
 #include <mach/mach_init.h>
 #include "../fun/memoryControl.h"
 #include "../jbserver/bsm/audit.h"
+#include "../jbserver/xpc_private.h"
 
 #define PT_DETACH       11      /* stop tracing a process */
 #define PT_ATTACHEXC    14      /* attach to running process with signal exception */
 #define MEMORYSTATUS_CMD_SET_JETSAM_HIGH_WATER_MARK 5
-#define JBD_MSG_DEBUG_ME 24
+#define JBD_MSG_PROC_SET_DEBUGGED 23
 
 int ptrace(int request, pid_t pid, caddr_t addr, int data);
 // void JBLogError(const char *format, ...);
@@ -58,8 +59,8 @@ void jitterd_received_message(mach_port_t machPort, bool systemwide)
         if (messageType == XPC_TYPE_DICTIONARY) {
             audit_token_t auditToken = {};
             xpc_dictionary_get_audit_token(message, &auditToken);
-            uid_t clientUid = audit_token_to_euid(auditToken);
-            pid_t clientPid = audit_token_to_pid(auditToken);
+            // uid_t clientUid = audit_token_to_euid(auditToken);
+            // pid_t clientPid = audit_token_to_pid(auditToken);
             msgId = xpc_dictionary_get_int64(message, "id");
             char *description = xpc_copy_description(message);
             free(description);
diff --git a/RootHelperSample/launchdshim/launchdhook/jitter/jitterd.plist b/RootHelperSample/launchdshim/launchdhook/jitter/jitterd.plist
index 442900c..3825075 100644
--- a/RootHelperSample/launchdshim/launchdhook/jitter/jitterd.plist
+++ b/RootHelperSample/launchdshim/launchdhook/jitter/jitterd.plist
@@ -16,10 +16,10 @@
 		<key>com.hrtowii.jitterd.systemwide</key>
 		<true/>
 	</dict>
-	<!-- <key>ProgramArguments</key>
+	<key>ProgramArguments</key>
 	<array>
-		<string>basebin/jailbreakd</string>
-	</array> -->
+		<string>jitterd</string>
+	</array>
 	<key>UserName</key>
 	<string>root</string>
 	<key>RunAtLoad</key>
diff --git a/RootHelperSample/launchdshim/launchdhook/main.m b/RootHelperSample/launchdshim/launchdhook/main.m
index 5227438..3075805 100644
--- a/RootHelperSample/launchdshim/launchdhook/main.m
+++ b/RootHelperSample/launchdshim/launchdhook/main.m
@@ -208,7 +208,7 @@ bool hook_xpc_dictionary_get_bool(xpc_object_t dictionary, const char *key) {
 xpc_object_t hook_xpc_dictionary_get_value(xpc_object_t dict, const char *key) {
     xpc_object_t retval = xpc_dictionary_get_value_orig(dict, key);
 
-    if (strcmp(key, "Paths") == 0) {
+    if (!strcmp(key, "Paths")) {
         const char *paths[] = {
             jbroot("/Library/LaunchDaemons"),
             jbroot("/System/Library/LaunchDaemons"),
@@ -219,7 +219,11 @@ xpc_object_t hook_xpc_dictionary_get_value(xpc_object_t dict, const char *key) {
         for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
             xpc_array_append_value(retval, xpc_string_create(paths[i]));
         }
-    }
+		if (xpc_get_type(retval) == XPC_TYPE_ARRAY) {
+			xpc_array_set_string(retval, XPC_ARRAY_APPEND, jbroot("/Library/LaunchDaemons")); // todo: copy jitterd daemon plist to serotonin app, roothelper copy from app to library/launchdaemons in jbroot
+		}
+	}
+    
 
     return retval;
 }
diff --git a/RootHelperSample/main.m b/RootHelperSample/main.m
index 2e68006..242d587 100644
--- a/RootHelperSample/main.m
+++ b/RootHelperSample/main.m
@@ -20,75 +20,7 @@
 #include <sys/types.h>
 #include "insert_dylib.h"
 #include "exepatch.h"
-#define JB_ROOT_PREFIX ".jbroot-"
-#define JB_RAND_LENGTH  (sizeof(uint64_t)*sizeof(char)*2)
-
-int is_jbrand_value(uint64_t value)
-{
-   uint8_t check = value>>8 ^ value >> 16 ^ value>>24 ^ value>>32 ^ value>>40 ^ value>>48 ^ value>>56;
-   return check == (uint8_t)value;
-}
-
-int is_jbroot_name(const char* name)
-{
-    if(strlen(name) != (sizeof(JB_ROOT_PREFIX)-1+JB_RAND_LENGTH))
-        return 0;
-    
-    if(strncmp(name, JB_ROOT_PREFIX, sizeof(JB_ROOT_PREFIX)-1) != 0)
-        return 0;
-    
-    char* endp=NULL;
-    uint64_t value = strtoull(name+sizeof(JB_ROOT_PREFIX)-1, &endp, 16);
-    if(!endp || *endp!='\0')
-        return 0;
-    
-    if(!is_jbrand_value(value))
-        return 0;
-    
-    return 1;
-}
-
-uint64_t resolve_jbrand_value(const char* name)
-{
-    if(strlen(name) != (sizeof(JB_ROOT_PREFIX)-1+JB_RAND_LENGTH))
-        return 0;
-    
-    if(strncmp(name, JB_ROOT_PREFIX, sizeof(JB_ROOT_PREFIX)-1) != 0)
-        return 0;
-    
-    char* endp=NULL;
-    uint64_t value = strtoull(name+sizeof(JB_ROOT_PREFIX)-1, &endp, 16);
-    if(!endp || *endp!='\0')
-        return 0;
-    
-    if(!is_jbrand_value(value))
-        return 0;
-    
-    return value;
-}
-
-
-NSString* find_jbroot()
-{
-    //jbroot path may change when re-randomize it
-    NSString * jbroot = nil;
-    NSArray *subItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/var/containers/Bundle/Application/" error:nil];
-    for (NSString *subItem in subItems) {
-        if (is_jbroot_name(subItem.UTF8String))
-        {
-            NSString* path = [@"/var/containers/Bundle/Application/" stringByAppendingPathComponent:subItem];
-            jbroot = path;
-            break;
-        }
-    }
-    return jbroot;
-}
-
-NSString *jbroot(NSString *path)
-{
-    NSString* jbroot = find_jbroot();
-    return [jbroot stringByAppendingPathComponent:path];
-}
+#include "jbroot.h"
 
 
 NSString* usprebooterPath()
@@ -333,11 +265,7 @@ void installClone(NSString *path) {
     NSString *stdErr;
     spawnRoot(fastPathSignPath, @[@"-i", jbroot(path), @"-r", @"-o", jbroot(path)], &stdOut, &stdErr);
 
-
     NSString *symlink_path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:@".jbroot"];
-    
-
-
     [[NSFileManager defaultManager] createSymbolicLinkAtPath:jbroot(symlink_path) withDestinationPath:jbroot(@"/") error:nil];
 }
 
@@ -373,7 +301,8 @@ int main(int argc, char *argv[], char *envp[]) {
                 
                 [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"generalhooksigned.dylib"] toPath:jbroot(@"/generalhooksigned.dylib") error:nil];
                 [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"jitter"] toPath:jbroot(@"/jitter") error:nil];
-                
+                [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"jitterd.plist"] toPath:jbroot(@"/Library/LaunchDaemons/com.hrtowii.jitterd.plist") error:nil];
+
 //                [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"Serotonin.jp2"] toPath:@"/var/mobile/Serotonin.jp2" error:nil];
             }
         } else if ([action isEqual: @"uninstall"]) {
diff --git a/Serotonin.xcodeproj/project.pbxproj b/Serotonin.xcodeproj/project.pbxproj
index 3b8e391..5ea4f4a 100644
--- a/Serotonin.xcodeproj/project.pbxproj
+++ b/Serotonin.xcodeproj/project.pbxproj
@@ -43,6 +43,7 @@
 		C8BFCCAC2B3FFE570008D8FD /* offsets.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BFCC882B3FFE560008D8FD /* offsets.m */; };
 		C8BFCCAD2B3FFE570008D8FD /* vnode.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BFCC892B3FFE560008D8FD /* vnode.m */; };
 		C8E70B7B2C48443A008AC2E6 /* mediaserverdents.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E70B792C48443A008AC2E6 /* mediaserverdents.plist */; };
+		C8E70B7D2C511339008AC2E6 /* jitterd.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E70B7C2C511339008AC2E6 /* jitterd.plist */; };
 		D6F9CF3F2B4B2F7D00274803 /* ct_bypass in Resources */ = {isa = PBXBuildFile; fileRef = D6F9CF3E2B4B2F7D00274803 /* ct_bypass */; };
 		D6F9CF412B4B306400274803 /* fastPathSign in Resources */ = {isa = PBXBuildFile; fileRef = D6F9CF402B4B306400274803 /* fastPathSign */; };
 /* End PBXBuildFile section */
@@ -152,6 +153,7 @@
 		C8BFCCA22B3FFE560008D8FD /* info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = info.h; sourceTree = "<group>"; };
 		C8BFCCA32B3FFE570008D8FD /* libkfd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libkfd.h; sourceTree = "<group>"; };
 		C8E70B792C48443A008AC2E6 /* mediaserverdents.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = mediaserverdents.plist; path = RootHelperSample/launchdshim/generalhook/mediaserverdents.plist; sourceTree = "<group>"; };
+		C8E70B7C2C511339008AC2E6 /* jitterd.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = jitterd.plist; path = RootHelperSample/launchdshim/launchdhook/jitter/jitterd.plist; sourceTree = "<group>"; };
 		D6E090922B48E60300AE49BF /* bootstrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bootstrap.h; sourceTree = "<group>"; };
 		D6E090942B48E60300AE49BF /* endpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endpoint.h; sourceTree = "<group>"; };
 		D6E090952B48E60300AE49BF /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = "<group>"; };
@@ -197,6 +199,7 @@
 		C81122912B15E7BB00AD077B = {
 			isa = PBXGroup;
 			children = (
+				C8E70B7C2C511339008AC2E6 /* jitterd.plist */,
 				C8E70B792C48443A008AC2E6 /* mediaserverdents.plist */,
 				C862EB622C47F3B30052F3AD /* nfcdents.plist */,
 				C862EB522C47A9710052F3AD /* installdents.plist */,
@@ -583,6 +586,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				C8E70B7D2C511339008AC2E6 /* jitterd.plist in Resources */,
 				C862EB602C47EE6D0052F3AD /* Serotonin.jp2 in Resources */,
 				C8E70B7B2C48443A008AC2E6 /* mediaserverdents.plist in Resources */,
 				C862EB532C47A9710052F3AD /* installdents.plist in Resources */,