├── README.md ├── launchr.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── launchr.xcscheme └── xcuserdata │ └── jim.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── launchr ├── PrivateApiSupport.h ├── dyld_patch.c ├── dyld_patch.h ├── entitlements.plist └── main.m /README.md: -------------------------------------------------------------------------------- 1 | # launchr 2 | 3 | Tool for launching graphical iOS and macOS apps on ARM Macs, with full control. 4 | 5 | ## Usage 6 | 7 | ```console 8 | Usage: launchr [-platform macos|ios] [-allowinterpose yes] [-mode suspended|running] [-envfile ] -exec 9 | ``` 10 | 11 | Example `env.plist`: 12 | 13 | ```xml 14 | 15 | 16 | 17 | 18 | DYLD_INSERT_LIBRARIES 19 | hax.dylib 20 | 21 | 22 | ``` 23 | 24 | ## A Note On AMFI 25 | In order to run ad-hoc signed code with platform set to iOS, AMFI must be disabled (if you know a workaround, let me know!). With AMFI enabled, you'll likely get an error like the following: 26 | 27 | ```console 28 | default 21:27:49.339550+0200 kernel AMFI: '/Users/jim/Desktop/MobileSafari.app/MobileSafari': unsuitable CT policy 0 for this platform/device, rejecting signature. 29 | default 21:27:49.339554+0200 kernel AMFI: code signature validation failed. 30 | 31 | ``` 32 | 33 | Disable AMFI by adding a boot parameter (note that SIP needs to be disabled for this to be allowed): 34 | 35 | ```console 36 | sudo nvram boot-args="amfi_get_out_of_my_way=1" 37 | ``` 38 | 39 | ## Credits 40 | 41 | * dyld patching code: Samuel Groß (Google Project Zero). 42 | -------------------------------------------------------------------------------- /launchr.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 38533C7F26BFD45000AA386B /* dyld_patch.c in Sources */ = {isa = PBXBuildFile; fileRef = 38533C7E26BFD45000AA386B /* dyld_patch.c */; }; 11 | 38CB89C026906625008C88AD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 38CB89BF26906625008C88AD /* main.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 38CB89BA26906625008C88AD /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 383B3CD42691C0920076E024 /* PrivateApiSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrivateApiSupport.h; sourceTree = ""; }; 28 | 38533C7D26BFD45000AA386B /* dyld_patch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dyld_patch.h; sourceTree = ""; }; 29 | 38533C7E26BFD45000AA386B /* dyld_patch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dyld_patch.c; sourceTree = ""; }; 30 | 38533C8126BFE73000AA386B /* entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = entitlements.plist; sourceTree = ""; }; 31 | 38CB89BC26906625008C88AD /* launchr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = launchr; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 38CB89BF26906625008C88AD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 38CB89B926906625008C88AD /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 38CB89B326906625008C88AD = { 47 | isa = PBXGroup; 48 | children = ( 49 | 38CB89BE26906625008C88AD /* launchr */, 50 | 38CB89BD26906625008C88AD /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | 38CB89BD26906625008C88AD /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 38CB89BC26906625008C88AD /* launchr */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | 38CB89BE26906625008C88AD /* launchr */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 38CB89BF26906625008C88AD /* main.m */, 66 | 383B3CD42691C0920076E024 /* PrivateApiSupport.h */, 67 | 38533C7D26BFD45000AA386B /* dyld_patch.h */, 68 | 38533C7E26BFD45000AA386B /* dyld_patch.c */, 69 | 38533C8126BFE73000AA386B /* entitlements.plist */, 70 | ); 71 | path = launchr; 72 | sourceTree = ""; 73 | }; 74 | /* End PBXGroup section */ 75 | 76 | /* Begin PBXNativeTarget section */ 77 | 38CB89BB26906625008C88AD /* launchr */ = { 78 | isa = PBXNativeTarget; 79 | buildConfigurationList = 38CB89C326906625008C88AD /* Build configuration list for PBXNativeTarget "launchr" */; 80 | buildPhases = ( 81 | 38CB89B826906625008C88AD /* Sources */, 82 | 38CB89B926906625008C88AD /* Frameworks */, 83 | 38CB89BA26906625008C88AD /* CopyFiles */, 84 | ); 85 | buildRules = ( 86 | ); 87 | dependencies = ( 88 | ); 89 | name = launchr; 90 | productName = launchr; 91 | productReference = 38CB89BC26906625008C88AD /* launchr */; 92 | productType = "com.apple.product-type.tool"; 93 | }; 94 | /* End PBXNativeTarget section */ 95 | 96 | /* Begin PBXProject section */ 97 | 38CB89B426906625008C88AD /* Project object */ = { 98 | isa = PBXProject; 99 | attributes = { 100 | LastUpgradeCheck = 1250; 101 | TargetAttributes = { 102 | 38CB89BB26906625008C88AD = { 103 | CreatedOnToolsVersion = 12.5.1; 104 | }; 105 | }; 106 | }; 107 | buildConfigurationList = 38CB89B726906625008C88AD /* Build configuration list for PBXProject "launchr" */; 108 | compatibilityVersion = "Xcode 9.3"; 109 | developmentRegion = en; 110 | hasScannedForEncodings = 0; 111 | knownRegions = ( 112 | en, 113 | Base, 114 | ); 115 | mainGroup = 38CB89B326906625008C88AD; 116 | productRefGroup = 38CB89BD26906625008C88AD /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | 38CB89BB26906625008C88AD /* launchr */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXSourcesBuildPhase section */ 126 | 38CB89B826906625008C88AD /* Sources */ = { 127 | isa = PBXSourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 38CB89C026906625008C88AD /* main.m in Sources */, 131 | 38533C7F26BFD45000AA386B /* dyld_patch.c in Sources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXSourcesBuildPhase section */ 136 | 137 | /* Begin XCBuildConfiguration section */ 138 | 38CB89C126906625008C88AD /* Debug */ = { 139 | isa = XCBuildConfiguration; 140 | buildSettings = { 141 | ALWAYS_SEARCH_USER_PATHS = NO; 142 | CLANG_ANALYZER_NONNULL = YES; 143 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 144 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 145 | CLANG_CXX_LIBRARY = "libc++"; 146 | CLANG_ENABLE_MODULES = YES; 147 | CLANG_ENABLE_OBJC_ARC = YES; 148 | CLANG_ENABLE_OBJC_WEAK = YES; 149 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 150 | CLANG_WARN_BOOL_CONVERSION = YES; 151 | CLANG_WARN_COMMA = YES; 152 | CLANG_WARN_CONSTANT_CONVERSION = YES; 153 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 154 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 155 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 156 | CLANG_WARN_EMPTY_BODY = YES; 157 | CLANG_WARN_ENUM_CONVERSION = YES; 158 | CLANG_WARN_INFINITE_RECURSION = YES; 159 | CLANG_WARN_INT_CONVERSION = YES; 160 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 161 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 162 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 163 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 164 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 165 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 166 | CLANG_WARN_STRICT_PROTOTYPES = YES; 167 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 168 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 169 | CLANG_WARN_UNREACHABLE_CODE = YES; 170 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 171 | COPY_PHASE_STRIP = NO; 172 | DEBUG_INFORMATION_FORMAT = dwarf; 173 | ENABLE_STRICT_OBJC_MSGSEND = YES; 174 | ENABLE_TESTABILITY = YES; 175 | GCC_C_LANGUAGE_STANDARD = gnu11; 176 | GCC_DYNAMIC_NO_PIC = NO; 177 | GCC_NO_COMMON_BLOCKS = YES; 178 | GCC_OPTIMIZATION_LEVEL = 0; 179 | GCC_PREPROCESSOR_DEFINITIONS = ( 180 | "DEBUG=1", 181 | "$(inherited)", 182 | ); 183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 185 | GCC_WARN_UNDECLARED_SELECTOR = YES; 186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 187 | GCC_WARN_UNUSED_FUNCTION = YES; 188 | GCC_WARN_UNUSED_VARIABLE = YES; 189 | MACOSX_DEPLOYMENT_TARGET = 11.3; 190 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 191 | MTL_FAST_MATH = YES; 192 | ONLY_ACTIVE_ARCH = YES; 193 | SDKROOT = macosx; 194 | }; 195 | name = Debug; 196 | }; 197 | 38CB89C226906625008C88AD /* Release */ = { 198 | isa = XCBuildConfiguration; 199 | buildSettings = { 200 | ALWAYS_SEARCH_USER_PATHS = NO; 201 | CLANG_ANALYZER_NONNULL = YES; 202 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 203 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 204 | CLANG_CXX_LIBRARY = "libc++"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_ENABLE_OBJC_WEAK = YES; 208 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_COMMA = YES; 211 | CLANG_WARN_CONSTANT_CONVERSION = YES; 212 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 213 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 214 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INFINITE_RECURSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 220 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 224 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 225 | CLANG_WARN_STRICT_PROTOTYPES = YES; 226 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 227 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 228 | CLANG_WARN_UNREACHABLE_CODE = YES; 229 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 230 | COPY_PHASE_STRIP = NO; 231 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 232 | ENABLE_NS_ASSERTIONS = NO; 233 | ENABLE_STRICT_OBJC_MSGSEND = YES; 234 | GCC_C_LANGUAGE_STANDARD = gnu11; 235 | GCC_NO_COMMON_BLOCKS = YES; 236 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 237 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 238 | GCC_WARN_UNDECLARED_SELECTOR = YES; 239 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 240 | GCC_WARN_UNUSED_FUNCTION = YES; 241 | GCC_WARN_UNUSED_VARIABLE = YES; 242 | MACOSX_DEPLOYMENT_TARGET = 11.3; 243 | MTL_ENABLE_DEBUG_INFO = NO; 244 | MTL_FAST_MATH = YES; 245 | SDKROOT = macosx; 246 | }; 247 | name = Release; 248 | }; 249 | 38CB89C426906625008C88AD /* Debug */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | CODE_SIGN_IDENTITY = "-"; 253 | CODE_SIGN_STYLE = Manual; 254 | DEVELOPMENT_TEAM = ""; 255 | ENABLE_HARDENED_RUNTIME = YES; 256 | PRODUCT_NAME = "$(TARGET_NAME)"; 257 | PROVISIONING_PROFILE_SPECIFIER = ""; 258 | }; 259 | name = Debug; 260 | }; 261 | 38CB89C526906625008C88AD /* Release */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | CODE_SIGN_ENTITLEMENTS = launchr/launchrRelease.entitlements; 265 | CODE_SIGN_IDENTITY = "-"; 266 | CODE_SIGN_STYLE = Manual; 267 | DEVELOPMENT_TEAM = ""; 268 | ENABLE_HARDENED_RUNTIME = NO; 269 | PRODUCT_NAME = "$(TARGET_NAME)"; 270 | PROVISIONING_PROFILE_SPECIFIER = ""; 271 | }; 272 | name = Release; 273 | }; 274 | /* End XCBuildConfiguration section */ 275 | 276 | /* Begin XCConfigurationList section */ 277 | 38CB89B726906625008C88AD /* Build configuration list for PBXProject "launchr" */ = { 278 | isa = XCConfigurationList; 279 | buildConfigurations = ( 280 | 38CB89C126906625008C88AD /* Debug */, 281 | 38CB89C226906625008C88AD /* Release */, 282 | ); 283 | defaultConfigurationIsVisible = 0; 284 | defaultConfigurationName = Release; 285 | }; 286 | 38CB89C326906625008C88AD /* Build configuration list for PBXNativeTarget "launchr" */ = { 287 | isa = XCConfigurationList; 288 | buildConfigurations = ( 289 | 38CB89C426906625008C88AD /* Debug */, 290 | 38CB89C526906625008C88AD /* Release */, 291 | ); 292 | defaultConfigurationIsVisible = 0; 293 | defaultConfigurationName = Release; 294 | }; 295 | /* End XCConfigurationList section */ 296 | }; 297 | rootObject = 38CB89B426906625008C88AD /* Project object */; 298 | } 299 | -------------------------------------------------------------------------------- /launchr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /launchr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /launchr.xcodeproj/xcshareddata/xcschemes/launchr.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 61 | 63 | 69 | 70 | 71 | 72 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /launchr.xcodeproj/xcuserdata/jim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /launchr.xcodeproj/xcuserdata/jim.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | launchr.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 38CB89BB26906625008C88AD 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /launchr/PrivateApiSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateApiSupport.h 3 | // launchr 4 | // 5 | // Created by jim on 04/07/2021. 6 | // 7 | 8 | #ifndef PrivateApiSupport_h 9 | #define PrivateApiSupport_h 10 | 11 | #define PLATFORM_IOS 2 12 | #define PLATFORM_MACOS 1 13 | 14 | @class RBSProcessIdentity; 15 | 16 | @interface RBSProcessIdentity : NSObject 17 | + (id)identityOfCurrentProcess; 18 | + (id)identityForApplicationJobLabel:(id)arg1 bundleID:(id)arg2 platform:(int)arg3; 19 | @end 20 | 21 | @interface RBSLaunchContext : NSObject 22 | @property (nonatomic,copy) NSDictionary * environment; 23 | @property (nonatomic,copy) NSString * standardOutputPath; 24 | @property (nonatomic,copy) NSString * standardErrorPath; 25 | @property (nonatomic,copy) NSString * executablePath; 26 | @property (nonatomic) unsigned long lsSpawnFlags; 27 | @property (nonatomic) BOOL lsManageRoleOnly; 28 | @property (nonatomic) unsigned int lsInitialRole; 29 | @property (nonatomic) unsigned int executionOptions; 30 | 31 | + (id)contextWithIdentity:(id)arg1; 32 | + (id)context; 33 | @end 34 | 35 | @interface RBSLaunchRequest : NSObject 36 | - (_Bool)execute:(out id *)arg1 error:(out id *)arg2; 37 | - (id)initWithContext:(id)arg1; 38 | @end 39 | 40 | @interface MIInstallerClient : NSObject 41 | - (id)init; 42 | - (void)fetchInfoForAppWithBundleID:(id)arg1 wrapperURL:(id)arg2 completion:(id)arg3; 43 | @end 44 | 45 | @interface RBSProcessHandle : NSObject 46 | + (id)handleForPredicate:(id)arg1 error:(out id*)arg2; 47 | - (int) rbs_pid; 48 | @end 49 | 50 | @interface RBSProcessPredicate : NSObject 51 | + (id)predicateMatchingIdentity:(id)arg1; 52 | @end 53 | #endif /* PrivateApiSupport_h */ 54 | -------------------------------------------------------------------------------- /launchr/dyld_patch.c: -------------------------------------------------------------------------------- 1 | // 2 | // dyld_patch.c 3 | // launchr 4 | // 5 | // Taken straight from 6 | // All credits go to Samuel Groß (Project Zero) 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "dyld_patch.h" 21 | 22 | #define page_align(addr) (vm_address_t)((uintptr_t)(addr) & (~(vm_page_size - 1))) 23 | 24 | extern char **environ; 25 | 26 | int patch_dyld_in_process(pid_t pid) { 27 | kern_return_t kr; 28 | task_t task; 29 | // puts("[*] Patching child process to allow dyld interposing..."); 30 | // Find patch point 31 | FILE* output = popen("nm -arch arm64e /usr/lib/dyld | grep _amfi_check_dyld_policy_self", "r"); 32 | unsigned int patch_offset; 33 | int r = fscanf(output, "%x t _amfi_check_dyld_policy_self", &patch_offset); 34 | if (r != 1) { 35 | // printf("Failed to find offset of _amfi_check_dyld_policy_self in /usr/lib/dyld\n"); 36 | return -1; 37 | } 38 | // printf("[*] _amfi_check_dyld_policy_self at offset 0x%x in /usr/lib/dyld\n", patch_offset); 39 | 40 | // Attach to the target process 41 | kr = task_for_pid(mach_task_self(), pid, &task); 42 | if (kr != KERN_SUCCESS) { 43 | // printf("task_for_pid failed. Is this binary signed and possesses the com.apple.security.cs.debugger entitlement?\n"); 44 | return -1; 45 | } 46 | vm_address_t dyld_addr = 0; 47 | int headers_found = 0; 48 | vm_address_t addr = 0; 49 | vm_size_t size; 50 | vm_region_submap_info_data_64_t info; 51 | mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; 52 | unsigned int depth = 0; 53 | while (1) { 54 | // get next memory region 55 | kr = vm_region_recurse_64(task, &addr, &size, &depth, (vm_region_info_t)&info, &info_count); 56 | if (kr != KERN_SUCCESS) 57 | break; 58 | unsigned int header; 59 | vm_size_t bytes_read; 60 | kr = vm_read_overwrite(task, addr, 4, (vm_address_t)&header, &bytes_read); 61 | if (kr != KERN_SUCCESS) { 62 | // TODO handle this, some mappings are probably just not readable 63 | // printf("vm_read_overwrite failed\n"); 64 | return -1; 65 | } 66 | if (bytes_read != 4) { 67 | // TODO handle this properly 68 | // printf("[-] vm_read read to few bytes\n"); 69 | return -1; 70 | } 71 | if (header == 0xfeedfacf) { 72 | headers_found++; 73 | } 74 | if (headers_found == 2) { 75 | // This is dyld 76 | dyld_addr = addr; 77 | break; 78 | } 79 | addr += size; 80 | } 81 | if (dyld_addr == 0) { 82 | // printf("[-] Failed to find /usr/lib/dyld\n"); 83 | return -1; 84 | } 85 | // printf("[*] /usr/lib/dyld mapped at 0x%lx\n", dyld_addr); 86 | vm_address_t patch_addr = dyld_addr + patch_offset; 87 | // VM_PROT_COPY forces COW, probably, see vm_map_protect in vm_map.c 88 | kr = vm_protect(task, page_align(patch_addr), vm_page_size, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); 89 | if (kr != KERN_SUCCESS) { 90 | // printf("vm_protect failed\n"); 91 | return -1; 92 | } 93 | 94 | // MOV X8, 0x5f 95 | // STR X8, [X1] 96 | // RET 97 | const char* code = "\xe8\x0b\x80\xd2\x28\x00\x00\xf9\xc0\x03\x5f\xd6"; 98 | kr = vm_write(task, patch_addr, (vm_offset_t)code, 12); 99 | if (kr != KERN_SUCCESS) { 100 | // printf("vm_write failed\n"); 101 | return -1; 102 | } 103 | kr = vm_protect(task, page_align(patch_addr), vm_page_size, false, VM_PROT_READ | VM_PROT_EXECUTE); 104 | if (kr != KERN_SUCCESS) { 105 | // printf("vm_protect failed\n"); 106 | return -1; 107 | } 108 | 109 | return 0; 110 | // puts("[+] Successfully patched _amfi_check_dyld_policy_self"); 111 | } 112 | -------------------------------------------------------------------------------- /launchr/dyld_patch.h: -------------------------------------------------------------------------------- 1 | // 2 | // dyld_patch.h 3 | // launchr 4 | // 5 | // Created by jim on 08/08/2021. 6 | // 7 | 8 | #ifndef dyld_patch_h 9 | #define dyld_patch_h 10 | 11 | #include 12 | 13 | int patch_dyld_in_process(pid_t pid); 14 | 15 | #endif /* dyld_patch_h */ 16 | -------------------------------------------------------------------------------- /launchr/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.debugger 6 | 7 | 8 | -------------------------------------------------------------------------------- /launchr/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // launchr 4 | // 5 | // Created by jim on 03/07/2021. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | #import "PrivateApiSupport.h" 12 | 13 | #include 14 | #include "dyld_patch.h" 15 | 16 | void load_private_framework(NSString const* framework) { 17 | NSBundle *b = [NSBundle bundleWithPath: [NSString stringWithFormat:@"/System/Library/PrivateFrameworks/%@.framework",framework]]; 18 | 19 | if (![b load]) { 20 | NSLog(@"Error loading %@.framework!", framework); 21 | exit(1); 22 | } 23 | } 24 | 25 | void print_usage(void) { 26 | fprintf(stderr, "Usage: launchr [-platform macos|ios] [-allowinterpose yes] [-mode suspended|running] [-envfile ] -exec \n"); 27 | } 28 | 29 | int main(int argc, const char * argv[]) { 30 | @autoreleasepool { 31 | 32 | load_private_framework(@"RunningBoardServices"); 33 | 34 | NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults]; 35 | NSString const* runMode = [standardDefaults stringForKey:@"mode"]; 36 | NSString const* platform = [standardDefaults stringForKey:@"platform"]; 37 | NSString const* allowinterpose = [standardDefaults stringForKey:@"allowinterpose"]; 38 | NSString* executablePath = [standardDefaults stringForKey:@"exec"]; 39 | 40 | int platformIdentifier = PLATFORM_IOS; // 2 for ios, 1 for macos 41 | int lsSpawnFlags = 1; // 0 for normal launch, 1 to launch suspended 42 | 43 | if (!([executablePath length] > 0)) { 44 | print_usage(); 45 | exit(1); 46 | } 47 | 48 | if ([platform isEqualToString:@"macos"]) { 49 | platformIdentifier = PLATFORM_MACOS; 50 | } 51 | 52 | Class cRbsLaunchContext = NSClassFromString(@"RBSLaunchContext"); 53 | Class cRbsLaunchRequest = NSClassFromString(@"RBSLaunchRequest"); 54 | Class cRbsProcessIdentity = NSClassFromString(@"RBSProcessIdentity"); 55 | Class cRbsProcessHandle = NSClassFromString(@"RBSProcessHandle"); 56 | Class cRbsProcessPredicate = NSClassFromString(@"RBSProcessPredicate"); 57 | 58 | // TODO: find out how to get rid of the runningboard watchdog, our child is being killed in ~30s when launched suspended now. This is Jetsam, find out how to configure 59 | // TODO: create proper container for data 60 | 61 | NSMutableDictionary *infoPlistDic = [[NSMutableDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", [executablePath stringByDeletingLastPathComponent]]]; 62 | 63 | NSString* jobLabel; 64 | NSString const* bundleId = [infoPlistDic objectForKey:@"CFBundleIdentifier"]; 65 | 66 | NSUUID *uuid = [NSUUID UUID]; 67 | NSString *envFile = [standardDefaults stringForKey:@"envfile"]; 68 | NSDictionary *env = nil; 69 | 70 | if ([envFile length] > 0) { 71 | 72 | env = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@", envFile]]; 73 | 74 | if ([env count] < 1) { 75 | fprintf(stderr, "Environment file specified, but no variables found! Ensure the file has a correct format (plist) and contains at least one variable/value combo!\n"); 76 | print_usage(); 77 | exit(1); 78 | } 79 | 80 | NSLog(@"Using additional environment variables:\n%@", env); 81 | } 82 | 83 | jobLabel = [NSString stringWithFormat:@"%@-%@",bundleId,uuid]; // Add the UUID to have unique job names if spawning multiple instances of the same app 84 | 85 | NSLog(@"Submitting job: %@", jobLabel); 86 | 87 | RBSProcessIdentity* identity = [cRbsProcessIdentity identityForApplicationJobLabel:jobLabel bundleID:bundleId platform:platformIdentifier]; 88 | RBSLaunchContext* context = [cRbsLaunchContext contextWithIdentity:identity]; 89 | 90 | NSString *outPath = NSTemporaryDirectory(); 91 | NSString *stdoutPath = [outPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_stdout.txt",jobLabel]]; 92 | NSString *stderrPath = [outPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_stderr.txt",jobLabel]]; 93 | 94 | // Look at -[RBLaunchdJobManager _generateDataWithIdentity:context:] to learn about the meaning of the context properties 95 | [context setExecutablePath:executablePath]; 96 | [context setLsSpawnFlags:lsSpawnFlags]; 97 | [context setStandardOutputPath:stdoutPath]; 98 | [context setStandardErrorPath:stderrPath]; 99 | [context setEnvironment:env]; 100 | [context setExecutionOptions:0x8]; // Looking inside runningboard code it looks like this disabled pointer auth? Might come in handy 101 | [context setLsInitialRole:0x7]; // This value is mapped to PRIO_DARWIN_ROLE_UI_FOCAL by RBSDarwinRoleFromRBSRole() 102 | 103 | RBSLaunchRequest* request = [[cRbsLaunchRequest alloc] initWithContext:context]; 104 | 105 | NSError* errResult; 106 | BOOL success = [request execute:&context error:&errResult]; 107 | 108 | if (!success) { 109 | NSLog(@"Error: %@", errResult); 110 | exit(1); 111 | } 112 | 113 | RBSProcessPredicate* predicate = [cRbsProcessPredicate predicateMatchingIdentity:identity]; 114 | RBSProcessHandle* process = [cRbsProcessHandle handleForPredicate:predicate error:nil]; 115 | 116 | int pid = [process rbs_pid]; 117 | 118 | if ([allowinterpose isEqualToString:@"yes"]) { 119 | NSLog(@"Patching dyld to allow for interposing..."); 120 | if (patch_dyld_in_process(pid)) { 121 | NSLog(@"Error while patching, am I signed with debugger entitlements?"); 122 | return -1; 123 | } 124 | } 125 | 126 | if (![runMode isEqualToString:@"suspended"]) { 127 | kill(pid, SIGCONT); 128 | } 129 | 130 | NSLog(@"Redirecting child's output to: %@", outPath); 131 | NSLog(@"Child PID: %d", pid); 132 | } 133 | return 0; 134 | } 135 | --------------------------------------------------------------------------------