├── .gitignore ├── README.md ├── SwiftInMemoryLoading.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── SwiftInMemoryLoading.xcscheme ├── SwiftInMemoryLoading ├── SwiftInMemoryLoading-Bridging-Header.h ├── inmemory_load.m ├── machoload.c └── main.swift └── example.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Gcc Patch 26 | /*.gcno 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftInMemoryLoading 2 | 3 | Swift implementation of in-memory Mach-O execution. Works on Big Sur and Monterey. It should be noted that this is not truly file-less on macOS Monterey (see blogpost). 4 | - ```./SwiftInMemoryLoading /full/path/to/binary arg1 arg2 arg3``` 5 | 6 | 7 | ## Blogpost 8 | - https://slyd0g.medium.com/understanding-and-defending-against-reflective-code-loading-on-macos-e2e83211e48f 9 | 10 | 11 | ## Example Usage 12 | 13 | ![Example](https://raw.githubusercontent.com/slyd0g/SwiftInMemoryLoading/main/example.png) 14 | 15 | ## Credit 16 | - https://github.com/its-a-feature/macos_execute_from_memory 17 | - https://github.com/djhohnstein/macos_shell_memory 18 | - https://gist.github.com/johnkhbaek/771a98212045f327cc1c86aaac63a4e3 19 | - https://hackd.net/posts/macos-reflective-code-loading-analysis/ 20 | - [Timo Schmid](https://twitter.com/bluec0re) and [Carl Svensson](https://twitter.com/zetatwo) for discovering the changes in NSLinkModule's return value on Monterey -------------------------------------------------------------------------------- /SwiftInMemoryLoading.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3F8492A227DC0C0F0056FC37 /* machoload.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F8492A127DC0C0F0056FC37 /* machoload.c */; }; 11 | 3FD7145A27D93D0800E6F519 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FD7145927D93D0800E6F519 /* main.swift */; }; 12 | 3FD7146227D9400F00E6F519 /* inmemory_load.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FD7146127D9400F00E6F519 /* inmemory_load.m */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | 3FD7145427D93D0800E6F519 /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 3F8492A127DC0C0F0056FC37 /* machoload.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = machoload.c; sourceTree = ""; }; 29 | 3FD7145627D93D0800E6F519 /* SwiftInMemoryLoading */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SwiftInMemoryLoading; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 3FD7145927D93D0800E6F519 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 31 | 3FD7146027D9400E00E6F519 /* SwiftInMemoryLoading-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftInMemoryLoading-Bridging-Header.h"; sourceTree = ""; }; 32 | 3FD7146127D9400F00E6F519 /* inmemory_load.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = inmemory_load.m; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 3FD7145327D93D0800E6F519 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 3FD7144D27D93D0800E6F519 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 3FD7145827D93D0800E6F519 /* SwiftInMemoryLoading */, 50 | 3FD7145727D93D0800E6F519 /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | 3FD7145727D93D0800E6F519 /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 3FD7145627D93D0800E6F519 /* SwiftInMemoryLoading */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | 3FD7145827D93D0800E6F519 /* SwiftInMemoryLoading */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 3FD7145927D93D0800E6F519 /* main.swift */, 66 | 3FD7146127D9400F00E6F519 /* inmemory_load.m */, 67 | 3FD7146027D9400E00E6F519 /* SwiftInMemoryLoading-Bridging-Header.h */, 68 | 3F8492A127DC0C0F0056FC37 /* machoload.c */, 69 | ); 70 | path = SwiftInMemoryLoading; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | 3FD7145527D93D0800E6F519 /* SwiftInMemoryLoading */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = 3FD7145D27D93D0800E6F519 /* Build configuration list for PBXNativeTarget "SwiftInMemoryLoading" */; 79 | buildPhases = ( 80 | 3FD7145227D93D0800E6F519 /* Sources */, 81 | 3FD7145327D93D0800E6F519 /* Frameworks */, 82 | 3FD7145427D93D0800E6F519 /* CopyFiles */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = SwiftInMemoryLoading; 89 | productName = SwiftInMemoryLoading; 90 | productReference = 3FD7145627D93D0800E6F519 /* SwiftInMemoryLoading */; 91 | productType = "com.apple.product-type.tool"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 3FD7144E27D93D0800E6F519 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastSwiftUpdateCheck = 1250; 100 | LastUpgradeCheck = 1250; 101 | TargetAttributes = { 102 | 3FD7145527D93D0800E6F519 = { 103 | CreatedOnToolsVersion = 12.5; 104 | LastSwiftMigration = 1250; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = 3FD7145127D93D0800E6F519 /* Build configuration list for PBXProject "SwiftInMemoryLoading" */; 109 | compatibilityVersion = "Xcode 9.3"; 110 | developmentRegion = en; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | Base, 115 | ); 116 | mainGroup = 3FD7144D27D93D0800E6F519; 117 | productRefGroup = 3FD7145727D93D0800E6F519 /* Products */; 118 | projectDirPath = ""; 119 | projectRoot = ""; 120 | targets = ( 121 | 3FD7145527D93D0800E6F519 /* SwiftInMemoryLoading */, 122 | ); 123 | }; 124 | /* End PBXProject section */ 125 | 126 | /* Begin PBXSourcesBuildPhase section */ 127 | 3FD7145227D93D0800E6F519 /* Sources */ = { 128 | isa = PBXSourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | 3F8492A227DC0C0F0056FC37 /* machoload.c in Sources */, 132 | 3FD7145A27D93D0800E6F519 /* main.swift in Sources */, 133 | 3FD7146227D9400F00E6F519 /* inmemory_load.m in Sources */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXSourcesBuildPhase section */ 138 | 139 | /* Begin XCBuildConfiguration section */ 140 | 3FD7145B27D93D0800E6F519 /* Debug */ = { 141 | isa = XCBuildConfiguration; 142 | buildSettings = { 143 | ALWAYS_SEARCH_USER_PATHS = NO; 144 | CLANG_ANALYZER_NONNULL = YES; 145 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 146 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 147 | CLANG_CXX_LIBRARY = "libc++"; 148 | CLANG_ENABLE_MODULES = YES; 149 | CLANG_ENABLE_OBJC_ARC = YES; 150 | CLANG_ENABLE_OBJC_WEAK = YES; 151 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 152 | CLANG_WARN_BOOL_CONVERSION = YES; 153 | CLANG_WARN_COMMA = YES; 154 | CLANG_WARN_CONSTANT_CONVERSION = YES; 155 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 156 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 157 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 158 | CLANG_WARN_EMPTY_BODY = YES; 159 | CLANG_WARN_ENUM_CONVERSION = YES; 160 | CLANG_WARN_INFINITE_RECURSION = YES; 161 | CLANG_WARN_INT_CONVERSION = YES; 162 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 163 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 164 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 165 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 166 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 167 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 168 | CLANG_WARN_STRICT_PROTOTYPES = YES; 169 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 170 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 171 | CLANG_WARN_UNREACHABLE_CODE = YES; 172 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 173 | COPY_PHASE_STRIP = NO; 174 | DEBUG_INFORMATION_FORMAT = dwarf; 175 | ENABLE_STRICT_OBJC_MSGSEND = YES; 176 | ENABLE_TESTABILITY = YES; 177 | GCC_C_LANGUAGE_STANDARD = gnu11; 178 | GCC_DYNAMIC_NO_PIC = NO; 179 | GCC_NO_COMMON_BLOCKS = YES; 180 | GCC_OPTIMIZATION_LEVEL = 0; 181 | GCC_PREPROCESSOR_DEFINITIONS = ( 182 | "DEBUG=1", 183 | "$(inherited)", 184 | ); 185 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 186 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 187 | GCC_WARN_UNDECLARED_SELECTOR = YES; 188 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 189 | GCC_WARN_UNUSED_FUNCTION = YES; 190 | GCC_WARN_UNUSED_VARIABLE = YES; 191 | MACOSX_DEPLOYMENT_TARGET = 11.3; 192 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 193 | MTL_FAST_MATH = YES; 194 | ONLY_ACTIVE_ARCH = YES; 195 | SDKROOT = macosx; 196 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 197 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 198 | }; 199 | name = Debug; 200 | }; 201 | 3FD7145C27D93D0800E6F519 /* Release */ = { 202 | isa = XCBuildConfiguration; 203 | buildSettings = { 204 | ALWAYS_SEARCH_USER_PATHS = NO; 205 | CLANG_ANALYZER_NONNULL = YES; 206 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 207 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 208 | CLANG_CXX_LIBRARY = "libc++"; 209 | CLANG_ENABLE_MODULES = YES; 210 | CLANG_ENABLE_OBJC_ARC = YES; 211 | CLANG_ENABLE_OBJC_WEAK = YES; 212 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 213 | CLANG_WARN_BOOL_CONVERSION = YES; 214 | CLANG_WARN_COMMA = YES; 215 | CLANG_WARN_CONSTANT_CONVERSION = YES; 216 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 217 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 218 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 219 | CLANG_WARN_EMPTY_BODY = YES; 220 | CLANG_WARN_ENUM_CONVERSION = YES; 221 | CLANG_WARN_INFINITE_RECURSION = YES; 222 | CLANG_WARN_INT_CONVERSION = YES; 223 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 224 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 225 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 226 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 227 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 228 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 229 | CLANG_WARN_STRICT_PROTOTYPES = YES; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 232 | CLANG_WARN_UNREACHABLE_CODE = YES; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | COPY_PHASE_STRIP = NO; 235 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 236 | ENABLE_NS_ASSERTIONS = NO; 237 | ENABLE_STRICT_OBJC_MSGSEND = YES; 238 | GCC_C_LANGUAGE_STANDARD = gnu11; 239 | GCC_NO_COMMON_BLOCKS = YES; 240 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 241 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 242 | GCC_WARN_UNDECLARED_SELECTOR = YES; 243 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 244 | GCC_WARN_UNUSED_FUNCTION = YES; 245 | GCC_WARN_UNUSED_VARIABLE = YES; 246 | MACOSX_DEPLOYMENT_TARGET = 11.3; 247 | MTL_ENABLE_DEBUG_INFO = NO; 248 | MTL_FAST_MATH = YES; 249 | SDKROOT = macosx; 250 | SWIFT_COMPILATION_MODE = wholemodule; 251 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 252 | }; 253 | name = Release; 254 | }; 255 | 3FD7145E27D93D0800E6F519 /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | CLANG_ENABLE_MODULES = YES; 259 | CODE_SIGN_STYLE = Automatic; 260 | LD_RUNPATH_SEARCH_PATHS = ( 261 | "$(inherited)", 262 | "@executable_path/../Frameworks", 263 | "@loader_path/../Frameworks", 264 | ); 265 | PRODUCT_NAME = "$(TARGET_NAME)"; 266 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftInMemoryLoading/SwiftInMemoryLoading-Bridging-Header.h"; 267 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 268 | SWIFT_VERSION = 5.0; 269 | }; 270 | name = Debug; 271 | }; 272 | 3FD7145F27D93D0800E6F519 /* Release */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | CLANG_ENABLE_MODULES = YES; 276 | CODE_SIGN_STYLE = Automatic; 277 | LD_RUNPATH_SEARCH_PATHS = ( 278 | "$(inherited)", 279 | "@executable_path/../Frameworks", 280 | "@loader_path/../Frameworks", 281 | ); 282 | PRODUCT_NAME = "$(TARGET_NAME)"; 283 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftInMemoryLoading/SwiftInMemoryLoading-Bridging-Header.h"; 284 | SWIFT_VERSION = 5.0; 285 | }; 286 | name = Release; 287 | }; 288 | /* End XCBuildConfiguration section */ 289 | 290 | /* Begin XCConfigurationList section */ 291 | 3FD7145127D93D0800E6F519 /* Build configuration list for PBXProject "SwiftInMemoryLoading" */ = { 292 | isa = XCConfigurationList; 293 | buildConfigurations = ( 294 | 3FD7145B27D93D0800E6F519 /* Debug */, 295 | 3FD7145C27D93D0800E6F519 /* Release */, 296 | ); 297 | defaultConfigurationIsVisible = 0; 298 | defaultConfigurationName = Release; 299 | }; 300 | 3FD7145D27D93D0800E6F519 /* Build configuration list for PBXNativeTarget "SwiftInMemoryLoading" */ = { 301 | isa = XCConfigurationList; 302 | buildConfigurations = ( 303 | 3FD7145E27D93D0800E6F519 /* Debug */, 304 | 3FD7145F27D93D0800E6F519 /* Release */, 305 | ); 306 | defaultConfigurationIsVisible = 0; 307 | defaultConfigurationName = Release; 308 | }; 309 | /* End XCConfigurationList section */ 310 | }; 311 | rootObject = 3FD7144E27D93D0800E6F519 /* Project object */; 312 | } 313 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading.xcodeproj/xcshareddata/xcschemes/SwiftInMemoryLoading.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 67 | 68 | 74 | 76 | 82 | 83 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading/SwiftInMemoryLoading-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | void executeMemory(void* memory, int machoSize, const char* functionName, int argumentCount, char** arguments); 6 | void executeMemory2(void* memory, int machoSize, const char* functionName, int argumentCount, char** arguments, int monterey); 7 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading/inmemory_load.m: -------------------------------------------------------------------------------- 1 | // 2 | // inmemory_load.m 3 | // SwiftInMemoryLoading 4 | // 5 | // Created by Justin Bui on 3/9/22. 6 | // 7 | 8 | #import 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void executeMemory(void* memory, int machoSize, const char* functionName, int argumentCount, char** arguments) { 18 | NSObjectFileImage fileImage = NULL; 19 | NSModule module = NULL; 20 | NSSymbol symbol = NULL; 21 | int pid = 0; 22 | 23 | *((uint8_t*)memory + 12) = MH_BUNDLE; 24 | 25 | NSCreateObjectFileImageFromMemory(memory, machoSize, &fileImage); 26 | if(fileImage == NULL){ 27 | //printf(" |-> Failed to create File Image from memory\n"); 28 | } 29 | else { 30 | //printf(" |-> NSCreateObjectFileImageFromMemory success!\n"); 31 | } 32 | 33 | module = NSLinkModule(fileImage, "module", NSLINKMODULE_OPTION_NONE); 34 | if(module == NULL){ 35 | //printf(" |-> Failed to get module from File Image\n"); 36 | } 37 | else { 38 | //printf(" |-> NSLinkModule success!\n"); 39 | } 40 | 41 | symbol = NSLookupSymbolInModule(module, functionName); 42 | if(symbol == NULL){ 43 | //printf(" |-> Failed to find function name in module\n"); 44 | } 45 | else { 46 | //printf(" |-> NSLookupSymbolInModule success!\n"); 47 | //printf(" |-> Executing function pointer!\n"); 48 | //printf(" |-> Mach-O Output:\n"); 49 | } 50 | 51 | int(*main)(int, char**) = (int(*)(int, char**)) NSAddressOfSymbol(symbol); 52 | 53 | main(argumentCount, arguments); 54 | 55 | //printf(" |-> Cleaning up with NSUnLinkModule and NSDestroyObjectFileImage\n"); 56 | NSUnLinkModule(module, NSUNLINKMODULE_OPTION_NONE); 57 | NSDestroyObjectFileImage(fileImage); 58 | } 59 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading/machoload.c: -------------------------------------------------------------------------------- 1 | // 2 | // machoload.c 3 | // SwiftInMemoryLoading 4 | // 5 | // Created by Justin Bui on 3/11/22. 6 | // 7 | 8 | /* 9 | ================================================================================ 10 | modified from this: https://github.com/its-a-feature/macos_execute_from_memory (supports only bundle) 11 | code injection: https://github.com/CylanceVulnResearch/osx_runbin by Stephanie Archibald (does not support m1 x64 emulation and FAT header) 12 | FAT header (universal Macho) parsing: @exploitpreacher 13 | atexit() to prevent Mach-O from exiting + searching for Mach-O header with NSLookupSymbolInModule: https://github.com/djhohnstein/macos_shell_memory 14 | ================================================================================ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include // for close 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | // Stack info for when dynamically loaded program exits 33 | static jmp_buf SAVED_ENV; 34 | // Integer switch for program control flow on setjmp 35 | static int RETVAL = 0; 36 | 37 | void my_exit() { 38 | if (RETVAL == 0) { 39 | longjmp(SAVED_ENV, 1); 40 | } else { 41 | return; 42 | } 43 | } 44 | 45 | int find_epc(unsigned long base, struct entry_point_command **entry) { 46 | // find the entry point command by searching through base's load commands 47 | struct mach_header_64 *mh; 48 | struct load_command *lc; 49 | 50 | *entry = NULL; 51 | mh = (struct mach_header_64 *)base; 52 | lc = (struct load_command *)(base + sizeof(struct mach_header_64)); 53 | for(int i=0; incmds; i++) { 54 | if(lc->cmd == LC_MAIN) { //0x80000028 55 | *entry = (struct entry_point_command *)lc; 56 | return 0; 57 | } 58 | lc = (struct load_command *)((unsigned long)lc + lc->cmdsize); 59 | } 60 | return 1; 61 | } 62 | uint32_t swap_endian(uint32_t wrong_endian) { 63 | uint32_t swapped = ((wrong_endian>>24)&0xff) | // move byte 3 to byte 0 64 | ((wrong_endian<<8)&0xff0000) | // move byte 1 to byte 2 65 | ((wrong_endian>>8)&0xff00) | // move byte 2 to byte 1 66 | ((wrong_endian<<24)&0xff000000); // byte 0 to byte 3 67 | return swapped; 68 | } 69 | 70 | void executeMemory2(void* memory, int machoSize, const char* functionName, int argumentCount, char** arguments, int monterey) 71 | { 72 | NSObjectFileImage fileImage = NULL; 73 | NSModule module = NULL; 74 | NSSymbol symbol = NULL; 75 | void *codeAddr = memory; 76 | void *machoAddr = NULL; 77 | uint32_t type; 78 | uint32_t offset = 0; 79 | uint32_t machoBufSize; 80 | 81 | // determine the type of the file we loaded 82 | if (((int *)codeAddr)[0] == 0xbebafeca) /* MAGIC for FAT */ { 83 | struct fat_arch *fa; 84 | uint32_t num_arch = swap_endian(((uint32_t *)codeAddr)[1]); 85 | 86 | for (int i=0;ioffset); 89 | machoAddr = codeAddr + offset; 90 | if (((int *)codeAddr)[0] != 0xfeedfacf /* MAGIC for MACHO 64 */) { 91 | break; 92 | } 93 | } 94 | if (!machoAddr) { 95 | goto err; 96 | } 97 | } else if (((int *)codeAddr)[0] == 0xfeedfacf) /* MAGIC for MACHO x64 */ { 98 | machoAddr = codeAddr; 99 | } else { 100 | goto err; 101 | } 102 | 103 | type = ((int *)machoAddr)[3]; 104 | machoBufSize = machoSize - offset; 105 | 106 | if (type == 0x8) { // bundle - nothing to do 107 | void (*function)(); 108 | 109 | NSCreateObjectFileImageFromMemory(machoAddr, machoBufSize, &fileImage); 110 | module = NSLinkModule(fileImage, "module", NSLINKMODULE_OPTION_NONE); 111 | 112 | symbol = NSLookupSymbolInModule(module, "_main"); 113 | function = NSAddressOfSymbol(symbol); 114 | RETVAL = setjmp(SAVED_ENV); 115 | if (RETVAL == 0) { 116 | // Create an atexit routine to longjmp back to our saved buffer. 117 | // When the thin MachO executes in-memory, it'll attempt to exit 118 | // the program. Creating this thin hook allows us to stop that process. 119 | atexit(my_exit); 120 | function(); 121 | } 122 | } else { // we have to find the main function 123 | struct entry_point_command *epc; 124 | 125 | ((int *)machoAddr)[3] = 0x8; // first change to mh_bundle type 126 | 127 | NSCreateObjectFileImageFromMemory(machoAddr, machoBufSize, &fileImage); 128 | module = NSLinkModule(fileImage, "module", NSLINKMODULE_OPTION_NONE); 129 | if (monterey == 1) { 130 | //module = ((uintptr_t)(module)) >> 1; 131 | } 132 | 133 | NSSymbol symbol = NSLookupSymbolInModule(module, "__mh_execute_header"); 134 | void* execute_base = NSAddressOfSymbol(symbol); 135 | 136 | 137 | if(find_epc(execute_base, &epc)) { 138 | goto err; 139 | } 140 | 141 | int(*main)(int, char**, char**, char**) = (int(*)(int, char**, char**, char**))(execute_base + epc->entryoff); 142 | char *env[] = {NULL}; 143 | char *apple[] = {NULL}; 144 | 145 | RETVAL = setjmp(SAVED_ENV); 146 | if (RETVAL == 0) { 147 | // Create an atexit routine to longjmp back to our saved buffer. 148 | // When the thin MachO executes in-memory, it'll attempt to exit 149 | // the program. Creating this thin hook allows us to stop that process. 150 | atexit(my_exit); 151 | main(argumentCount, arguments, env, apple); 152 | } 153 | } 154 | err: 155 | NSUnLinkModule(module, NSUNLINKMODULE_OPTION_NONE); 156 | NSDestroyObjectFileImage(fileImage); 157 | } 158 | -------------------------------------------------------------------------------- /SwiftInMemoryLoading/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // SwiftInMemoryLoading 4 | // 5 | // Created by Justin Bui on 3/9/22. 6 | // 7 | 8 | import Foundation 9 | 10 | let pipeName = "/private/tmp/" + UUID().uuidString 11 | let functionName = "_main" 12 | var cargs:Array>> = [] 13 | 14 | func startPipeServer() { 15 | // Delete named pipe if it exists 16 | let fileManager = FileManager.default 17 | if fileManager.fileExists(atPath: pipeName) { 18 | do { 19 | try FileManager.default.removeItem(atPath: pipeName) 20 | } 21 | catch { 22 | print("Exception caught: \(error)") 23 | } 24 | } 25 | 26 | // Create named pipe 27 | let namedPipe = mkfifo(pipeName, S_IFIFO|S_IRUSR|S_IWUSR) 28 | if namedPipe != 0 { 29 | print("[-] Could not created named pipe. Error:", errno) 30 | exit(-1) 31 | } 32 | else { 33 | print("[+] Named pipe created successfully at", pipeName) 34 | } 35 | 36 | let charBufferSize = 10000 37 | var charBuffer = [Int8](repeating:0, count:charBufferSize) 38 | while(true) { 39 | // Open named pipe for reading (this is blocking until a client connects for writing) 40 | print("[+] Waiting for a client to write ...") 41 | let fileDescriptor = open(pipeName, O_RDONLY) 42 | if fileDescriptor == -1 { 43 | print("[-] Could not open named pipe. Error:", errno) 44 | exit(-1) 45 | } 46 | 47 | // Read data from named pipe 48 | let readPipe = read(fileDescriptor, &charBuffer, charBufferSize) 49 | if readPipe == -1 { 50 | print("[-] Could not read data from named pipe. Error:", errno) 51 | 52 | // Delete named pipe 53 | do { 54 | try FileManager.default.removeItem(atPath: pipeName) 55 | } 56 | catch { 57 | print("Exception caught: \(error)") 58 | } 59 | } 60 | else { 61 | print("[+] Grabbed file handle to named pipe") 62 | print("[+] Read data from the pipe") 63 | print(" |-> Data:\n\n\(String(cString: charBuffer))") 64 | 65 | if (String(cString:charBuffer).contains("WRITE_COMPLETE")) 66 | { 67 | // Delete named pipe 68 | do { 69 | try FileManager.default.removeItem(atPath: pipeName) 70 | } 71 | catch { 72 | print("Exception caught: \(error)") 73 | } 74 | 75 | // Close file descriptor 76 | close(fileDescriptor) 77 | return 78 | } 79 | } 80 | } 81 | } 82 | 83 | func executeMemorySwift(macho: Data, arguments: [String]) { 84 | let machoSize = macho.count 85 | 86 | if machoSize < 1 { 87 | exit(-1) 88 | } 89 | 90 | let rawPtrMacho = UnsafeMutableRawPointer.init(mutating: (macho as NSData).bytes) 91 | executeMemory2(rawPtrMacho, Int32(machoSize), functionName, Int32(arguments.count), &cargs, CheckMonterey()) 92 | } 93 | 94 | func CheckMonterey() -> Int32 { 95 | var osVersion = OperatingSystemVersion.init() 96 | osVersion.majorVersion = 12 //Monterey 97 | if(ProcessInfo.processInfo.isOperatingSystemAtLeast(osVersion)) { 98 | return 1 99 | } 100 | else { 101 | return 0 102 | } 103 | } 104 | 105 | func Help() { 106 | print("|---------------------------------------------|") 107 | print("|-----| SwiftInMemoryLoading by @slyd0g |-----|") 108 | print("|---------------------------------------------|") 109 | print("./SwiftInMemoryLoading ") 110 | } 111 | 112 | var cliArguments = CommandLine.arguments 113 | if cliArguments.count == 1 114 | { 115 | Help() 116 | exit(0) 117 | } 118 | else if cliArguments.count >= 2 { 119 | // Load up Mach-O 120 | let macho = try Data(contentsOf: URL(fileURLWithPath: cliArguments[1])) 121 | 122 | // Load up CLI arguments if they exist 123 | if cliArguments.count > 2 { 124 | cliArguments = Array(cliArguments.dropFirst(1)) 125 | } 126 | cargs = cliArguments.map { strdup($0) } 127 | 128 | // Setup DispatchQueue to run async threads 129 | let queue = DispatchQueue(label: "", qos: .background, attributes: .concurrent) 130 | 131 | // Start pipe server in a thread 132 | queue.async{startPipeServer()} 133 | sleep(1) 134 | 135 | // Save stdout/stderr 136 | let saveStdout = dup(1) 137 | let saveStderr = dup(2) 138 | freopen(pipeName, "w", stdout) 139 | freopen(pipeName, "w", stderr) 140 | 141 | // Execute Mach-O in memory 142 | executeMemorySwift(macho: macho, arguments: cliArguments) 143 | 144 | // Flush stdout to push it to the named pipe + restore old stdout/stderr, no need to do this for stderr because it is streamed 145 | print("WRITE_COMPLETE") 146 | sleep(2) 147 | fflush(stdout) 148 | sleep(2) 149 | dup2(saveStdout, 1) 150 | dup2(saveStderr, 2) 151 | 152 | // Free the duplicated strings 153 | for ptr in cargs { free(ptr) } 154 | 155 | exit(42) 156 | } 157 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slyd0g/SwiftInMemoryLoading/dd5ee74c1f937f419b2b82effc7dd46168b62580/example.png --------------------------------------------------------------------------------