├── .gitignore ├── .gitmodules ├── COPYING ├── README.md ├── SWRoute.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SWRoute ├── Info.plist ├── SWRoute.h ├── SWRoute.swift └── rd_get_func_impl.c └── SWRouteTests ├── Info.plist └── SWRouteTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rd_route"] 2 | path = rd_route 3 | url = https://github.com/rodionovd/rd_route.git 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `SWRoute` is a tiny Swift wrapper over [`rd_route()`](https://github.com/rodionovd/rd_route). It allows you to route (hook) quite any function/method with another function/method or even a closure. 2 | 3 | **This code hasn't been updated since Xcode beta 1, so it may be outdated. Remember, it's just a proof-of-concept of what you can do in Swift.** 4 | 5 | If you're curious how it works, check out an article [„Function hooking in Swift“](https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift). 6 | 7 | > Going to use it in your iOS project? Please, read [the corresponding section](#swroute-and-ios) below. 8 | 9 | #### Exported interface 10 | 11 | ```swift 12 | class SwiftRoute { 13 | class func replace(function targetMethod : MethodT, with replacement : MethodT) -> Int 14 | } 15 | ``` 16 | 17 | ##### Arguments 18 | 19 | Argument | Type (in/out) | Description 20 | :--------: | :-----------: | :---------- 21 | `function` | in | _**(required)**_ a function/method to override 22 | `with` | in| _**(required)**_ any other function/method or closure to overrride a `function` with 23 | 24 | 25 | ##### Return value 26 | 27 | `KERN_SUCCESS` (== 0) upon success, `> 0` otherwise. 28 | 29 | ##### Example usage 30 | (see `SWRouteTests/SWRouteTests.swift` for more) 31 | 32 | ```swift 33 | class DemoClass { 34 | func demoMethod(arg: Int) -> Int { 35 | return (42 + arg); 36 | } 37 | } 38 | 39 | var err = SwiftRoute.replace(function: DemoClass().demoMethod, with: { 40 | (arg : Int) -> Int in 41 | return (90 + arg) 42 | }) 43 | 44 | ``` 45 | 46 | ## SWRoute and iOS 47 | 48 | Unfortunately `rd_route` (the back-end of `SWRoute`) doesn't work well on iOS (until jailbroken), because it does some tricks with memory pages that aren't allowed there. But you can choose any other library for function hooking instead! I recommend [`libevil`](https://github.com/landonf/libevil_patch) by Landon Fuller. 49 | 50 | You'll only need `rd_get_func_impl.c` source file included into your project to create your version of SWRoute: 51 | 52 | ```swift 53 | // Route functions in Swift using libevil and rd_get_func_impl() 54 | import Darwin 55 | 56 | @asmname("_rd_get_func_impl") 57 | func rd_get_func_impl(Q) -> UInt64; 58 | @asmname("evil_init") 59 | func evil_init(); 60 | @asmname("evil_override_ptr") 61 | func evil_override_ptr(UInt64, UInt64, CMutablePointer) -> CInt; 62 | 63 | class EvilRoute { 64 | struct onceToken { 65 | static var token : dispatch_once_t = 0 66 | } 67 | 68 | class func replace(function targetMethod : MethodT, with replacement : MethodT) -> Int 69 | { 70 | dispatch_once(&onceToken.token, { 71 | evil_init() 72 | }) 73 | 74 | let err: CInt = evil_override_ptr(rd_get_func_impl(DemoClass().demoMethod), 75 | rd_get_func_impl(someFunction), 76 | nil) 77 | 78 | return Int(err) 79 | } 80 | } 81 | ``` 82 | 83 | ### License 84 | 85 | [WTFPL](http://www.wtfpl.net/). 86 | 87 | ```cpp 88 | // Copyright © 2014 Dmitry Rodionov 89 | // This work is free. You can redistribute it and/or modify it under the 90 | // terms of the Do What The Fuck You Want To Public License, Version 2, 91 | // as published by Sam Hocevar. See the COPYING file for more details. 92 | ``` 93 | 94 | 95 | ------ 96 | 97 | If you found any bug(s) or something, please open an issue or a pull request — I'd appreciate your help! `(^,,^)` 98 | 99 | ------ 100 | 101 | *Dmitry Rodionov, 2014* 102 | *i.am.rodionovd@gmail.com* 103 | -------------------------------------------------------------------------------- /SWRoute.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0A940B1E194CEF5E00831127 /* SWRoute.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A940B1D194CEF5E00831127 /* SWRoute.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 0A940B24194CEF5E00831127 /* SWRoute.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A940B18194CEF5E00831127 /* SWRoute.framework */; }; 12 | 0A940B35194CEF8400831127 /* SWRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A940B34194CEF8400831127 /* SWRoute.swift */; }; 13 | 0A940B3A194CEFB800831127 /* rd_route.c in Sources */ = {isa = PBXBuildFile; fileRef = 0A940B38194CEFB800831127 /* rd_route.c */; }; 14 | 0A940B3B194CEFB800831127 /* rd_route.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A940B39194CEFB800831127 /* rd_route.h */; }; 15 | 0A940B3E194CEFE800831127 /* rd_get_func_impl.c in Sources */ = {isa = PBXBuildFile; fileRef = 0A940B3D194CEFE800831127 /* rd_get_func_impl.c */; }; 16 | 0AC4CCE1195598330053E665 /* SWRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A940B2A194CEF5E00831127 /* SWRouteTests.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 0A940B25194CEF5E00831127 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = 0A940B0F194CEF5E00831127 /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 0A940B17194CEF5E00831127; 25 | remoteInfo = SWRoute; 26 | }; 27 | 0A940B3F194CF17600831127 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 0A940B0F194CEF5E00831127 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 0A940B17194CEF5E00831127; 32 | remoteInfo = SWRoute; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 0A940B18194CEF5E00831127 /* SWRoute.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWRoute.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 0A940B1C194CEF5E00831127 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 0A940B1D194CEF5E00831127 /* SWRoute.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWRoute.h; sourceTree = ""; }; 40 | 0A940B23194CEF5E00831127 /* SWRouteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SWRouteTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 0A940B29194CEF5E00831127 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 0A940B2A194CEF5E00831127 /* SWRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SWRouteTests.swift; sourceTree = ""; }; 43 | 0A940B34194CEF8400831127 /* SWRoute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWRoute.swift; sourceTree = ""; }; 44 | 0A940B38194CEFB800831127 /* rd_route.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rd_route.c; path = rd_route/rd_route.c; sourceTree = SOURCE_ROOT; }; 45 | 0A940B39194CEFB800831127 /* rd_route.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rd_route.h; path = rd_route/rd_route.h; sourceTree = SOURCE_ROOT; }; 46 | 0A940B3D194CEFE800831127 /* rd_get_func_impl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rd_get_func_impl.c; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 0A940B14194CEF5E00831127 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 0A940B20194CEF5E00831127 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | 0A940B24194CEF5E00831127 /* SWRoute.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 0A940B0E194CEF5E00831127 = { 69 | isa = PBXGroup; 70 | children = ( 71 | 0A940B1A194CEF5E00831127 /* SWRoute */, 72 | 0A940B27194CEF5E00831127 /* SWRouteTests */, 73 | 0A940B19194CEF5E00831127 /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | 0A940B19194CEF5E00831127 /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 0A940B18194CEF5E00831127 /* SWRoute.framework */, 81 | 0A940B23194CEF5E00831127 /* SWRouteTests.xctest */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 0A940B1A194CEF5E00831127 /* SWRoute */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 0A940B34194CEF8400831127 /* SWRoute.swift */, 90 | 0A940B3D194CEFE800831127 /* rd_get_func_impl.c */, 91 | 0A940B1D194CEF5E00831127 /* SWRoute.h */, 92 | 0A940B3C194CEFBF00831127 /* rd_route */, 93 | 0A940B1B194CEF5E00831127 /* Supporting Files */, 94 | ); 95 | path = SWRoute; 96 | sourceTree = ""; 97 | }; 98 | 0A940B1B194CEF5E00831127 /* Supporting Files */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 0A940B1C194CEF5E00831127 /* Info.plist */, 102 | ); 103 | name = "Supporting Files"; 104 | sourceTree = ""; 105 | }; 106 | 0A940B27194CEF5E00831127 /* SWRouteTests */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 0A940B2A194CEF5E00831127 /* SWRouteTests.swift */, 110 | 0A940B28194CEF5E00831127 /* Supporting Files */, 111 | ); 112 | path = SWRouteTests; 113 | sourceTree = ""; 114 | }; 115 | 0A940B28194CEF5E00831127 /* Supporting Files */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 0A940B29194CEF5E00831127 /* Info.plist */, 119 | ); 120 | name = "Supporting Files"; 121 | sourceTree = ""; 122 | }; 123 | 0A940B3C194CEFBF00831127 /* rd_route */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 0A940B38194CEFB800831127 /* rd_route.c */, 127 | 0A940B39194CEFB800831127 /* rd_route.h */, 128 | ); 129 | name = rd_route; 130 | sourceTree = ""; 131 | }; 132 | /* End PBXGroup section */ 133 | 134 | /* Begin PBXHeadersBuildPhase section */ 135 | 0A940B15194CEF5E00831127 /* Headers */ = { 136 | isa = PBXHeadersBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | 0A940B3B194CEFB800831127 /* rd_route.h in Headers */, 140 | 0A940B1E194CEF5E00831127 /* SWRoute.h in Headers */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXHeadersBuildPhase section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | 0A940B17194CEF5E00831127 /* SWRoute */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = 0A940B2E194CEF5E00831127 /* Build configuration list for PBXNativeTarget "SWRoute" */; 150 | buildPhases = ( 151 | 0A940B13194CEF5E00831127 /* Sources */, 152 | 0A940B14194CEF5E00831127 /* Frameworks */, 153 | 0A940B15194CEF5E00831127 /* Headers */, 154 | 0A940B16194CEF5E00831127 /* Resources */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = SWRoute; 161 | productName = SWRoute; 162 | productReference = 0A940B18194CEF5E00831127 /* SWRoute.framework */; 163 | productType = "com.apple.product-type.framework"; 164 | }; 165 | 0A940B22194CEF5E00831127 /* SWRouteTests */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 0A940B31194CEF5E00831127 /* Build configuration list for PBXNativeTarget "SWRouteTests" */; 168 | buildPhases = ( 169 | 0A940B1F194CEF5E00831127 /* Sources */, 170 | 0A940B20194CEF5E00831127 /* Frameworks */, 171 | 0A940B21194CEF5E00831127 /* Resources */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | 0A940B26194CEF5E00831127 /* PBXTargetDependency */, 177 | 0A940B40194CF17600831127 /* PBXTargetDependency */, 178 | ); 179 | name = SWRouteTests; 180 | productName = SWRouteTests; 181 | productReference = 0A940B23194CEF5E00831127 /* SWRouteTests.xctest */; 182 | productType = "com.apple.product-type.bundle.unit-test"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | 0A940B0F194CEF5E00831127 /* Project object */ = { 188 | isa = PBXProject; 189 | attributes = { 190 | LastUpgradeCheck = 0600; 191 | ORGANIZATIONNAME = rodionovd; 192 | TargetAttributes = { 193 | 0A940B17194CEF5E00831127 = { 194 | CreatedOnToolsVersion = 6.0; 195 | }; 196 | 0A940B22194CEF5E00831127 = { 197 | CreatedOnToolsVersion = 6.0; 198 | TestTargetID = 0A940B17194CEF5E00831127; 199 | }; 200 | }; 201 | }; 202 | buildConfigurationList = 0A940B12194CEF5E00831127 /* Build configuration list for PBXProject "SWRoute" */; 203 | compatibilityVersion = "Xcode 3.2"; 204 | developmentRegion = English; 205 | hasScannedForEncodings = 0; 206 | knownRegions = ( 207 | en, 208 | ); 209 | mainGroup = 0A940B0E194CEF5E00831127; 210 | productRefGroup = 0A940B19194CEF5E00831127 /* Products */; 211 | projectDirPath = ""; 212 | projectRoot = ""; 213 | targets = ( 214 | 0A940B17194CEF5E00831127 /* SWRoute */, 215 | 0A940B22194CEF5E00831127 /* SWRouteTests */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | 0A940B16194CEF5E00831127 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | 0A940B21194CEF5E00831127 /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXResourcesBuildPhase section */ 236 | 237 | /* Begin PBXSourcesBuildPhase section */ 238 | 0A940B13194CEF5E00831127 /* Sources */ = { 239 | isa = PBXSourcesBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | 0A940B3E194CEFE800831127 /* rd_get_func_impl.c in Sources */, 243 | 0A940B35194CEF8400831127 /* SWRoute.swift in Sources */, 244 | 0A940B3A194CEFB800831127 /* rd_route.c in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | 0A940B1F194CEF5E00831127 /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 0AC4CCE1195598330053E665 /* SWRouteTests.swift in Sources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXSourcesBuildPhase section */ 257 | 258 | /* Begin PBXTargetDependency section */ 259 | 0A940B26194CEF5E00831127 /* PBXTargetDependency */ = { 260 | isa = PBXTargetDependency; 261 | target = 0A940B17194CEF5E00831127 /* SWRoute */; 262 | targetProxy = 0A940B25194CEF5E00831127 /* PBXContainerItemProxy */; 263 | }; 264 | 0A940B40194CF17600831127 /* PBXTargetDependency */ = { 265 | isa = PBXTargetDependency; 266 | target = 0A940B17194CEF5E00831127 /* SWRoute */; 267 | targetProxy = 0A940B3F194CF17600831127 /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin XCBuildConfiguration section */ 272 | 0A940B2C194CEF5E00831127 /* Debug */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 283 | CLANG_WARN_EMPTY_BODY = YES; 284 | CLANG_WARN_ENUM_CONVERSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | COPY_PHASE_STRIP = NO; 290 | CURRENT_PROJECT_VERSION = 1; 291 | ENABLE_STRICT_OBJC_MSGSEND = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_OPTIMIZATION_LEVEL = 0; 295 | GCC_PREPROCESSOR_DEFINITIONS = ( 296 | "DEBUG=1", 297 | "$(inherited)", 298 | ); 299 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 302 | GCC_WARN_UNDECLARED_SELECTOR = YES; 303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 304 | GCC_WARN_UNUSED_FUNCTION = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | MACOSX_DEPLOYMENT_TARGET = 10.9; 307 | METAL_ENABLE_DEBUG_INFO = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = macosx; 310 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 311 | VERSIONING_SYSTEM = "apple-generic"; 312 | VERSION_INFO_PREFIX = ""; 313 | }; 314 | name = Debug; 315 | }; 316 | 0A940B2D194CEF5E00831127 /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ALWAYS_SEARCH_USER_PATHS = NO; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | COPY_PHASE_STRIP = YES; 334 | CURRENT_PROJECT_VERSION = 1; 335 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 336 | ENABLE_NS_ASSERTIONS = NO; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 341 | GCC_WARN_UNDECLARED_SELECTOR = YES; 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 343 | GCC_WARN_UNUSED_FUNCTION = YES; 344 | GCC_WARN_UNUSED_VARIABLE = YES; 345 | MACOSX_DEPLOYMENT_TARGET = 10.9; 346 | METAL_ENABLE_DEBUG_INFO = NO; 347 | SDKROOT = macosx; 348 | VERSIONING_SYSTEM = "apple-generic"; 349 | VERSION_INFO_PREFIX = ""; 350 | }; 351 | name = Release; 352 | }; 353 | 0A940B2F194CEF5E00831127 /* Debug */ = { 354 | isa = XCBuildConfiguration; 355 | buildSettings = { 356 | CLANG_ENABLE_MODULES = YES; 357 | COMBINE_HIDPI_IMAGES = YES; 358 | DEFINES_MODULE = YES; 359 | DYLIB_COMPATIBILITY_VERSION = 1; 360 | DYLIB_CURRENT_VERSION = 1; 361 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 362 | FRAMEWORK_VERSION = A; 363 | INFOPLIST_FILE = SWRoute/Info.plist; 364 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 366 | PRODUCT_NAME = "$(TARGET_NAME)"; 367 | SKIP_INSTALL = YES; 368 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 369 | }; 370 | name = Debug; 371 | }; 372 | 0A940B30194CEF5E00831127 /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | CLANG_ENABLE_MODULES = YES; 376 | COMBINE_HIDPI_IMAGES = YES; 377 | DEFINES_MODULE = YES; 378 | DYLIB_COMPATIBILITY_VERSION = 1; 379 | DYLIB_CURRENT_VERSION = 1; 380 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 381 | FRAMEWORK_VERSION = A; 382 | INFOPLIST_FILE = SWRoute/Info.plist; 383 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 384 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 385 | PRODUCT_NAME = "$(TARGET_NAME)"; 386 | SKIP_INSTALL = YES; 387 | }; 388 | name = Release; 389 | }; 390 | 0A940B32194CEF5E00831127 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | COMBINE_HIDPI_IMAGES = YES; 394 | FRAMEWORK_SEARCH_PATHS = ( 395 | "$(DEVELOPER_FRAMEWORKS_DIR)", 396 | "$(inherited)", 397 | ); 398 | GCC_PREPROCESSOR_DEFINITIONS = ( 399 | "DEBUG=1", 400 | "$(inherited)", 401 | ); 402 | INFOPLIST_FILE = SWRouteTests/Info.plist; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 404 | METAL_ENABLE_DEBUG_INFO = YES; 405 | PRODUCT_NAME = "$(TARGET_NAME)"; 406 | }; 407 | name = Debug; 408 | }; 409 | 0A940B33194CEF5E00831127 /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | COMBINE_HIDPI_IMAGES = YES; 413 | FRAMEWORK_SEARCH_PATHS = ( 414 | "$(DEVELOPER_FRAMEWORKS_DIR)", 415 | "$(inherited)", 416 | ); 417 | INFOPLIST_FILE = SWRouteTests/Info.plist; 418 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 419 | METAL_ENABLE_DEBUG_INFO = NO; 420 | PRODUCT_NAME = "$(TARGET_NAME)"; 421 | }; 422 | name = Release; 423 | }; 424 | /* End XCBuildConfiguration section */ 425 | 426 | /* Begin XCConfigurationList section */ 427 | 0A940B12194CEF5E00831127 /* Build configuration list for PBXProject "SWRoute" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | 0A940B2C194CEF5E00831127 /* Debug */, 431 | 0A940B2D194CEF5E00831127 /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | 0A940B2E194CEF5E00831127 /* Build configuration list for PBXNativeTarget "SWRoute" */ = { 437 | isa = XCConfigurationList; 438 | buildConfigurations = ( 439 | 0A940B2F194CEF5E00831127 /* Debug */, 440 | 0A940B30194CEF5E00831127 /* Release */, 441 | ); 442 | defaultConfigurationIsVisible = 0; 443 | defaultConfigurationName = Release; 444 | }; 445 | 0A940B31194CEF5E00831127 /* Build configuration list for PBXNativeTarget "SWRouteTests" */ = { 446 | isa = XCConfigurationList; 447 | buildConfigurations = ( 448 | 0A940B32194CEF5E00831127 /* Debug */, 449 | 0A940B33194CEF5E00831127 /* Release */, 450 | ); 451 | defaultConfigurationIsVisible = 0; 452 | defaultConfigurationName = Release; 453 | }; 454 | /* End XCConfigurationList section */ 455 | }; 456 | rootObject = 0A940B0F194CEF5E00831127 /* Project object */; 457 | } 458 | -------------------------------------------------------------------------------- /SWRoute.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SWRoute/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | i.am.rodionovd.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSHumanReadableCopyright 24 | Copyright © 2014 rodionovd. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /SWRoute/SWRoute.h: -------------------------------------------------------------------------------- 1 | // 2 | // SWRoute.h 3 | // SWRoute 4 | // 5 | // Copyright © 2014 Dmitry Rodionov 6 | // This work is free. You can redistribute it and/or modify it under the 7 | // terms of the Do What The Fuck You Want To Public License, Version 2, 8 | // as published by Sam Hocevar. See the COPYING file for more details. 9 | 10 | #import 11 | 12 | //! Project version number for SWRoute. 13 | FOUNDATION_EXPORT double SWRouteVersionNumber; 14 | 15 | //! Project version string for SWRoute. 16 | FOUNDATION_EXPORT const unsigned char SWRouteVersionString[]; 17 | 18 | // In this header, you should import all the public headers of your framework using statements like #import 19 | -------------------------------------------------------------------------------- /SWRoute/SWRoute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWRoute.swift 3 | // SWRoute 4 | // 5 | // Copyright © 2014 Dmitry Rodionov 6 | // This work is free. You can redistribute it and/or modify it under the 7 | // terms of the Do What The Fuck You Want To Public License, Version 2, 8 | // as published by Sam Hocevar. See the COPYING file for more details. 9 | 10 | import Darwin 11 | 12 | @asmname("_rd_get_func_impl") 13 | func rd_get_func_impl(Q) -> uintptr_t; 14 | @asmname("rd_route") 15 | func rd_route(uintptr_t, uintptr_t, CMutablePointer) -> CInt; 16 | 17 | class SwiftRoute { 18 | class func replace(function targetMethod : MethodT, with replacement : MethodT) -> Int 19 | { 20 | // @todo: what can we do with a duplicate of the original function? 21 | return Int(rd_route(rd_get_func_impl(targetMethod), rd_get_func_impl(replacement), nil)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SWRoute/rd_get_func_impl.c: -------------------------------------------------------------------------------- 1 | // 2 | // rd_get_func_impl.c 3 | // SWRoute 4 | // 5 | // Copyright © 2014 Dmitry Rodionov 6 | // This work is free. You can redistribute it and/or modify it under the 7 | // terms of the Do What The Fuck You Want To Public License, Version 2, 8 | // as published by Sam Hocevar. See the COPYING file for more details. 9 | 10 | #include 11 | 12 | #define kObjectFieldOffset sizeof(uintptr_t) 13 | 14 | struct swift_func_object { 15 | uintptr_t *original_type_ptr; 16 | #if defined(__x86_64__) 17 | uintptr_t *unknown0; 18 | #else 19 | uintptr_t *unknown0, *unknown1; 20 | #endif 21 | uintptr_t function_address; 22 | uintptr_t *self; 23 | }; 24 | 25 | uintptr_t _rd_get_func_impl(void *func) 26 | { 27 | struct swift_func_object *obj = (struct swift_func_object *)*(uintptr_t *)(func + kObjectFieldOffset); 28 | 29 | return obj->function_address; 30 | } 31 | -------------------------------------------------------------------------------- /SWRouteTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | i.am.rodionovd.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SWRouteTests/SWRouteTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SWRouteTests.swift 3 | // SWRouteTests 4 | // 5 | // Created by Dmitry Rodionov on 6/15/14. 6 | // Copyright (c) 2014 rodionovd. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SWRoute 11 | 12 | class DemoClass { 13 | func demoMethod(arg: Int) -> Int { 14 | return (42 + arg); 15 | } 16 | } 17 | 18 | class SWRouteTests: XCTestCase { 19 | 20 | func testSWRoute() { 21 | 22 | /* Preconditions */ 23 | let arg = 13 24 | XCTAssert(DemoClass().demoMethod(arg) == (42 + arg)) 25 | 26 | /* 27 | * Replacing the method with a custom closure 28 | */ 29 | var err = SwiftRoute.replace(function: DemoClass().demoMethod, with: { 30 | (arg : Int) -> Int in 31 | return (90 + arg) 32 | }) 33 | XCTAssert(err == Int(KERN_SUCCESS)) 34 | XCTAssert(DemoClass().demoMethod(arg) == (90 + arg)) 35 | 36 | /* 37 | * Replacing the method with a function 38 | */ 39 | func replacement(arg : Int) -> Int { 40 | return (567 - arg); 41 | } 42 | err = SwiftRoute.replace(function: DemoClass().demoMethod, with: replacement) 43 | XCTAssert(err == Int(KERN_SUCCESS)) 44 | XCTAssert(DemoClass().demoMethod(arg) == (567 - arg)) 45 | 46 | /* 47 | * Replacing a function with another function 48 | */ 49 | func target(arg : Int) -> Int { 50 | return 0; 51 | } 52 | err = SwiftRoute.replace(function: target, with: replacement) 53 | XCTAssert(err == Int(KERN_SUCCESS)) 54 | XCTAssert(target(arg) == (567 - arg)) 55 | } 56 | } 57 | --------------------------------------------------------------------------------