├── .gitignore ├── LICENSE.md ├── LibraryInjector.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── LibraryInjector.xcscheme ├── LibraryInjector ├── LibraryInjector.entitlements └── main.cpp ├── README.md ├── TestHost └── main.cpp └── TestLib └── testlib.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | !.keep 3 | 4 | ## Build generated 5 | DerivedData/ 6 | 7 | ## Various settings 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata/ 17 | 18 | ## Other 19 | *.moved-aside 20 | *.xccheckout 21 | *.xcscmblueprint 22 | 23 | ## Obj-C/Swift specific 24 | *.hmap 25 | *.ipa 26 | *.dSYM.zip 27 | *.dSYM 28 | 29 | ## Playgrounds 30 | timeline.xctimeline 31 | playground.xcworkspace 32 | 33 | ## Swift Package Manager 34 | .build/ 35 | */.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 36 | 37 | ## CocoaPods 38 | Pods/ 39 | 40 | ## Xcode 41 | UserInterfaceState.xcuserstate 42 | 43 | ## Theos 44 | .theos 45 | /packages 46 | compile_commands.json 47 | 48 | ## VScode 49 | .vscode 50 | 51 | ## Misc 52 | /Private/ 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU Lesser General Public License 2 | ================================= 3 | 4 | _Version 3, 29 June 2007_ 5 | _Copyright © 2007 Free Software Foundation, Inc. <>_ 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | 11 | This version of the GNU Lesser General Public License incorporates 12 | the terms and conditions of version 3 of the GNU General Public 13 | License, supplemented by the additional permissions listed below. 14 | 15 | ### 0. Additional Definitions 16 | 17 | As used herein, “this License” refers to version 3 of the GNU Lesser 18 | General Public License, and the “GNU GPL” refers to version 3 of the GNU 19 | General Public License. 20 | 21 | “The Library” refers to a covered work governed by this License, 22 | other than an Application or a Combined Work as defined below. 23 | 24 | An “Application” is any work that makes use of an interface provided 25 | by the Library, but which is not otherwise based on the Library. 26 | Defining a subclass of a class defined by the Library is deemed a mode 27 | of using an interface provided by the Library. 28 | 29 | A “Combined Work” is a work produced by combining or linking an 30 | Application with the Library. The particular version of the Library 31 | with which the Combined Work was made is also called the “Linked 32 | Version”. 33 | 34 | The “Minimal Corresponding Source” for a Combined Work means the 35 | Corresponding Source for the Combined Work, excluding any source code 36 | for portions of the Combined Work that, considered in isolation, are 37 | based on the Application, and not on the Linked Version. 38 | 39 | The “Corresponding Application Code” for a Combined Work means the 40 | object code and/or source code for the Application, including any data 41 | and utility programs needed for reproducing the Combined Work from the 42 | Application, but excluding the System Libraries of the Combined Work. 43 | 44 | ### 1. Exception to Section 3 of the GNU GPL 45 | 46 | You may convey a covered work under sections 3 and 4 of this License 47 | without being bound by section 3 of the GNU GPL. 48 | 49 | ### 2. Conveying Modified Versions 50 | 51 | If you modify a copy of the Library, and, in your modifications, a 52 | facility refers to a function or data to be supplied by an Application 53 | that uses the facility (other than as an argument passed when the 54 | facility is invoked), then you may convey a copy of the modified 55 | version: 56 | 57 | * **a)** under this License, provided that you make a good faith effort to 58 | ensure that, in the event an Application does not supply the 59 | function or data, the facility still operates, and performs 60 | whatever part of its purpose remains meaningful, or 61 | 62 | * **b)** under the GNU GPL, with none of the additional permissions of 63 | this License applicable to that copy. 64 | 65 | ### 3. Object Code Incorporating Material from Library Header Files 66 | 67 | The object code form of an Application may incorporate material from 68 | a header file that is part of the Library. You may convey such object 69 | code under terms of your choice, provided that, if the incorporated 70 | material is not limited to numerical parameters, data structure 71 | layouts and accessors, or small macros, inline functions and templates 72 | (ten or fewer lines in length), you do both of the following: 73 | 74 | * **a)** Give prominent notice with each copy of the object code that the 75 | Library is used in it and that the Library and its use are 76 | covered by this License. 77 | * **b)** Accompany the object code with a copy of the GNU GPL and this license 78 | document. 79 | 80 | ### 4. Combined Works 81 | 82 | You may convey a Combined Work under terms of your choice that, 83 | taken together, effectively do not restrict modification of the 84 | portions of the Library contained in the Combined Work and reverse 85 | engineering for debugging such modifications, if you also do each of 86 | the following: 87 | 88 | * **a)** Give prominent notice with each copy of the Combined Work that 89 | the Library is used in it and that the Library and its use are 90 | covered by this License. 91 | 92 | * **b)** Accompany the Combined Work with a copy of the GNU GPL and this license 93 | document. 94 | 95 | * **c)** For a Combined Work that displays copyright notices during 96 | execution, include the copyright notice for the Library among 97 | these notices, as well as a reference directing the user to the 98 | copies of the GNU GPL and this license document. 99 | 100 | * **d)** Do one of the following: 101 | - **0)** Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | - **1)** Use a suitable shared library mechanism for linking with the 109 | Library. A suitable mechanism is one that **(a)** uses at run time 110 | a copy of the Library already present on the user's computer 111 | system, and **(b)** will operate properly with a modified version 112 | of the Library that is interface-compatible with the Linked 113 | Version. 114 | 115 | * **e)** Provide Installation Information, but only if you would otherwise 116 | be required to provide such information under section 6 of the 117 | GNU GPL, and only to the extent that such information is 118 | necessary to install and execute a modified version of the 119 | Combined Work produced by recombining or relinking the 120 | Application with a modified version of the Linked Version. (If 121 | you use option **4d0**, the Installation Information must accompany 122 | the Minimal Corresponding Source and Corresponding Application 123 | Code. If you use option **4d1**, you must provide the Installation 124 | Information in the manner specified by section 6 of the GNU GPL 125 | for conveying Corresponding Source.) 126 | 127 | ### 5. Combined Libraries 128 | 129 | You may place library facilities that are a work based on the 130 | Library side by side in a single library together with other library 131 | facilities that are not Applications and are not covered by this 132 | License, and convey such a combined library under terms of your 133 | choice, if you do both of the following: 134 | 135 | * **a)** Accompany the combined library with a copy of the same work based 136 | on the Library, uncombined with any other library facilities, 137 | conveyed under the terms of this License. 138 | * **b)** Give prominent notice with the combined library that part of it 139 | is a work based on the Library, and explaining where to find the 140 | accompanying uncombined form of the same work. 141 | 142 | ### 6. Revised Versions of the GNU Lesser General Public License 143 | 144 | The Free Software Foundation may publish revised and/or new versions 145 | of the GNU Lesser General Public License from time to time. Such new 146 | versions will be similar in spirit to the present version, but may 147 | differ in detail to address new problems or concerns. 148 | 149 | Each version is given a distinguishing version number. If the 150 | Library as you received it specifies that a certain numbered version 151 | of the GNU Lesser General Public License “or any later version” 152 | applies to it, you have the option of following the terms and 153 | conditions either of that published version or of any later version 154 | published by the Free Software Foundation. If the Library as you 155 | received it does not specify a version number of the GNU Lesser 156 | General Public License, you may choose any version of the GNU Lesser 157 | General Public License ever published by the Free Software Foundation. 158 | 159 | If the Library as you received it specifies that a proxy can decide 160 | whether future versions of the GNU Lesser General Public License shall 161 | apply, that proxy's public statement of acceptance of any version is 162 | permanent authorization for you to choose that version for the 163 | Library. 164 | -------------------------------------------------------------------------------- /LibraryInjector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 56760EE226E46D2E0023A211 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56760EE126E46D2E0023A211 /* main.cpp */; }; 11 | 56760EEA26E46D640023A211 /* libEndpointSecurity.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 56760EE926E46D640023A211 /* libEndpointSecurity.tbd */; }; 12 | 56760EEC26E46D700023A211 /* libbsm.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 56760EEB26E46D700023A211 /* libbsm.tbd */; }; 13 | 56E2987326E46E1C00FFBC1F /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56E2987226E46E1C00FFBC1F /* main.cpp */; }; 14 | 56E2988126E46E5F00FFBC1F /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 56E2988026E46E5F00FFBC1F /* testlib.c */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXCopyFilesBuildPhase section */ 18 | 56760EDC26E46D2E0023A211 /* CopyFiles */ = { 19 | isa = PBXCopyFilesBuildPhase; 20 | buildActionMask = 2147483647; 21 | dstPath = /usr/share/man/man1/; 22 | dstSubfolderSpec = 0; 23 | files = ( 24 | ); 25 | runOnlyForDeploymentPostprocessing = 1; 26 | }; 27 | 56E2986E26E46E1C00FFBC1F /* CopyFiles */ = { 28 | isa = PBXCopyFilesBuildPhase; 29 | buildActionMask = 2147483647; 30 | dstPath = /usr/share/man/man1/; 31 | dstSubfolderSpec = 0; 32 | files = ( 33 | ); 34 | runOnlyForDeploymentPostprocessing = 1; 35 | }; 36 | /* End PBXCopyFilesBuildPhase section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 56760EDE26E46D2E0023A211 /* LibraryInjector */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = LibraryInjector; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 56760EE126E46D2E0023A211 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 41 | 56760EE926E46D640023A211 /* libEndpointSecurity.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libEndpointSecurity.tbd; path = usr/lib/libEndpointSecurity.tbd; sourceTree = SDKROOT; }; 42 | 56760EEB26E46D700023A211 /* libbsm.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbsm.tbd; path = usr/lib/libbsm.tbd; sourceTree = SDKROOT; }; 43 | 56760EED26E46D8F0023A211 /* LibraryInjector.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LibraryInjector.entitlements; sourceTree = ""; }; 44 | 56E2987026E46E1C00FFBC1F /* TestHost */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TestHost; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 56E2987226E46E1C00FFBC1F /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 46 | 56E2987B26E46E3D00FFBC1F /* libTestLib.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libTestLib.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 56E2988026E46E5F00FFBC1F /* testlib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testlib.c; sourceTree = ""; }; 48 | 56E2988626E48F7000FFBC1F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 49 | 56E2988726E4906600FFBC1F /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 56760EDB26E46D2E0023A211 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 56760EEC26E46D700023A211 /* libbsm.tbd in Frameworks */, 58 | 56760EEA26E46D640023A211 /* libEndpointSecurity.tbd in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | 56E2986D26E46E1C00FFBC1F /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 56E2987926E46E3D00FFBC1F /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXFrameworksBuildPhase section */ 77 | 78 | /* Begin PBXGroup section */ 79 | 56760ED526E46D2E0023A211 = { 80 | isa = PBXGroup; 81 | children = ( 82 | 56E2988726E4906600FFBC1F /* LICENSE.md */, 83 | 56E2988626E48F7000FFBC1F /* README.md */, 84 | 56E2987F26E46E4F00FFBC1F /* TestLib */, 85 | 56760EE026E46D2E0023A211 /* LibraryInjector */, 86 | 56E2987126E46E1C00FFBC1F /* TestHost */, 87 | 56760EDF26E46D2E0023A211 /* Products */, 88 | 56760EE826E46D640023A211 /* Frameworks */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | 56760EDF26E46D2E0023A211 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 56760EDE26E46D2E0023A211 /* LibraryInjector */, 96 | 56E2987026E46E1C00FFBC1F /* TestHost */, 97 | 56E2987B26E46E3D00FFBC1F /* libTestLib.dylib */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 56760EE026E46D2E0023A211 /* LibraryInjector */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 56760EE126E46D2E0023A211 /* main.cpp */, 106 | 56760EED26E46D8F0023A211 /* LibraryInjector.entitlements */, 107 | ); 108 | path = LibraryInjector; 109 | sourceTree = ""; 110 | }; 111 | 56760EE826E46D640023A211 /* Frameworks */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 56760EEB26E46D700023A211 /* libbsm.tbd */, 115 | 56760EE926E46D640023A211 /* libEndpointSecurity.tbd */, 116 | ); 117 | name = Frameworks; 118 | sourceTree = ""; 119 | }; 120 | 56E2987126E46E1C00FFBC1F /* TestHost */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 56E2987226E46E1C00FFBC1F /* main.cpp */, 124 | ); 125 | path = TestHost; 126 | sourceTree = ""; 127 | }; 128 | 56E2987F26E46E4F00FFBC1F /* TestLib */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 56E2988026E46E5F00FFBC1F /* testlib.c */, 132 | ); 133 | path = TestLib; 134 | sourceTree = ""; 135 | }; 136 | /* End PBXGroup section */ 137 | 138 | /* Begin PBXHeadersBuildPhase section */ 139 | 56E2987726E46E3D00FFBC1F /* Headers */ = { 140 | isa = PBXHeadersBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXHeadersBuildPhase section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 56760EDD26E46D2E0023A211 /* LibraryInjector */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 56760EE526E46D2E0023A211 /* Build configuration list for PBXNativeTarget "LibraryInjector" */; 152 | buildPhases = ( 153 | 56760EDA26E46D2E0023A211 /* Sources */, 154 | 56760EDB26E46D2E0023A211 /* Frameworks */, 155 | 56760EDC26E46D2E0023A211 /* CopyFiles */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = LibraryInjector; 162 | productName = LibraryInjector; 163 | productReference = 56760EDE26E46D2E0023A211 /* LibraryInjector */; 164 | productType = "com.apple.product-type.tool"; 165 | }; 166 | 56E2986F26E46E1C00FFBC1F /* TestHost */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = 56E2987626E46E1C00FFBC1F /* Build configuration list for PBXNativeTarget "TestHost" */; 169 | buildPhases = ( 170 | 56E2986C26E46E1C00FFBC1F /* Sources */, 171 | 56E2986D26E46E1C00FFBC1F /* Frameworks */, 172 | 56E2986E26E46E1C00FFBC1F /* CopyFiles */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | ); 178 | name = TestHost; 179 | productName = TestHost; 180 | productReference = 56E2987026E46E1C00FFBC1F /* TestHost */; 181 | productType = "com.apple.product-type.tool"; 182 | }; 183 | 56E2987A26E46E3D00FFBC1F /* TestLib */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 56E2987C26E46E3D00FFBC1F /* Build configuration list for PBXNativeTarget "TestLib" */; 186 | buildPhases = ( 187 | 56E2987726E46E3D00FFBC1F /* Headers */, 188 | 56E2987826E46E3D00FFBC1F /* Sources */, 189 | 56E2987926E46E3D00FFBC1F /* Frameworks */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = TestLib; 196 | productName = TestLib; 197 | productReference = 56E2987B26E46E3D00FFBC1F /* libTestLib.dylib */; 198 | productType = "com.apple.product-type.library.dynamic"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 56760ED626E46D2E0023A211 /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | LastUpgradeCheck = 1250; 207 | TargetAttributes = { 208 | 56760EDD26E46D2E0023A211 = { 209 | CreatedOnToolsVersion = 12.5; 210 | }; 211 | 56E2986F26E46E1C00FFBC1F = { 212 | CreatedOnToolsVersion = 12.5; 213 | }; 214 | 56E2987A26E46E3D00FFBC1F = { 215 | CreatedOnToolsVersion = 12.5; 216 | }; 217 | }; 218 | }; 219 | buildConfigurationList = 56760ED926E46D2E0023A211 /* Build configuration list for PBXProject "LibraryInjector" */; 220 | compatibilityVersion = "Xcode 9.3"; 221 | developmentRegion = en; 222 | hasScannedForEncodings = 0; 223 | knownRegions = ( 224 | en, 225 | Base, 226 | ); 227 | mainGroup = 56760ED526E46D2E0023A211; 228 | productRefGroup = 56760EDF26E46D2E0023A211 /* Products */; 229 | projectDirPath = ""; 230 | projectRoot = ""; 231 | targets = ( 232 | 56760EDD26E46D2E0023A211 /* LibraryInjector */, 233 | 56E2986F26E46E1C00FFBC1F /* TestHost */, 234 | 56E2987A26E46E3D00FFBC1F /* TestLib */, 235 | ); 236 | }; 237 | /* End PBXProject section */ 238 | 239 | /* Begin PBXSourcesBuildPhase section */ 240 | 56760EDA26E46D2E0023A211 /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 56760EE226E46D2E0023A211 /* main.cpp in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | 56E2986C26E46E1C00FFBC1F /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 56E2987326E46E1C00FFBC1F /* main.cpp in Sources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | 56E2987826E46E3D00FFBC1F /* Sources */ = { 257 | isa = PBXSourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | 56E2988126E46E5F00FFBC1F /* testlib.c in Sources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXSourcesBuildPhase section */ 265 | 266 | /* Begin XCBuildConfiguration section */ 267 | 56760EE326E46D2E0023A211 /* Debug */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_ANALYZER_NONNULL = YES; 272 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_ENABLE_OBJC_WEAK = YES; 278 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_COMMA = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 284 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 285 | CLANG_WARN_EMPTY_BODY = YES; 286 | CLANG_WARN_ENUM_CONVERSION = YES; 287 | CLANG_WARN_INFINITE_RECURSION = YES; 288 | CLANG_WARN_INT_CONVERSION = YES; 289 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 290 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 291 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 294 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 295 | CLANG_WARN_STRICT_PROTOTYPES = YES; 296 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 297 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 298 | CLANG_WARN_UNREACHABLE_CODE = YES; 299 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 300 | COPY_PHASE_STRIP = NO; 301 | DEBUG_INFORMATION_FORMAT = dwarf; 302 | ENABLE_STRICT_OBJC_MSGSEND = YES; 303 | ENABLE_TESTABILITY = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu11; 305 | GCC_DYNAMIC_NO_PIC = NO; 306 | GCC_NO_COMMON_BLOCKS = YES; 307 | GCC_OPTIMIZATION_LEVEL = 0; 308 | GCC_PREPROCESSOR_DEFINITIONS = ( 309 | "DEBUG=1", 310 | "$(inherited)", 311 | ); 312 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 313 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 314 | GCC_WARN_UNDECLARED_SELECTOR = YES; 315 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 316 | GCC_WARN_UNUSED_FUNCTION = YES; 317 | GCC_WARN_UNUSED_VARIABLE = YES; 318 | MACOSX_DEPLOYMENT_TARGET = 11.3; 319 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 320 | MTL_FAST_MATH = YES; 321 | ONLY_ACTIVE_ARCH = YES; 322 | SDKROOT = macosx; 323 | }; 324 | name = Debug; 325 | }; 326 | 56760EE426E46D2E0023A211 /* Release */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ALWAYS_SEARCH_USER_PATHS = NO; 330 | CLANG_ANALYZER_NONNULL = YES; 331 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 332 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 333 | CLANG_CXX_LIBRARY = "libc++"; 334 | CLANG_ENABLE_MODULES = YES; 335 | CLANG_ENABLE_OBJC_ARC = YES; 336 | CLANG_ENABLE_OBJC_WEAK = YES; 337 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 338 | CLANG_WARN_BOOL_CONVERSION = YES; 339 | CLANG_WARN_COMMA = YES; 340 | CLANG_WARN_CONSTANT_CONVERSION = YES; 341 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 342 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 343 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 344 | CLANG_WARN_EMPTY_BODY = YES; 345 | CLANG_WARN_ENUM_CONVERSION = YES; 346 | CLANG_WARN_INFINITE_RECURSION = YES; 347 | CLANG_WARN_INT_CONVERSION = YES; 348 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 350 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 351 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 352 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 353 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 354 | CLANG_WARN_STRICT_PROTOTYPES = YES; 355 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 356 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | COPY_PHASE_STRIP = NO; 360 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 361 | ENABLE_NS_ASSERTIONS = NO; 362 | ENABLE_STRICT_OBJC_MSGSEND = YES; 363 | GCC_C_LANGUAGE_STANDARD = gnu11; 364 | GCC_NO_COMMON_BLOCKS = YES; 365 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 366 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 367 | GCC_WARN_UNDECLARED_SELECTOR = YES; 368 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 369 | GCC_WARN_UNUSED_FUNCTION = YES; 370 | GCC_WARN_UNUSED_VARIABLE = YES; 371 | MACOSX_DEPLOYMENT_TARGET = 11.3; 372 | MTL_ENABLE_DEBUG_INFO = NO; 373 | MTL_FAST_MATH = YES; 374 | SDKROOT = macosx; 375 | }; 376 | name = Release; 377 | }; 378 | 56760EE626E46D2E0023A211 /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | CLANG_CXX_LANGUAGE_STANDARD = "c++20"; 382 | CODE_SIGN_ENTITLEMENTS = LibraryInjector/LibraryInjector.entitlements; 383 | CODE_SIGN_STYLE = Automatic; 384 | DEVELOPMENT_TEAM = 25MTNC334J; 385 | ENABLE_HARDENED_RUNTIME = YES; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | }; 388 | name = Debug; 389 | }; 390 | 56760EE726E46D2E0023A211 /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | CLANG_CXX_LANGUAGE_STANDARD = "c++20"; 394 | CODE_SIGN_ENTITLEMENTS = LibraryInjector/LibraryInjector.entitlements; 395 | CODE_SIGN_STYLE = Automatic; 396 | DEVELOPMENT_TEAM = 25MTNC334J; 397 | ENABLE_HARDENED_RUNTIME = YES; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | }; 400 | name = Release; 401 | }; 402 | 56E2987426E46E1C00FFBC1F /* Debug */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | CODE_SIGN_STYLE = Automatic; 406 | DEVELOPMENT_TEAM = 25MTNC334J; 407 | ENABLE_HARDENED_RUNTIME = YES; 408 | PRODUCT_NAME = "$(TARGET_NAME)"; 409 | }; 410 | name = Debug; 411 | }; 412 | 56E2987526E46E1C00FFBC1F /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | CODE_SIGN_STYLE = Automatic; 416 | DEVELOPMENT_TEAM = 25MTNC334J; 417 | ENABLE_HARDENED_RUNTIME = YES; 418 | PRODUCT_NAME = "$(TARGET_NAME)"; 419 | }; 420 | name = Release; 421 | }; 422 | 56E2987D26E46E3D00FFBC1F /* Debug */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | CODE_SIGN_STYLE = Automatic; 426 | DEVELOPMENT_TEAM = 25MTNC334J; 427 | DYLIB_COMPATIBILITY_VERSION = 1; 428 | DYLIB_CURRENT_VERSION = 1; 429 | EXECUTABLE_PREFIX = lib; 430 | PRODUCT_NAME = "$(TARGET_NAME)"; 431 | SKIP_INSTALL = YES; 432 | }; 433 | name = Debug; 434 | }; 435 | 56E2987E26E46E3D00FFBC1F /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | CODE_SIGN_STYLE = Automatic; 439 | DEVELOPMENT_TEAM = 25MTNC334J; 440 | DYLIB_COMPATIBILITY_VERSION = 1; 441 | DYLIB_CURRENT_VERSION = 1; 442 | EXECUTABLE_PREFIX = lib; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | SKIP_INSTALL = YES; 445 | }; 446 | name = Release; 447 | }; 448 | /* End XCBuildConfiguration section */ 449 | 450 | /* Begin XCConfigurationList section */ 451 | 56760ED926E46D2E0023A211 /* Build configuration list for PBXProject "LibraryInjector" */ = { 452 | isa = XCConfigurationList; 453 | buildConfigurations = ( 454 | 56760EE326E46D2E0023A211 /* Debug */, 455 | 56760EE426E46D2E0023A211 /* Release */, 456 | ); 457 | defaultConfigurationIsVisible = 0; 458 | defaultConfigurationName = Release; 459 | }; 460 | 56760EE526E46D2E0023A211 /* Build configuration list for PBXNativeTarget "LibraryInjector" */ = { 461 | isa = XCConfigurationList; 462 | buildConfigurations = ( 463 | 56760EE626E46D2E0023A211 /* Debug */, 464 | 56760EE726E46D2E0023A211 /* Release */, 465 | ); 466 | defaultConfigurationIsVisible = 0; 467 | defaultConfigurationName = Release; 468 | }; 469 | 56E2987626E46E1C00FFBC1F /* Build configuration list for PBXNativeTarget "TestHost" */ = { 470 | isa = XCConfigurationList; 471 | buildConfigurations = ( 472 | 56E2987426E46E1C00FFBC1F /* Debug */, 473 | 56E2987526E46E1C00FFBC1F /* Release */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | 56E2987C26E46E3D00FFBC1F /* Build configuration list for PBXNativeTarget "TestLib" */ = { 479 | isa = XCConfigurationList; 480 | buildConfigurations = ( 481 | 56E2987D26E46E3D00FFBC1F /* Debug */, 482 | 56E2987E26E46E3D00FFBC1F /* Release */, 483 | ); 484 | defaultConfigurationIsVisible = 0; 485 | defaultConfigurationName = Release; 486 | }; 487 | /* End XCConfigurationList section */ 488 | }; 489 | rootObject = 56760ED626E46D2E0023A211 /* Project object */; 490 | } 491 | -------------------------------------------------------------------------------- /LibraryInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LibraryInjector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LibraryInjector.xcodeproj/xcshareddata/xcschemes/LibraryInjector.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 44 | 46 | 52 | 53 | 54 | 55 | 61 | 63 | 69 | 70 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /LibraryInjector/LibraryInjector.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.endpoint-security.client 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LibraryInjector/main.cpp: -------------------------------------------------------------------------------- 1 | // Based on this gist by Saagar Jha: 2 | // https://gist.github.com/saagarjha/a70d44951cb72f82efee3317d80ac07f 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __arm64__ 15 | #include 16 | #define x_thread_state_get_sp(state) (arm_thread_state64_get_sp(state)) 17 | #define x_thread_state_set_sp(state, sp) (arm_thread_state64_set_sp(state, sp)) 18 | #define X_THREAD_STATE ARM_THREAD_STATE64 19 | typedef arm_thread_state64_t x_thread_state_t; 20 | #elif __x86_64__ 21 | #include 22 | #define x_thread_state_get_sp(state) (state.__rsp) 23 | #define x_thread_state_set_sp(state, sp) do { state.__rsp = sp; } while (0) 24 | #define X_THREAD_STATE x86_THREAD_STATE64 25 | typedef x86_thread_state64_t x_thread_state_t; 26 | #else 27 | #error "Only arm64 and x86_64 are currently supported" 28 | #endif 29 | 30 | namespace fs = std::filesystem; 31 | 32 | static inline void _ensure(bool cond, const char *cond_s, const char *file, long line, const char *fn) { 33 | if (!(cond)) { 34 | throw std::runtime_error(std::string("") + "Check \"" + cond_s + "\" failed at " + 35 | file + ":" + std::to_string(line) + " in function " + fn); 36 | } 37 | } 38 | #define ensure(condition) (_ensure(condition, #condition, __FILE__, __LINE__, __FUNCTION__)) 39 | 40 | template 41 | static inline void _ensureEq(const T &a, const U &b, 42 | const char *a_s, const char *b_s, 43 | const char *file, long line, const char *fn) { 44 | if (a != b) { 45 | throw std::runtime_error(std::string("") + "Check \"" + a_s + " == " + b_s + "\" failed at " + 46 | file + ":" + std::to_string(line) + " in function " + fn + " (got " + 47 | std::to_string(a) + " != " + std::to_string(b) + ")"); 48 | } 49 | } 50 | #define ensureEq(a, b) (_ensureEq(a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__)) 51 | 52 | #define kcheck(a) (ensureEq(a, KERN_SUCCESS)) 53 | 54 | template 55 | static constexpr inline T align(T orig) { 56 | static_assert(alignment <= (1 << sizeof(T)), "alignment too large for given type"); 57 | static_assert(alignment && ((alignment & (alignment - 1)) == 0), 58 | "alignment must be a positive power of two"); 59 | return (orig + (T)alignment - 1) & ~((T)alignment - 1); 60 | } 61 | // static_assert(align<8>(15) == 16); 62 | // static_assert(align<8>(16) == 16); 63 | // static_assert(align<8>(17) == 24); 64 | 65 | template 66 | static constexpr inline T round_down(T orig, U boundary) { 67 | return orig / boundary * boundary; 68 | } 69 | //static_assert(round_down(8, 7) == 7); 70 | //static_assert(round_down(12, 7) == 7); 71 | //static_assert(round_down(14, 7) == 14); 72 | 73 | class TaskCursor { 74 | private: 75 | const task_t task_; 76 | std::uintptr_t address_; 77 | public: 78 | TaskCursor(task_t task, std::uintptr_t address) : task_(task), address_(address) {} 79 | 80 | const task_t &task() { return task_; } 81 | std::uintptr_t &address() { return address_; } 82 | 83 | // write next val_size bytes without moving cursor 84 | void write_ahead(const void *val, unsigned int val_size) const { 85 | vm_region_basic_info_data_64_t info; 86 | mach_msg_type_number_t info_sz = sizeof(info); 87 | vm_address_t region_addr = round_down(address_, PAGE_SIZE); 88 | vm_size_t region_size; 89 | mach_port_t object; // unused 90 | kcheck(vm_region_64(task_, ®ion_addr, ®ion_size, VM_REGION_BASIC_INFO_64, reinterpret_cast(&info), &info_sz, &object)); 91 | kcheck(vm_protect(task_, region_addr, region_size, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY)); 92 | kcheck(vm_write(task_, address_, reinterpret_cast(val), val_size)); 93 | kcheck(vm_protect(task_, region_addr, region_size, false, info.protection)); 94 | } 95 | 96 | void write(const void *val, unsigned int val_size) { 97 | write_ahead(val, val_size); 98 | address_ += val_size; 99 | } 100 | 101 | template 102 | void write_ahead(const T &val) const { 103 | write_ahead(&val, sizeof(val)); 104 | } 105 | 106 | template 107 | void write(const T &val) { 108 | write(&val, sizeof(val)); 109 | } 110 | 111 | void peek(void *out, size_t size) const { 112 | size_t count; 113 | kcheck(vm_read_overwrite(task_, address_, size, reinterpret_cast(out), &count)); 114 | ensureEq(count, size); 115 | } 116 | 117 | std::unique_ptr peek(size_t size) const { 118 | auto ptr = std::make_unique(size); 119 | peek(ptr.get(), size); 120 | return ptr; 121 | } 122 | 123 | void scan(void *out, size_t size) { 124 | peek(out, size); 125 | address_ += size; 126 | } 127 | 128 | std::unique_ptr scan(size_t size) { 129 | auto ptr = peek(size); 130 | address_ += size; 131 | return ptr; 132 | } 133 | 134 | template 135 | T peek() const { 136 | T t; 137 | peek(&t, sizeof(t)); 138 | return t; 139 | } 140 | 141 | template 142 | T scan() { 143 | T t; 144 | scan(&t, sizeof(t)); 145 | return t; 146 | } 147 | 148 | std::vector scan_string_array() { 149 | std::vector strings; 150 | std::uintptr_t string; 151 | while ((string = scan())) { 152 | strings.push_back(string); 153 | } 154 | return strings; 155 | } 156 | 157 | std::string scan_string() { 158 | std::string string; 159 | char c; 160 | while ((c = scan())) { 161 | string.push_back(c); 162 | } 163 | return string; 164 | } 165 | }; 166 | 167 | // Adds DYLD_INSERT_LIBRARIES= to stack envp, moves cursor to new stack base 168 | // Returns load address. 169 | // 170 | // This function is needed because without DYLD_INSERT_LIBRARIES or some 171 | // other dyld env var, dyld may use a cached launch closure and ignore 172 | // our new load command. We're good as long as we have a relevant dyld 173 | // env var, even if it's empty and/or ignored by dyld. 174 | static std::uintptr_t rearrange_stack(TaskCursor &cur) { 175 | /// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/kern_exec.c#L4919 176 | /// 177 | /// initial stack layout (with dummy addresses): 178 | /// ... 179 | /// 0xFFB8 180 | /// 0xFFC0 181 | /// --- _dyld_start frame vvv --- 182 | /// 0xFFC8 (cursor) load_address 183 | /// 0xFFD0 argc 184 | /// 0xFFD8 argv[] 185 | /// 0xFFE0 envp[] 186 | /// 0xFFE8 apple[] 187 | /// 0xFFF0 strings 188 | /// --- start of stack --- 189 | /// 190 | /// final stack layout (with dummy addresses, slightly simplified): 191 | /// ... 192 | /// 0xFFB8 193 | /// --- _dyld_start frame vvv --- 194 | /// 0xFFC0 (cursor) load_address 195 | /// 0xFFC8 argc 196 | /// 0xFFD0 argv[] -| 197 | /// 0xFFD8 envp[] | - rebased 198 | /// 0xFFE0 apple[] -| 199 | /// 0xFFE8 strings 200 | /// 0xFFF0 "DYLD_INSERT_LIBRARIES=" 201 | /// --- start of stack --- 202 | 203 | std::cout << "Rearranging... SP: " << (void *)cur.address() << std::endl; 204 | 205 | auto loadAddress = cur.scan(); 206 | auto argc = cur.scan(); 207 | auto argvAddresses = cur.scan_string_array(); 208 | auto envpAddresses = cur.scan_string_array(); 209 | auto appleAddresses = cur.scan_string_array(); 210 | 211 | // cursor is now at the strings 212 | 213 | auto stringReader = [&](const std::uintptr_t address) { 214 | auto oldAddr = cur.address(); 215 | cur.address() = address; 216 | auto str = cur.scan_string(); 217 | cur.address() = oldAddr; 218 | return str; 219 | }; 220 | std::vector argv; 221 | std::transform(argvAddresses.begin(), argvAddresses.end(), std::back_inserter(argv), stringReader); 222 | std::vector envp; 223 | std::transform(envpAddresses.begin(), envpAddresses.end(), std::back_inserter(envp), stringReader); 224 | std::vector apple; 225 | std::transform(appleAddresses.begin(), appleAddresses.end(), std::back_inserter(apple), stringReader); 226 | 227 | auto dyld_insert_libraries = std::find_if(envp.begin(), envp.end(), [&](const auto &string) { 228 | return string.starts_with("DYLD_INSERT_LIBRARIES="); 229 | }); 230 | // if envp already has a DYLD_INSERT_LIBRARIES, no need to do anything 231 | if (dyld_insert_libraries == envp.end()) { 232 | // we don't actually insert the library this way; we just need 233 | // to ensure this env var exists so that dyld discards its 234 | // closure. 235 | envp.push_back("DYLD_INSERT_LIBRARIES="); 236 | } 237 | 238 | argvAddresses.clear(); 239 | envpAddresses.clear(); 240 | appleAddresses.clear(); 241 | 242 | std::vector strings; 243 | 244 | auto arrayGenerator = [&](auto &addresses, const auto &string) { 245 | addresses.push_back(strings.size()); 246 | std::copy(string.begin(), string.end(), std::back_inserter(strings)); 247 | strings.push_back('\0'); 248 | }; 249 | std::for_each(argv.begin(), argv.end(), std::bind(arrayGenerator, std::ref(argvAddresses), std::placeholders::_1)); 250 | std::for_each(envp.begin(), envp.end(), std::bind(arrayGenerator, std::ref(envpAddresses), std::placeholders::_1)); 251 | std::for_each(apple.begin(), apple.end(), std::bind(arrayGenerator, std::ref(appleAddresses), std::placeholders::_1)); 252 | 253 | // it's okay if this overwrites the arguments on the stack since we've saved them 254 | // locally, and intend to write rebased versions onto the stack later 255 | cur.address() = round_down(cur.address() - strings.size(), sizeof(std::uintptr_t)); 256 | cur.write_ahead(strings.data(), (unsigned int)strings.size()); 257 | 258 | auto rebaser = [&](auto &&address) { 259 | address += cur.address(); 260 | }; 261 | std::for_each(argvAddresses.begin(), argvAddresses.end(), rebaser); 262 | std::for_each(envpAddresses.begin(), envpAddresses.end(), rebaser); 263 | std::for_each(appleAddresses.begin(), appleAddresses.end(), rebaser); 264 | 265 | std::vector addresses; 266 | addresses.reserve(argvAddresses.size() + 1 + envpAddresses.size() + 1 + appleAddresses.size() + 1); 267 | std::copy(argvAddresses.begin(), argvAddresses.end(), std::back_inserter(addresses)); 268 | addresses.push_back(0); 269 | std::copy(envpAddresses.begin(), envpAddresses.end(), std::back_inserter(addresses)); 270 | addresses.push_back(0); 271 | std::copy(appleAddresses.begin(), appleAddresses.end(), std::back_inserter(addresses)); 272 | addresses.push_back(0); 273 | 274 | const auto stackTop = cur.address() - (addresses.size() + 2) * sizeof(std::uintptr_t); 275 | cur.address() = stackTop; 276 | cur.write(loadAddress); 277 | cur.write(argc); 278 | cur.write(addresses.data(), (unsigned int)addresses.size() * sizeof(std::uintptr_t)); 279 | cur.address() = stackTop; 280 | 281 | std::cout << "Rearranged! SP: " << (void *)stackTop << std::endl; 282 | 283 | return loadAddress; 284 | } 285 | 286 | // the cursor should point to the desired image header 287 | static void insert_dylib(TaskCursor &cur, const std::string &library) { 288 | const auto base = cur.address(); 289 | 290 | std::cout << "Inserting... MH: " << (void *)base << std::endl; 291 | 292 | auto mh = cur.scan(); 293 | cur.address() += mh.sizeofcmds; 294 | 295 | // +1 to NUL-terminate library 296 | auto cmdsize = (uint32_t)align<8>(sizeof(dylib_command) + library.length() + 1); 297 | std::unique_ptr mem = cur.peek(cmdsize); 298 | 299 | bool has_space = true; 300 | // TODO: Make this check smarter; the memory might be zeroed but still used somewhere (i.e. not just padding) 301 | for (unsigned i = 0; i < cmdsize; i++) { 302 | if (mem[i] != 0) { 303 | has_space = false; 304 | break; 305 | } 306 | } 307 | 308 | if (!has_space) { 309 | // use alternate mechanism, such as overwriting an existing LC? 310 | ensure(false && "not enough space"); 311 | } 312 | 313 | dylib_command cmd = { 314 | .cmd = LC_LOAD_DYLIB, 315 | .cmdsize = cmdsize, 316 | .dylib = { 317 | .name = { 318 | .offset = sizeof(dylib_command) 319 | } 320 | } 321 | }; 322 | memcpy(mem.get(), &cmd, sizeof(cmd)); 323 | memcpy(mem.get() + sizeof(cmd), library.c_str(), library.length()); 324 | cur.write(mem.get(), cmdsize); 325 | 326 | mh.sizeofcmds += cmdsize; 327 | mh.ncmds += 1; 328 | 329 | cur.address() = base; 330 | cur.write(mh); 331 | 332 | std::cout << "Inserted!" << std::endl; 333 | } 334 | 335 | static void inject(pid_t pid, const std::string &library) { 336 | std::string libname = fs::path(library).filename().string(); 337 | std::cout << "Injecting " << libname << " into pid " << pid << std::endl; 338 | 339 | task_port_t task; 340 | kcheck(task_for_pid(mach_task_self(), pid, &task)); 341 | 342 | thread_act_array_t threads; 343 | mach_msg_type_number_t count; 344 | kcheck(task_threads(task, &threads, &count)); 345 | ensureEq(count, 1); 346 | 347 | x_thread_state_t state; 348 | count = sizeof(state); 349 | kcheck(thread_get_state(threads[0], X_THREAD_STATE, reinterpret_cast(&state), &count)); 350 | kcheck(thread_convert_thread_state(threads[0], THREAD_CONVERT_THREAD_STATE_TO_SELF, X_THREAD_STATE, reinterpret_cast(&state), count, reinterpret_cast(&state), &count)); 351 | 352 | auto cur = TaskCursor(task, x_thread_state_get_sp(state)); 353 | const auto load_addr = rearrange_stack(cur); 354 | x_thread_state_set_sp(state, cur.address()); 355 | cur.address() = load_addr; 356 | 357 | kcheck(thread_convert_thread_state(*threads, THREAD_CONVERT_THREAD_STATE_FROM_SELF, X_THREAD_STATE, reinterpret_cast(&state), count, reinterpret_cast(&state), &count)); 358 | kcheck(thread_set_state(*threads, X_THREAD_STATE, reinterpret_cast(&state), count)); 359 | 360 | insert_dylib(cur, library); 361 | 362 | std::cout << "Injected " << libname << "!" << std::endl; 363 | } 364 | 365 | int main(int argc, char **argv) { 366 | if (argc != 3) { 367 | std::cerr << "Usage: " << argv[0] << " " << std::endl; 368 | std::exit(1); 369 | } 370 | 371 | if (geteuid() != 0) { 372 | std::cerr << "You must run this program as root." << std::endl; 373 | std::exit(1); 374 | } 375 | 376 | fs::path process = argv[1]; 377 | fs::path library = fs::canonical(argv[2]); 378 | 379 | es_client_t *client = NULL; 380 | ensureEq(es_new_client(&client, ^(es_client_t *client, const es_message_t *message) { 381 | switch (message->event_type) { 382 | case ES_EVENT_TYPE_AUTH_EXEC: { 383 | const char *name = message->event.exec.target->executable->path.data; 384 | if (fs::equivalent(name, process)) { 385 | pid_t pid = audit_token_to_pid(message->process->audit_token); 386 | try { 387 | inject(pid, library); 388 | } catch (const std::exception &e) { 389 | std::cerr << "error: Failed to inject: " << e.what() << std::endl; 390 | } 391 | } 392 | es_respond_auth_result(client, message, ES_AUTH_RESULT_ALLOW, false); 393 | break; 394 | } 395 | default: 396 | ensure(false && "Unexpected event type!"); 397 | } 398 | }), ES_NEW_CLIENT_RESULT_SUCCESS); 399 | es_event_type_t events[] = { ES_EVENT_TYPE_AUTH_EXEC }; 400 | ensureEq(es_subscribe(client, events, sizeof(events) / sizeof(*events)), ES_RETURN_SUCCESS); 401 | std::cout << "Listening..." << std::endl; 402 | dispatch_main(); 403 | } 404 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Library Injector 2 | 3 | Load a library into newly spawned processes using EndpointSecurity. 4 | 5 | Based on [this gist](https://gist.github.com/saagarjha/a70d44951cb72f82efee3317d80ac07f) by Saagar Jha. 6 | -------------------------------------------------------------------------------- /TestHost/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // TestHost 4 | // 5 | // Created by Kabir Oberai on 05/09/21. 6 | // 7 | 8 | #include 9 | 10 | int main(int argc, const char * argv[]) { 11 | std::cout << "Hello, World!\n"; 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /TestLib/testlib.c: -------------------------------------------------------------------------------- 1 | // 2 | // testlib.c 3 | // TestLib 4 | // 5 | // Created by Kabir Oberai on 05/09/21. 6 | // 7 | 8 | #include 9 | 10 | __attribute__((constructor)) static void init() { 11 | printf("testlib loaded!\n"); 12 | } 13 | --------------------------------------------------------------------------------