├── LICENSE ├── README.md ├── task-unchain.xcodeproj └── project.pbxproj └── task-unchain └── main.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Landon Fuller 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # task-unchain 2 | 3 | Binary patch for taskgated that disables entitlement restrictions (including AppStore-only restrictions). 4 | 5 | ## Introduction 6 | 7 | On Mac OS X, application signing [entitlements](https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html) 8 | enforce client-side constraints on non-AppStore (or non-Apple-distributed) applications. This is used, for instance, to prevent a non-AppStore application from using the 9 | MapKit APIs. 10 | 11 | Recently, I wanted to backport Xcode 6.3 from Yosemite, getting it running on Mavericks. Unfortunately, simply stripping 12 | signatures wasn't an option -- Xcode itself transitively depends on code signing via its use of XPC. 13 | 14 | It's *also* not possible to simply resign a modified Xcode binary with a local adhoc 15 | certificate (or a standard paid Mac Developer certificate); Xcode relies on Apple-privileged 16 | entitlements -- including the MapKit entitlement -- that aren't available without a trusted 17 | entitlement-granting provisioning profile. 18 | 19 | These AppStore-only functionality constraints are enforced by the `/usr/libexec/taskgated` daemon; to 20 | work around it, task-unchain patches the taskgated code, disabling all checks 21 | for restricted entitlements. 22 | 23 | ## Supported Systems and Warnings 24 | 25 | **THIS MODIFIES A CRITICAL SECURITY DAEMON. THERE IS NO WARRANTY. MAKE BACKUPS. USE AT YOUR OWN RISK.** 26 | 27 | This is a hack, I've only reverse engineered just enough of the enforcement mechanisms to implement 28 | what I need, and like any hack, it may have unexpected consequences. 29 | 30 | The patch has been tested on Mac OS X 10.9.5 (13F1066); since the patch performs a search for the machine 31 | code containing the policy check, it may work on other releases. It also could just as easily leave you 32 | with a non-booting system and a massive hangover. 33 | 34 | **MAKE BACKUPS**. 35 | 36 | ## Applying the Patch 37 | 38 | Once the patch is applied, you'll need to re-sign taskgated and possibly also taskgated-helper: 39 | 40 | sudo codesign -f -s - --preserve-metadata /usr/libexec/taskgated 41 | sudo codesign -f -s - --preserve-metadata /usr/libexec/taskgated-helper 42 | 43 | Once you restart taskgated, entitlement policy will no longer be enforced. 44 | -------------------------------------------------------------------------------- /task-unchain.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 05B025EE1AB4A57700F6BF2B /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 05B025ED1AB4A57700F6BF2B /* main.cpp */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 05B025E81AB4A57700F6BF2B /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 05B025EA1AB4A57700F6BF2B /* task-unchain */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "task-unchain"; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 05B025ED1AB4A57700F6BF2B /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | 05B025E71AB4A57700F6BF2B /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | 05B025E11AB4A57700F6BF2B = { 42 | isa = PBXGroup; 43 | children = ( 44 | 05B025EC1AB4A57700F6BF2B /* task-unchain */, 45 | 05B025EB1AB4A57700F6BF2B /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 05B025EB1AB4A57700F6BF2B /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 05B025EA1AB4A57700F6BF2B /* task-unchain */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | 05B025EC1AB4A57700F6BF2B /* task-unchain */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 05B025ED1AB4A57700F6BF2B /* main.cpp */, 61 | ); 62 | path = "task-unchain"; 63 | sourceTree = ""; 64 | }; 65 | /* End PBXGroup section */ 66 | 67 | /* Begin PBXNativeTarget section */ 68 | 05B025E91AB4A57700F6BF2B /* task-unchain */ = { 69 | isa = PBXNativeTarget; 70 | buildConfigurationList = 05B025F11AB4A57700F6BF2B /* Build configuration list for PBXNativeTarget "task-unchain" */; 71 | buildPhases = ( 72 | 05B025E61AB4A57700F6BF2B /* Sources */, 73 | 05B025E71AB4A57700F6BF2B /* Frameworks */, 74 | 05B025E81AB4A57700F6BF2B /* CopyFiles */, 75 | ); 76 | buildRules = ( 77 | ); 78 | dependencies = ( 79 | ); 80 | name = "task-unchain"; 81 | productName = "task-unchain"; 82 | productReference = 05B025EA1AB4A57700F6BF2B /* task-unchain */; 83 | productType = "com.apple.product-type.tool"; 84 | }; 85 | /* End PBXNativeTarget section */ 86 | 87 | /* Begin PBXProject section */ 88 | 05B025E21AB4A57700F6BF2B /* Project object */ = { 89 | isa = PBXProject; 90 | attributes = { 91 | LastUpgradeCheck = 0620; 92 | ORGANIZATIONNAME = "Landon Fuller"; 93 | TargetAttributes = { 94 | 05B025E91AB4A57700F6BF2B = { 95 | CreatedOnToolsVersion = 6.2; 96 | }; 97 | }; 98 | }; 99 | buildConfigurationList = 05B025E51AB4A57700F6BF2B /* Build configuration list for PBXProject "task-unchain" */; 100 | compatibilityVersion = "Xcode 3.2"; 101 | developmentRegion = English; 102 | hasScannedForEncodings = 0; 103 | knownRegions = ( 104 | en, 105 | ); 106 | mainGroup = 05B025E11AB4A57700F6BF2B; 107 | productRefGroup = 05B025EB1AB4A57700F6BF2B /* Products */; 108 | projectDirPath = ""; 109 | projectRoot = ""; 110 | targets = ( 111 | 05B025E91AB4A57700F6BF2B /* task-unchain */, 112 | ); 113 | }; 114 | /* End PBXProject section */ 115 | 116 | /* Begin PBXSourcesBuildPhase section */ 117 | 05B025E61AB4A57700F6BF2B /* Sources */ = { 118 | isa = PBXSourcesBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | 05B025EE1AB4A57700F6BF2B /* main.cpp in Sources */, 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXSourcesBuildPhase section */ 126 | 127 | /* Begin XCBuildConfiguration section */ 128 | 05B025EF1AB4A57700F6BF2B /* Debug */ = { 129 | isa = XCBuildConfiguration; 130 | buildSettings = { 131 | ALWAYS_SEARCH_USER_PATHS = NO; 132 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 133 | CLANG_CXX_LIBRARY = "libc++"; 134 | CLANG_ENABLE_MODULES = YES; 135 | CLANG_ENABLE_OBJC_ARC = YES; 136 | CLANG_WARN_BOOL_CONVERSION = YES; 137 | CLANG_WARN_CONSTANT_CONVERSION = YES; 138 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 139 | CLANG_WARN_EMPTY_BODY = YES; 140 | CLANG_WARN_ENUM_CONVERSION = YES; 141 | CLANG_WARN_INT_CONVERSION = YES; 142 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 143 | CLANG_WARN_UNREACHABLE_CODE = YES; 144 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 145 | COPY_PHASE_STRIP = NO; 146 | ENABLE_STRICT_OBJC_MSGSEND = YES; 147 | GCC_C_LANGUAGE_STANDARD = gnu99; 148 | GCC_DYNAMIC_NO_PIC = NO; 149 | GCC_OPTIMIZATION_LEVEL = 0; 150 | GCC_PREPROCESSOR_DEFINITIONS = ( 151 | "DEBUG=1", 152 | "$(inherited)", 153 | ); 154 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 155 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 156 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 157 | GCC_WARN_UNDECLARED_SELECTOR = YES; 158 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 159 | GCC_WARN_UNUSED_FUNCTION = YES; 160 | GCC_WARN_UNUSED_VARIABLE = YES; 161 | MACOSX_DEPLOYMENT_TARGET = 10.9; 162 | MTL_ENABLE_DEBUG_INFO = YES; 163 | ONLY_ACTIVE_ARCH = YES; 164 | SDKROOT = macosx; 165 | }; 166 | name = Debug; 167 | }; 168 | 05B025F01AB4A57700F6BF2B /* Release */ = { 169 | isa = XCBuildConfiguration; 170 | buildSettings = { 171 | ALWAYS_SEARCH_USER_PATHS = NO; 172 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 173 | CLANG_CXX_LIBRARY = "libc++"; 174 | CLANG_ENABLE_MODULES = YES; 175 | CLANG_ENABLE_OBJC_ARC = YES; 176 | CLANG_WARN_BOOL_CONVERSION = YES; 177 | CLANG_WARN_CONSTANT_CONVERSION = YES; 178 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 179 | CLANG_WARN_EMPTY_BODY = YES; 180 | CLANG_WARN_ENUM_CONVERSION = YES; 181 | CLANG_WARN_INT_CONVERSION = YES; 182 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 183 | CLANG_WARN_UNREACHABLE_CODE = YES; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | COPY_PHASE_STRIP = NO; 186 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 187 | ENABLE_NS_ASSERTIONS = NO; 188 | ENABLE_STRICT_OBJC_MSGSEND = YES; 189 | GCC_C_LANGUAGE_STANDARD = gnu99; 190 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 191 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 192 | GCC_WARN_UNDECLARED_SELECTOR = YES; 193 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 194 | GCC_WARN_UNUSED_FUNCTION = YES; 195 | GCC_WARN_UNUSED_VARIABLE = YES; 196 | MACOSX_DEPLOYMENT_TARGET = 10.9; 197 | MTL_ENABLE_DEBUG_INFO = NO; 198 | SDKROOT = macosx; 199 | }; 200 | name = Release; 201 | }; 202 | 05B025F21AB4A57700F6BF2B /* Debug */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | PRODUCT_NAME = "$(TARGET_NAME)"; 206 | }; 207 | name = Debug; 208 | }; 209 | 05B025F31AB4A57700F6BF2B /* Release */ = { 210 | isa = XCBuildConfiguration; 211 | buildSettings = { 212 | PRODUCT_NAME = "$(TARGET_NAME)"; 213 | }; 214 | name = Release; 215 | }; 216 | /* End XCBuildConfiguration section */ 217 | 218 | /* Begin XCConfigurationList section */ 219 | 05B025E51AB4A57700F6BF2B /* Build configuration list for PBXProject "task-unchain" */ = { 220 | isa = XCConfigurationList; 221 | buildConfigurations = ( 222 | 05B025EF1AB4A57700F6BF2B /* Debug */, 223 | 05B025F01AB4A57700F6BF2B /* Release */, 224 | ); 225 | defaultConfigurationIsVisible = 0; 226 | defaultConfigurationName = Release; 227 | }; 228 | 05B025F11AB4A57700F6BF2B /* Build configuration list for PBXNativeTarget "task-unchain" */ = { 229 | isa = XCConfigurationList; 230 | buildConfigurations = ( 231 | 05B025F21AB4A57700F6BF2B /* Debug */, 232 | 05B025F31AB4A57700F6BF2B /* Release */, 233 | ); 234 | defaultConfigurationIsVisible = 0; 235 | defaultConfigurationName = Release; 236 | }; 237 | /* End XCConfigurationList section */ 238 | }; 239 | rootObject = 05B025E21AB4A57700F6BF2B /* Project object */; 240 | } 241 | -------------------------------------------------------------------------------- /task-unchain/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Landon Fuller 3 | * All rights reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person 6 | * obtaining a copy of this software and associated documentation 7 | * files (the "Software"), to deal in the Software without 8 | * restriction, including without limitation the rights to use, 9 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following 12 | * conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #define PATCH_POS 0 40 | static const uint8_t target_pattern[] = { 41 | 0x44, 0x88, 0xF2, 0x80, 0xE2, 0x01, 0x88, 0x95, 0xE0, 0xFE, 0xFF, 0xFF, 0x8A, 0x8D, 0xF3, 0xFE, 42 | 0xFF, 0xFF, 0x80, 0xE1, 0x01, 0x88, 0x8D, 0xF3, 0xFE, 0xFF, 0xFF, 0x8A, 0x9D, 0xF4, 0xFE, 0xFF, 43 | 0xFF, 0x88, 0xD8, 0x24, 0x01, 0x0F, 0xB6, 0xC9, 0x89, 0x0C, 0x24, 0x0F, 0xB6, 0xCA, 0x44, 0x0F, 44 | 0xB6, 0xC0, 0x44, 0x88, 0xE8, 0x24, 0x01, 0x44, 0x0F, 0xB6, 0xC8, 0x48, 0x8D, 0x3D, 0xD0, 0x6F, 45 | 0x00, 0x00, 0x48, 0x8D, 0x35, 0x7A, 0x70, 0x00, 0x00, 0x44, 0x8B, 0xBD, 0xD4, 0xFE, 0xFF, 0xFF, 46 | 0x44, 0x89, 0xFA, 0x30, 0xC0, 0xE8, 0xAA, 0x4F, 0x00, 0x00, 0x45, 0x08, 0xF5, 0x41, 0xF6, 0xC5, 47 | 0x01, 0x74, 0x11, 0x44, 0x89, 0xFF, 0xBE, 0x03, 0x00, 0x00, 0x00, 0x31, 0xD2, 0x31, 0xC9, 0xE8, 48 | 0xCD, 0x52, 0x00, 0x00, 0x44, 0x08, 0xF3, 0xF6, 0xC3, 0x01, 0x0F, 0x84, 0xBD, 0x01, 49 | /* patch s/jz/jmp/ here ^^^^^^^^^^^^^^^^^^^^^^ */ 50 | }; 51 | static const uint8_t target_patch[] = { 0xE9, 0xBE, 0x01, 0x00 }; 52 | static const size_t target_patch_pos = sizeof(target_pattern) - sizeof(target_patch); 53 | 54 | /** 55 | * BMH search pattern. 56 | * 57 | * A Pratt-Boyer-Moore string search, written by Jerry Coffin 58 | * sometime or other in 1991. Removed from original program, and 59 | * (incorrectly) rewritten for separate, generic use in early 1992. 60 | * Corrected with help from Thad Smith, late March and early 61 | * April 1992...hopefully it's correct this time. Revised by Bob Stout. 62 | * 63 | * This is hereby placed in the Public Domain by its author. 64 | * 65 | * 10/21/93 rdg Fixed bug found by Jeff Dunlop 66 | * 12/16/2013 landonf Removed globals, updated to use C99 types. 67 | * 03/14/2015 landonf Convert to a C++ class for use in task-unchain. 68 | */ 69 | class bmh_pattern { 70 | public: 71 | /** 72 | * Initialize a pattern. 73 | * 74 | * @param string The binary string to search for. 75 | * @param len The length of @a string. 76 | */ 77 | bmh_pattern (const uint8_t *string, size_t len) : _findme(string), _len(len) { 78 | size_t i; 79 | 80 | for (i = 0; i <= UINT8_MAX; i++) /* rdg 10/93 */ 81 | _table[i] = len; 82 | for (i = 0; i < len; i++) 83 | _table[(unsigned char)string[i]] = len - i - 1; 84 | } 85 | 86 | /** 87 | * Search for this pattern in @a string. 88 | * 89 | * @param string The buffer to be searched. 90 | * @param limit The size of @a string in bytes. 91 | */ 92 | const uint8_t *search (const uint8_t *string, size_t limit) { 93 | size_t shift = 0; 94 | size_t pos = _len - 1; 95 | const uint8_t *here; 96 | 97 | while (pos < limit) { 98 | while (pos < limit && (shift = _table[(unsigned char)string[pos]]) > 0) { 99 | pos += shift; 100 | } 101 | 102 | if (0 == shift) { 103 | here = &string[pos - _len+1]; 104 | if (0 == memcmp(_findme, here, _len)) { 105 | return (here); 106 | } 107 | else pos++; 108 | } 109 | } 110 | return NULL; 111 | } 112 | 113 | private: 114 | size_t _table[UINT8_MAX + 1]; 115 | size_t _len; 116 | const uint8_t *_findme; 117 | }; 118 | 119 | int main(int argc, const char *argv[]) { 120 | int fd; 121 | struct stat statbuf; 122 | 123 | /* Parse the arguments */ 124 | if (argc != 2) 125 | errx(EXIT_FAILURE, "Usage: %s ", argv[0]); 126 | const char * const fname = argv[1]; 127 | 128 | /* open the target file */ 129 | fd = open(fname, O_RDWR); 130 | if (fd < 0) 131 | err(EXIT_FAILURE, "open(%s)", fname); 132 | 133 | /* compute the file size */ 134 | if (fstat(fd, &statbuf) != 0) 135 | err(EXIT_FAILURE, "fstat(%s)", fname); 136 | 137 | if (statbuf.st_size > SIZE_MAX) 138 | errx(EXIT_FAILURE, "File is too large to be mapped on this system."); 139 | size_t len = statbuf.st_size; 140 | 141 | /* mmap the file */ 142 | uint8_t * const start = (uint8_t * const) mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 143 | if (start == MAP_FAILED) 144 | err(EXIT_FAILURE, "mmap(%s)", fname); 145 | 146 | const uint8_t * const end = start + len; 147 | 148 | /* search for our patch pattern */ 149 | bmh_pattern bmh(target_pattern, sizeof(target_pattern)); 150 | 151 | uint8_t *pos = start; 152 | while (pos + sizeof(target_pattern) < end) { 153 | uint8_t *result = (uint8_t *) bmh.search(pos, end - pos); 154 | if (result == NULL) 155 | break; 156 | 157 | printf("Found at file offset %lx [%hhu], patching ...\n", (result - start), result[target_patch_pos]); 158 | pos = result + sizeof(target_pattern); 159 | 160 | /* Apply out patch */ 161 | memcpy(result + target_patch_pos, target_patch, sizeof(target_patch)); 162 | 163 | /* We shouldn't need to patch more than one instance */ 164 | break; 165 | } 166 | 167 | if (munmap(start, len) != 0) 168 | warn("munmap()"); 169 | 170 | if (fsync(fd) != 0) 171 | warn("fsync()"); 172 | 173 | if (close(fd) != 0) 174 | err(EXIT_FAILURE, "close(%s)", fname); 175 | 176 | 177 | return EXIT_SUCCESS; 178 | } --------------------------------------------------------------------------------