├── .gitignore ├── Example └── MainThreadCheckerExample │ ├── .gitignore │ ├── MainThreadCheckerExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── MainThreadCheckerExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── MainThreadChecker │ │ ├── MTInitializer.h │ │ └── MTInitializer.m │ ├── SceneDelegate.h │ ├── SceneDelegate.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m │ ├── Podfile │ └── Podfile.lock ├── README.md ├── TrampolineHook.podspec └── TrampolineHook ├── PageAllocator ├── THDynamicAllocatorProtocol.h ├── THPageAllocator.h ├── THPageAllocator.m ├── THSimplePageAllocator.h ├── THSimplePageAllocator.m ├── THVariadicPageAllocator.h └── THVariadicPageAllocator.m ├── THInterceptor.h ├── THInterceptor.m ├── THPageDefinition.h ├── THPageDefinition.m └── arm64 ├── THPageDefinition_arm64.h ├── THPageVar_arm64.s ├── THPageVariadicContext_arm64.h ├── THPageVariadicContext_arm64.m └── THPage_arm64.s /.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 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/.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 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | 70 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 19F7A8942461C337003A03AD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F7A8932461C337003A03AD /* AppDelegate.m */; }; 11 | 19F7A8972461C337003A03AD /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F7A8962461C337003A03AD /* SceneDelegate.m */; }; 12 | 19F7A89A2461C337003A03AD /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F7A8992461C337003A03AD /* ViewController.m */; }; 13 | 19F7A89D2461C337003A03AD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 19F7A89B2461C337003A03AD /* Main.storyboard */; }; 14 | 19F7A89F2461C341003A03AD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19F7A89E2461C341003A03AD /* Assets.xcassets */; }; 15 | 19F7A8A22461C341003A03AD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 19F7A8A02461C341003A03AD /* LaunchScreen.storyboard */; }; 16 | 19F7A8A52461C341003A03AD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F7A8A42461C341003A03AD /* main.m */; }; 17 | 19F7A8AD2461CBD1003A03AD /* MTInitializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F7A8AB2461CBD1003A03AD /* MTInitializer.m */; }; 18 | 552F8534335B996C4EB83317 /* Pods_MainThreadCheckerExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 338E84C2D8EDF2F554D7B2CB /* Pods_MainThreadCheckerExample.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 19F7A88F2461C337003A03AD /* MainThreadCheckerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MainThreadCheckerExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 19F7A8922461C337003A03AD /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | 19F7A8932461C337003A03AD /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | 19F7A8952461C337003A03AD /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 26 | 19F7A8962461C337003A03AD /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 27 | 19F7A8982461C337003A03AD /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 28 | 19F7A8992461C337003A03AD /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 29 | 19F7A89C2461C337003A03AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | 19F7A89E2461C341003A03AD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 19F7A8A12461C341003A03AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 19F7A8A32461C341003A03AD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 19F7A8A42461C341003A03AD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | 19F7A8AB2461CBD1003A03AD /* MTInitializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTInitializer.m; sourceTree = ""; }; 35 | 19F7A8AC2461CBD1003A03AD /* MTInitializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTInitializer.h; sourceTree = ""; }; 36 | 338E84C2D8EDF2F554D7B2CB /* Pods_MainThreadCheckerExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MainThreadCheckerExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 69D80956308F0A84EEA6F30E /* Pods-MainThreadCheckerExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MainThreadCheckerExample.debug.xcconfig"; path = "Target Support Files/Pods-MainThreadCheckerExample/Pods-MainThreadCheckerExample.debug.xcconfig"; sourceTree = ""; }; 38 | 9440A485B254FBE63563CD7C /* Pods-MainThreadCheckerExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MainThreadCheckerExample.release.xcconfig"; path = "Target Support Files/Pods-MainThreadCheckerExample/Pods-MainThreadCheckerExample.release.xcconfig"; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 19F7A88C2461C337003A03AD /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 552F8534335B996C4EB83317 /* Pods_MainThreadCheckerExample.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 19F7A8862461C337003A03AD = { 54 | isa = PBXGroup; 55 | children = ( 56 | 19F7A8912461C337003A03AD /* MainThreadCheckerExample */, 57 | 19F7A8902461C337003A03AD /* Products */, 58 | 469CA3AE07E668D99B338C2E /* Pods */, 59 | 61A7FAFD2A70D09268683CE0 /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 19F7A8902461C337003A03AD /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 19F7A88F2461C337003A03AD /* MainThreadCheckerExample.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 19F7A8912461C337003A03AD /* MainThreadCheckerExample */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 19F7A8AE2461CBD3003A03AD /* MainThreadChecker */, 75 | 19F7A8922461C337003A03AD /* AppDelegate.h */, 76 | 19F7A8932461C337003A03AD /* AppDelegate.m */, 77 | 19F7A8952461C337003A03AD /* SceneDelegate.h */, 78 | 19F7A8962461C337003A03AD /* SceneDelegate.m */, 79 | 19F7A8982461C337003A03AD /* ViewController.h */, 80 | 19F7A8992461C337003A03AD /* ViewController.m */, 81 | 19F7A89B2461C337003A03AD /* Main.storyboard */, 82 | 19F7A89E2461C341003A03AD /* Assets.xcassets */, 83 | 19F7A8A02461C341003A03AD /* LaunchScreen.storyboard */, 84 | 19F7A8A32461C341003A03AD /* Info.plist */, 85 | 19F7A8A42461C341003A03AD /* main.m */, 86 | ); 87 | path = MainThreadCheckerExample; 88 | sourceTree = ""; 89 | }; 90 | 19F7A8AE2461CBD3003A03AD /* MainThreadChecker */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 19F7A8AC2461CBD1003A03AD /* MTInitializer.h */, 94 | 19F7A8AB2461CBD1003A03AD /* MTInitializer.m */, 95 | ); 96 | path = MainThreadChecker; 97 | sourceTree = ""; 98 | }; 99 | 469CA3AE07E668D99B338C2E /* Pods */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 69D80956308F0A84EEA6F30E /* Pods-MainThreadCheckerExample.debug.xcconfig */, 103 | 9440A485B254FBE63563CD7C /* Pods-MainThreadCheckerExample.release.xcconfig */, 104 | ); 105 | path = Pods; 106 | sourceTree = ""; 107 | }; 108 | 61A7FAFD2A70D09268683CE0 /* Frameworks */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 338E84C2D8EDF2F554D7B2CB /* Pods_MainThreadCheckerExample.framework */, 112 | ); 113 | name = Frameworks; 114 | sourceTree = ""; 115 | }; 116 | /* End PBXGroup section */ 117 | 118 | /* Begin PBXNativeTarget section */ 119 | 19F7A88E2461C337003A03AD /* MainThreadCheckerExample */ = { 120 | isa = PBXNativeTarget; 121 | buildConfigurationList = 19F7A8A82461C341003A03AD /* Build configuration list for PBXNativeTarget "MainThreadCheckerExample" */; 122 | buildPhases = ( 123 | E5933B8F0791D9E48BA9CCB6 /* [CP] Check Pods Manifest.lock */, 124 | 19F7A88B2461C337003A03AD /* Sources */, 125 | 19F7A88C2461C337003A03AD /* Frameworks */, 126 | 19F7A88D2461C337003A03AD /* Resources */, 127 | ); 128 | buildRules = ( 129 | ); 130 | dependencies = ( 131 | ); 132 | name = MainThreadCheckerExample; 133 | productName = MainThreadCheckerExample; 134 | productReference = 19F7A88F2461C337003A03AD /* MainThreadCheckerExample.app */; 135 | productType = "com.apple.product-type.application"; 136 | }; 137 | /* End PBXNativeTarget section */ 138 | 139 | /* Begin PBXProject section */ 140 | 19F7A8872461C337003A03AD /* Project object */ = { 141 | isa = PBXProject; 142 | attributes = { 143 | LastUpgradeCheck = 1140; 144 | ORGANIZATIONNAME = SatanWoo; 145 | TargetAttributes = { 146 | 19F7A88E2461C337003A03AD = { 147 | CreatedOnToolsVersion = 11.4.1; 148 | }; 149 | }; 150 | }; 151 | buildConfigurationList = 19F7A88A2461C337003A03AD /* Build configuration list for PBXProject "MainThreadCheckerExample" */; 152 | compatibilityVersion = "Xcode 9.3"; 153 | developmentRegion = en; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | en, 157 | Base, 158 | ); 159 | mainGroup = 19F7A8862461C337003A03AD; 160 | productRefGroup = 19F7A8902461C337003A03AD /* Products */; 161 | projectDirPath = ""; 162 | projectRoot = ""; 163 | targets = ( 164 | 19F7A88E2461C337003A03AD /* MainThreadCheckerExample */, 165 | ); 166 | }; 167 | /* End PBXProject section */ 168 | 169 | /* Begin PBXResourcesBuildPhase section */ 170 | 19F7A88D2461C337003A03AD /* Resources */ = { 171 | isa = PBXResourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 19F7A8A22461C341003A03AD /* LaunchScreen.storyboard in Resources */, 175 | 19F7A89F2461C341003A03AD /* Assets.xcassets in Resources */, 176 | 19F7A89D2461C337003A03AD /* Main.storyboard in Resources */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | /* End PBXResourcesBuildPhase section */ 181 | 182 | /* Begin PBXShellScriptBuildPhase section */ 183 | E5933B8F0791D9E48BA9CCB6 /* [CP] Check Pods Manifest.lock */ = { 184 | isa = PBXShellScriptBuildPhase; 185 | buildActionMask = 2147483647; 186 | files = ( 187 | ); 188 | inputFileListPaths = ( 189 | ); 190 | inputPaths = ( 191 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 192 | "${PODS_ROOT}/Manifest.lock", 193 | ); 194 | name = "[CP] Check Pods Manifest.lock"; 195 | outputFileListPaths = ( 196 | ); 197 | outputPaths = ( 198 | "$(DERIVED_FILE_DIR)/Pods-MainThreadCheckerExample-checkManifestLockResult.txt", 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | shellPath = /bin/sh; 202 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 203 | showEnvVarsInLog = 0; 204 | }; 205 | /* End PBXShellScriptBuildPhase section */ 206 | 207 | /* Begin PBXSourcesBuildPhase section */ 208 | 19F7A88B2461C337003A03AD /* Sources */ = { 209 | isa = PBXSourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | 19F7A89A2461C337003A03AD /* ViewController.m in Sources */, 213 | 19F7A8942461C337003A03AD /* AppDelegate.m in Sources */, 214 | 19F7A8A52461C341003A03AD /* main.m in Sources */, 215 | 19F7A8AD2461CBD1003A03AD /* MTInitializer.m in Sources */, 216 | 19F7A8972461C337003A03AD /* SceneDelegate.m in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXSourcesBuildPhase section */ 221 | 222 | /* Begin PBXVariantGroup section */ 223 | 19F7A89B2461C337003A03AD /* Main.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 19F7A89C2461C337003A03AD /* Base */, 227 | ); 228 | name = Main.storyboard; 229 | sourceTree = ""; 230 | }; 231 | 19F7A8A02461C341003A03AD /* LaunchScreen.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 19F7A8A12461C341003A03AD /* Base */, 235 | ); 236 | name = LaunchScreen.storyboard; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | 19F7A8A62461C341003A03AD /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_ENABLE_OBJC_WEAK = YES; 253 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_COMMA = YES; 256 | CLANG_WARN_CONSTANT_CONVERSION = YES; 257 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 260 | CLANG_WARN_EMPTY_BODY = YES; 261 | CLANG_WARN_ENUM_CONVERSION = YES; 262 | CLANG_WARN_INFINITE_RECURSION = YES; 263 | CLANG_WARN_INT_CONVERSION = YES; 264 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 266 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 268 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 269 | CLANG_WARN_STRICT_PROTOTYPES = YES; 270 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 271 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | COPY_PHASE_STRIP = NO; 275 | DEBUG_INFORMATION_FORMAT = dwarf; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | ENABLE_TESTABILITY = YES; 278 | GCC_C_LANGUAGE_STANDARD = gnu11; 279 | GCC_DYNAMIC_NO_PIC = NO; 280 | GCC_NO_COMMON_BLOCKS = YES; 281 | GCC_OPTIMIZATION_LEVEL = 0; 282 | GCC_PREPROCESSOR_DEFINITIONS = ( 283 | "DEBUG=1", 284 | "$(inherited)", 285 | ); 286 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 287 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 288 | GCC_WARN_UNDECLARED_SELECTOR = YES; 289 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 290 | GCC_WARN_UNUSED_FUNCTION = YES; 291 | GCC_WARN_UNUSED_VARIABLE = YES; 292 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 293 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 294 | MTL_FAST_MATH = YES; 295 | ONLY_ACTIVE_ARCH = YES; 296 | SDKROOT = iphoneos; 297 | }; 298 | name = Debug; 299 | }; 300 | 19F7A8A72461C341003A03AD /* Release */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ALWAYS_SEARCH_USER_PATHS = NO; 304 | CLANG_ANALYZER_NONNULL = YES; 305 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_ENABLE_OBJC_WEAK = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 327 | CLANG_WARN_STRICT_PROTOTYPES = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 329 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 330 | CLANG_WARN_UNREACHABLE_CODE = YES; 331 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 332 | COPY_PHASE_STRIP = NO; 333 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 334 | ENABLE_NS_ASSERTIONS = NO; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu11; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | MTL_FAST_MATH = YES; 347 | SDKROOT = iphoneos; 348 | VALIDATE_PRODUCT = YES; 349 | }; 350 | name = Release; 351 | }; 352 | 19F7A8A92461C341003A03AD /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = 69D80956308F0A84EEA6F30E /* Pods-MainThreadCheckerExample.debug.xcconfig */; 355 | buildSettings = { 356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 357 | CODE_SIGN_STYLE = Automatic; 358 | DEVELOPMENT_TEAM = T3LQBYQCVZ; 359 | INFOPLIST_FILE = MainThreadCheckerExample/Info.plist; 360 | LD_RUNPATH_SEARCH_PATHS = ( 361 | "$(inherited)", 362 | "@executable_path/Frameworks", 363 | ); 364 | PRODUCT_BUNDLE_IDENTIFIER = SJTU.MainThreadCheckerExample123; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | TARGETED_DEVICE_FAMILY = "1,2"; 367 | }; 368 | name = Debug; 369 | }; 370 | 19F7A8AA2461C341003A03AD /* Release */ = { 371 | isa = XCBuildConfiguration; 372 | baseConfigurationReference = 9440A485B254FBE63563CD7C /* Pods-MainThreadCheckerExample.release.xcconfig */; 373 | buildSettings = { 374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 375 | CODE_SIGN_STYLE = Automatic; 376 | DEVELOPMENT_TEAM = T3LQBYQCVZ; 377 | INFOPLIST_FILE = MainThreadCheckerExample/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = ( 379 | "$(inherited)", 380 | "@executable_path/Frameworks", 381 | ); 382 | PRODUCT_BUNDLE_IDENTIFIER = SJTU.MainThreadCheckerExample; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | TARGETED_DEVICE_FAMILY = "1,2"; 385 | }; 386 | name = Release; 387 | }; 388 | /* End XCBuildConfiguration section */ 389 | 390 | /* Begin XCConfigurationList section */ 391 | 19F7A88A2461C337003A03AD /* Build configuration list for PBXProject "MainThreadCheckerExample" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | 19F7A8A62461C341003A03AD /* Debug */, 395 | 19F7A8A72461C341003A03AD /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | 19F7A8A82461C341003A03AD /* Build configuration list for PBXNativeTarget "MainThreadCheckerExample" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | 19F7A8A92461C341003A03AD /* Debug */, 404 | 19F7A8AA2461C341003A03AD /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | /* End XCConfigurationList section */ 410 | }; 411 | rootObject = 19F7A8872461C337003A03AD /* Project object */; 412 | } 413 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | @synthesize window = _window; 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | return YES; 22 | } 23 | 24 | #pragma mark - UISceneSession lifecycle 25 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 26 | // Called when a new scene session is being created. 27 | // Use this method to select a configuration to create the new scene with. 28 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 29 | } 30 | 31 | 32 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 33 | // Called when the user discards a scene session. 34 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 35 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 36 | } 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/MainThreadChecker/MTInitializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTInitializer.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface MTInitializer : NSObject 14 | 15 | + (void)enableMainThreadChecker; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/MainThreadChecker/MTInitializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTInitializer.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "MTInitializer.h" 10 | #import "THInterceptor.h" 11 | 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | 20 | #ifdef __LP64__ 21 | typedef struct mach_header_64 mt_macho_header; 22 | #else 23 | typedef struct mach_header mt_macho_header; 24 | #endif 25 | 26 | static void *MTGetDataSection(const struct mach_header *header, const char *sectname, size_t *bytes) 27 | { 28 | void *data = getsectiondata((void *)header, "__DATA", sectname, bytes); 29 | if (!data) { 30 | data = getsectiondata((void *)header, "__DATA_CONST", sectname, bytes); 31 | } 32 | return data; 33 | } 34 | 35 | static void MTFindClassesToSwizzleInImage(const mt_macho_header *header, const char *binaryName, NSMutableArray *toSwizzleClasses) 36 | { 37 | if (header == NULL) return; 38 | if (binaryName == NULL) return; 39 | 40 | unsigned long size = 0; 41 | unsigned int classCount = 0; 42 | 43 | Class *data = (Class *)MTGetDataSection(header, "__objc_classlist", &size); 44 | if (data == NULL) { 45 | data = objc_copyClassList(&classCount); 46 | } else { 47 | classCount = (unsigned int)(size / sizeof(void *)); 48 | } 49 | 50 | for (unsigned int i = 0; i < classCount; i++) { 51 | Class cls = data[i]; 52 | const char *className = class_getName(cls); 53 | const char *imageName = class_getImageName(cls); 54 | 55 | if (strncmp(className, "_", 1) == 0) continue; 56 | if (strcmp(imageName, binaryName) != 0) continue; 57 | 58 | BOOL isInheritedSubClass = NO; 59 | Class superCls = cls; 60 | while (superCls && superCls != [NSObject class]) { 61 | if (superCls == [UIView class]) { 62 | isInheritedSubClass = YES; 63 | break; 64 | } 65 | superCls = class_getSuperclass(superCls); 66 | } 67 | 68 | if (isInheritedSubClass) { 69 | [toSwizzleClasses addObject:cls]; 70 | } 71 | } 72 | } 73 | 74 | static void MTMainThreadChecker(id obj, SEL selector) // 75 | { 76 | if (![NSThread isMainThread]) { 77 | NSLog(@"[MTMainThreadChecker]::Found issue on %@ with selector %@", obj, NSStringFromSelector(selector)); 78 | } 79 | } 80 | 81 | static bool MTAddSwizzler(Class cls, SEL selector) 82 | { 83 | Method origMethod = class_getInstanceMethod(cls, selector); 84 | if (!origMethod) return false; 85 | 86 | IMP originIMP = method_getImplementation(origMethod); 87 | 88 | static THInterceptor *interceptor = nil; 89 | if (!interceptor) { 90 | interceptor = [[THInterceptor alloc] initWithRedirectionFunction:(IMP)MTMainThreadChecker]; 91 | } 92 | 93 | if (!interceptor) return false; 94 | 95 | THInterceptorResult *result = [interceptor interceptFunction:originIMP]; 96 | if (!result || result.state == THInterceptStateFailed) return false; 97 | 98 | method_setImplementation(origMethod, result.replacedAddress); 99 | 100 | return true; 101 | } 102 | 103 | @implementation MTInitializer 104 | 105 | + (void)enableMainThreadChecker 106 | { 107 | const char *UIKitImageName = class_getImageName([UIResponder class]); 108 | if (!UIKitImageName) { 109 | NSAssert(UIKitImageName != NULL, @"[MTInitializer]::Failed to find UIKItCore/UIKit binary"); 110 | return; 111 | } 112 | 113 | uint32_t imageCount = _dyld_image_count(); 114 | NSMutableArray *toSwizzleClasses = @[].mutableCopy; 115 | 116 | for (uint32_t idx = 0; idx < imageCount; idx++) { 117 | const char *binaryName = _dyld_get_image_name(idx); 118 | 119 | if (strcmp(binaryName, UIKitImageName) == 0) { 120 | const mt_macho_header *header = (const mt_macho_header *)_dyld_get_image_header(idx); 121 | MTFindClassesToSwizzleInImage(header, binaryName, toSwizzleClasses); 122 | break; 123 | } 124 | } 125 | 126 | // TODO: add webkit support 127 | // const char *webkitImageName = class_getImageName(NSClassFromString("WKWebView")); 128 | // if (webkitImageName) {} 129 | 130 | NSSet *ignoreList = [MTInitializer MTIgnoreSwizzleList]; 131 | 132 | for (Class cls in toSwizzleClasses) { 133 | unsigned int methodCount = 0; 134 | Method *methods = class_copyMethodList(cls, &methodCount); 135 | 136 | for (unsigned int i = 0; i < methodCount; i++) { 137 | Method m = *(methods + i); 138 | SEL sel = method_getName(m); 139 | 140 | NSString *selName = NSStringFromSelector(sel); 141 | 142 | if ([ignoreList containsObject:selName]) continue; 143 | 144 | if ([selName hasPrefix:@"nsli_"] || 145 | [selName hasPrefix:@"nsis_"]) { 146 | continue;; 147 | } 148 | 149 | bool ret = MTAddSwizzler(cls, sel); 150 | if (!ret) { 151 | NSAssert(ret, @"[MTInitializer]:: Add swizzler failed %@ %@", NSStringFromClass(cls), selName); 152 | } 153 | } 154 | 155 | free(methods); 156 | methods = NULL; 157 | } 158 | } 159 | 160 | + (NSSet *)MTIgnoreSwizzleList 161 | { 162 | static dispatch_once_t onceToken; 163 | static NSSet *ignoreList; 164 | dispatch_once(&onceToken, ^{ 165 | NSArray *lists = @[ 166 | /*UIViewController的:*/ 167 | @".cxx_destruct", 168 | @"dealloc", 169 | @"_isDeallocating", 170 | @"release", 171 | @"autorelease", 172 | @"retain", 173 | @"Retain", 174 | @"_tryRetain", 175 | @"copy", 176 | /*UIView的:*/ 177 | @"nsis_descriptionOfVariable:", 178 | /*NSObject的:*/ 179 | @"respondsToSelector:", 180 | @"class", 181 | @"methodSignatureForSelector:", 182 | @"allowsWeakReference", 183 | @"retainWeakReference", 184 | @"init", 185 | @"forwardInvocation:", 186 | @"description", 187 | @"debugDescription", 188 | @"self", 189 | @"beginBackgroundTaskWithExpirationHandler:", 190 | @"beginBackgroundTaskWithName:expirationHandler:", 191 | @"endBackgroundTask:", 192 | @"lockFocus", 193 | @"lockFocusIfCanDraw", 194 | @"lockFocusIfCanDraw" 195 | ]; 196 | 197 | ignoreList = [NSSet setWithArray:lists]; 198 | }); 199 | return ignoreList; 200 | } 201 | 202 | @end 203 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "SceneDelegate.h" 10 | 11 | @interface SceneDelegate () 12 | 13 | @end 14 | 15 | @implementation SceneDelegate 16 | 17 | 18 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 19 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 20 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 21 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 22 | } 23 | 24 | 25 | - (void)sceneDidDisconnect:(UIScene *)scene { 26 | // Called as the scene is being released by the system. 27 | // This occurs shortly after the scene enters the background, or when its session is discarded. 28 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 29 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 30 | } 31 | 32 | 33 | - (void)sceneDidBecomeActive:(UIScene *)scene { 34 | // Called when the scene has moved from an inactive state to an active state. 35 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 36 | } 37 | 38 | 39 | - (void)sceneWillResignActive:(UIScene *)scene { 40 | // Called when the scene will move from an active state to an inactive state. 41 | // This may occur due to temporary interruptions (ex. an incoming phone call). 42 | } 43 | 44 | 45 | - (void)sceneWillEnterForeground:(UIScene *)scene { 46 | // Called as the scene transitions from the background to the foreground. 47 | // Use this method to undo the changes made on entering the background. 48 | } 49 | 50 | 51 | - (void)sceneDidEnterBackground:(UIScene *)scene { 52 | // Called as the scene transitions from the foreground to the background. 53 | // Use this method to save data, release shared resources, and store enough scene-specific state information 54 | // to restore the scene back to its current state. 55 | } 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "MainThreadChecker/MTInitializer.h" 11 | #import 12 | 13 | @interface ViewController () 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | + (void)load 19 | { 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | [MTInitializer enableMainThreadChecker]; 23 | }); 24 | } 25 | 26 | - (void)viewDidLoad 27 | { 28 | [super viewDidLoad]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/MainThreadCheckerExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MainThreadCheckerExample 4 | // 5 | // Created by z on 2020/5/5. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | use_frameworks! 4 | 5 | target 'MainThreadCheckerExample' do 6 | pod 'TrampolineHook', :path => '../../' 7 | end 8 | -------------------------------------------------------------------------------- /Example/MainThreadCheckerExample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - TrampolineHook (0.0.5) 3 | 4 | DEPENDENCIES: 5 | - TrampolineHook (from `../../`) 6 | 7 | EXTERNAL SOURCES: 8 | TrampolineHook: 9 | :path: "../../" 10 | 11 | SPEC CHECKSUMS: 12 | TrampolineHook: 8611b14b8d7158822fba5e3ad8b73ddae11b29de 13 | 14 | PODFILE CHECKSUM: ff3c7b28464fc465967e0c109c496182ec835887 15 | 16 | COCOAPODS: 1.9.1 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrampolineHook 2 | **A solution for centralized method redirection.** 3 | 4 | You can read articles about underlying implementation of this project. 5 | 6 | 1. [基于桥的全量方法Hook方案 - 探究苹果主线程检查实现](http://satanwoo.github.io/2017/09/24/mainthreadchecker1/) 7 | 2. [基于桥的全量方法 Hook 方案(2) - 全新升级](http://satanwoo.github.io/2020/04/22/NewBridgeHook/) 8 | 3. [基于桥的全量方法 Hook 方案(3)- 开源 TrampolineHook](http://satanwoo.github.io/2020/04/26/TrampolineHookOpenSource/) 9 | 4. [TrampolineHook - 解决栈污染问题支持变参 Hook](http://satanwoo.github.io/2020/05/18/TrampolineHookStack/) 10 | 11 | 12 | ## 1. Usage 13 | 14 | The interface of TrampolineHook is very very simple. Only two steps are required. 15 | 16 | 1. Create the global interceptor with your centralized function as follows: 17 | 18 | ``` 19 | // Suppose you have function defined as 20 | 21 | // C Format 22 | void myInterceptor 23 | { 24 | // bla bla bla 25 | } 26 | 27 | // Create the inteceptor with your interceptor function 28 | THInterceptor *interceptor = [[THInterceptor alloc] initWithRedirectionFunction:(IMP)myInterceptor]; 29 | ``` 30 | 31 | 2. Intercept any function you want no matter what the method signature it is. 32 | 33 | ``` 34 | // Suppose you want to intercept the call of - [UIView initWithFrame:] 35 | Method m = class_getInstanceMethod([UIView class], @selector(initWithFrame:)); 36 | IMP imp = method_getImplementation(m); 37 | 38 | // Intercept the imp 39 | THInterceptorResult *result = [interceptor interceptFunction:imp]; 40 | 41 | // You can check the result.state to find whether the inteception is successfully carried out or not. 42 | result.state == THInterceptStateSuccess 43 | ``` 44 | 45 | 46 | 47 | ## 2. How to debug 48 | 49 | The debug of interception is not very easy. 50 | 51 | **Remember always use the `result.replacedAddress` returned from `interceptFunction` for breakpoint.** 52 | 53 | 54 | 55 | ## 3. Example 56 | There is one typical example associated with this open source project called **MainThreadChecker**。It is the rewritten version of the implementation in **Apple libMainThreadChecker.dylib** 57 | 58 | It is almost the same as the one of Apple based on my own reverse engineering. **No Private APIs used.** 59 | 60 | The usage of MainThreadChecker is quite easy. 61 | 62 | ``` 63 | + (void)load 64 | { 65 | [MTInitializer enableMainThreadChecker] 66 | } 67 | ``` 68 | 69 | 70 | 71 | ## 4. TODO 72 | 73 | - [x] API Stability. 74 | - [x] Varadic Argument Interceptor. 75 | - [ ] More examples. 76 | - [ ] Performance Benchmark. 77 | 78 | You can view the **Project** tab to follow the process of this project. 79 | 80 | 81 | 82 | ## 5.Reference 83 | 84 | The great works listed below inspired me a lot during the development of **TrampolineHook**. 85 | 86 | - [Dobby](https://github.com/jmpews/Dobby) @jmpews 87 | 88 | 89 | 90 | 91 | ## 6. License 92 | 93 | Copyright 2020 @SatanWoo 94 | 95 | Checkout [License](https://github.com/SatanWoo/TrampolineHook/blob/master/LICENSE) 96 | -------------------------------------------------------------------------------- /TrampolineHook.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "TrampolineHook" 3 | s.version = "0.0.5" 4 | s.summary = "A solution for centralized method redirection" 5 | s.description = <<-DESC 6 | Intercept any method implementation with a single method. 7 | DESC 8 | s.homepage = "https://github.com/SatanWoo/TrampolineHook" 9 | 10 | s.license = { :type => 'MIT', :file => 'LICENSE' } 11 | s.author = { "SatanWoo" => "" } 12 | s.source = { :git => "https://github.com/SatanWoo/TrampolineHook.git", :tag => s.version.to_s } 13 | 14 | s.source_files = "TrampolineHook/*.{h,m}", 15 | "TrampolineHook/PageAllocator/*.{h,m}", 16 | "TrampolineHook/arm64/*.{h,m}", 17 | "TrampolineHook/arm64/THPage_arm64.s", 18 | "TrampolineHook/arm64/THPageVar_arm64.s" 19 | 20 | s.public_header_files = "Trampoline/THInterceptor.h" 21 | s.static_framework = true 22 | 23 | s.ios.deployment_target = "9.0" 24 | s.requires_arc = true 25 | 26 | end 27 | 28 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THDynamicAllocatorProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // THDynamicAllocatorProtocol.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/18. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #ifndef THDynamicAllocatorProtocol_h 10 | #define THDynamicAllocatorProtocol_h 11 | #import 12 | 13 | @protocol THDynamicAllocatable 14 | @required 15 | 16 | - (instancetype)initWithRedirectionFunction:(IMP)redirectFunction; 17 | - (IMP)allocateDynamicPageForFunction:(IMP)functionAdress; 18 | 19 | @end 20 | 21 | 22 | #endif /* THDynamicAllocatorProtocol_h */ 23 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THPageAllocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPageAllocator.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/19. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "THDynamicAllocatorProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface THPageAllocator : NSObject 15 | @property (nonatomic, unsafe_unretained, readonly) IMP redirectFunction; 16 | 17 | - (void)configurePageLayoutForNewPage:(void *)newPage; 18 | - (BOOL)isValidReusablePage:(void *)resuablePage; 19 | - (void *)templatePageAddress; 20 | - (IMP)replaceAddress:(IMP)functionAddress inPage:(void *)page; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THPageAllocator.m: -------------------------------------------------------------------------------- 1 | // 2 | // THPageAllocator.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/19. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "THPageAllocator.h" 10 | #import "THPageDefinition.h" 11 | 12 | @interface THPageAllocator() 13 | @property (nonatomic, unsafe_unretained, readwrite) IMP redirectionFunction; 14 | @property (nonatomic, strong) NSMutableArray *dynamicPages; 15 | @end 16 | 17 | @implementation THPageAllocator 18 | 19 | - (instancetype)initWithRedirectionFunction:(IMP)redirectFunction 20 | { 21 | self = [super init]; 22 | if (self) { 23 | _redirectFunction = redirectFunction; 24 | } 25 | return self; 26 | } 27 | 28 | - (IMP)allocateDynamicPageForFunction:(IMP)functionAdress 29 | { 30 | if (!functionAdress) return NULL; 31 | 32 | void *dynamicePage = [self fetchCandidiateDynamicPage]; 33 | 34 | if (!dynamicePage) return NULL; 35 | 36 | return [self replaceAddress:functionAdress inPage:dynamicePage]; 37 | } 38 | 39 | #pragma mark - Abstract Function 40 | - (void)configurePageLayoutForNewPage:(void *)newPage 41 | { 42 | NSException *exception = [NSException exceptionWithName:@"com.satanwoo.pageallocator" reason:@" must be override by subclass" userInfo:nil]; 43 | [exception raise]; 44 | } 45 | 46 | - (BOOL)isValidReusablePage:(void *)resuablePage 47 | { 48 | NSException *exception = [NSException exceptionWithName:@"com.satanwoo.pageallocator" reason:@" must be override by subclass" userInfo:nil]; 49 | [exception raise]; 50 | 51 | return FALSE; 52 | } 53 | 54 | - (void *)templatePageAddress 55 | { 56 | NSException *exception = [NSException exceptionWithName:@"com.satanwoo.pageallocator" reason:@" must be override by subclass" userInfo:nil]; 57 | [exception raise]; 58 | 59 | return NULL; 60 | } 61 | 62 | - (IMP)replaceAddress:(IMP)functionAddress inPage:(void *)page 63 | { 64 | NSException *exception = [NSException exceptionWithName:@"com.satanwoo.pageallocator" reason:@" must be override by subclass" userInfo:nil]; 65 | [exception raise]; 66 | 67 | return NULL; 68 | } 69 | 70 | #pragma mark - Private 71 | - (void *)fetchCandidiateDynamicPage 72 | { 73 | void *reusablePage = [[self.dynamicPages lastObject] pointerValue]; 74 | 75 | if (![self isValidReusablePage:reusablePage]) { 76 | 77 | void *toCopyAddress = [self templatePageAddress]; 78 | if (!toCopyAddress) return NULL; 79 | 80 | reusablePage = (void *)THCreateDynamicePage(toCopyAddress); 81 | if (!reusablePage) return NULL; 82 | 83 | [self configurePageLayoutForNewPage:reusablePage]; 84 | 85 | [self.dynamicPages addObject:[NSValue valueWithPointer:reusablePage]]; 86 | } 87 | return reusablePage; 88 | } 89 | 90 | #pragma mark - Getter 91 | - (NSMutableArray *)dynamicPages 92 | { 93 | if (!_dynamicPages) { 94 | _dynamicPages = @[].mutableCopy; 95 | } 96 | return _dynamicPages; 97 | } 98 | 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THSimplePageAllocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // THDynamicAllocator.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "THPageAllocator.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface THSimplePageAllocator : THPageAllocator 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THSimplePageAllocator.m: -------------------------------------------------------------------------------- 1 | // 2 | // THDynamicAllocator.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "THSimplePageAllocator.h" 10 | #import "THPageDefinition.h" 11 | 12 | FOUNDATION_EXTERN id th_dynamic_page(id, SEL); 13 | 14 | #if defined(__arm64__) 15 | #import "THPageDefinition_arm64.h" 16 | static const int32_t THSimplePageInstructionCount = 32; 17 | #else 18 | #error x86_64 & arm64e to be supported 19 | #endif 20 | 21 | static const size_t THNumberOfDataPerSimplePage = (THPageSize - THSimplePageInstructionCount * sizeof(int32_t)) / sizeof(THDynamicPageEntryGroup); 22 | 23 | typedef struct { 24 | union { 25 | struct { 26 | IMP redirectFunction; 27 | int32_t nextAvailableIndex; 28 | }; 29 | 30 | int32_t placeholder[THSimplePageInstructionCount]; 31 | }; 32 | 33 | THDynamicData dynamicData[THNumberOfDataPerSimplePage]; 34 | } THDataPage; 35 | 36 | typedef struct { 37 | int32_t fixedInstructions[THSimplePageInstructionCount]; 38 | THDynamicPageEntryGroup jumpInstructions[THNumberOfDataPerSimplePage]; 39 | } THCodePage; 40 | 41 | typedef struct { 42 | THDataPage dataPage; 43 | THCodePage codePage; 44 | } THDynamicPage; 45 | 46 | 47 | @implementation THSimplePageAllocator 48 | 49 | - (void)configurePageLayoutForNewPage:(void *)newPage 50 | { 51 | if (!newPage) return; 52 | 53 | THDynamicPage *page = (THDynamicPage *)newPage; 54 | page->dataPage.redirectFunction = self.redirectFunction; 55 | } 56 | 57 | - (BOOL)isValidReusablePage:(void *)resuablePage 58 | { 59 | if (!resuablePage) return FALSE; 60 | 61 | THDynamicPage *page = (THDynamicPage *)resuablePage; 62 | if (page->dataPage.nextAvailableIndex == THNumberOfDataPerSimplePage) return FALSE; 63 | return YES; 64 | } 65 | 66 | - (void *)templatePageAddress 67 | { 68 | return &th_dynamic_page; 69 | } 70 | 71 | - (IMP)replaceAddress:(IMP)functionAddress inPage:(void *)page 72 | { 73 | if (!page) return NULL; 74 | 75 | THDynamicPage *dynamicPage = (THDynamicPage *)page; 76 | 77 | int slot = dynamicPage->dataPage.nextAvailableIndex; 78 | dynamicPage->dataPage.dynamicData[slot].originIMP = (IMP)functionAddress; 79 | dynamicPage->dataPage.nextAvailableIndex++; 80 | 81 | return (IMP)&dynamicPage->codePage.jumpInstructions[slot]; 82 | } 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THVariadicPageAllocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // THDynamicPageVariadicAllocator.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/18. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "THPageAllocator.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface THVariadicPageAllocator : THPageAllocator 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /TrampolineHook/PageAllocator/THVariadicPageAllocator.m: -------------------------------------------------------------------------------- 1 | // 2 | // THDynamicPageVariadicAllocator.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/18. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "THVariadicPageAllocator.h" 10 | #import "THPageDefinition.h" 11 | 12 | FOUNDATION_EXTERN id th_dynamic_page_var(id, SEL); 13 | 14 | #if defined(__arm64__) 15 | #import "THPageDefinition_arm64.h" 16 | #import "THPageVariadicContext_arm64.h" 17 | static const int32_t THDynamicPageVaradicInstructionCount = 10; 18 | #else 19 | #error x86_64 & arm64e to be supported 20 | #endif 21 | 22 | static const size_t THNumberOfDataPerVariadicPage = (THPageSize - THDynamicPageVaradicInstructionCount * sizeof(int32_t)) / sizeof(THDynamicPageEntryGroup); 23 | 24 | typedef struct { 25 | union { 26 | struct { 27 | IMP redirectFunction; 28 | IMP preFunction; 29 | IMP postFunction; 30 | int32_t nextAvailableIndex; 31 | }; 32 | 33 | int32_t placeholder[THDynamicPageVaradicInstructionCount]; 34 | }; 35 | 36 | THDynamicData dynamicData[THNumberOfDataPerVariadicPage]; 37 | } THVariadicDataPage; 38 | 39 | typedef struct { 40 | int32_t fixedInstructions[THDynamicPageVaradicInstructionCount]; 41 | THDynamicPageEntryGroup jumpInstructions[THNumberOfDataPerVariadicPage]; 42 | } THVariadicCodePage; 43 | 44 | typedef struct { 45 | THVariadicDataPage dataPage; 46 | THVariadicCodePage codePage; 47 | } THVariadicDynamicPage; 48 | 49 | @interface THVariadicPageAllocator() 50 | @property (nonatomic, unsafe_unretained, readonly) IMP preFunction; 51 | @property (nonatomic, unsafe_unretained, readonly) IMP postFunction; 52 | @end 53 | 54 | @implementation THVariadicPageAllocator 55 | 56 | - (instancetype)initWithRedirectionFunction:(IMP)redirectFunction 57 | { 58 | self = [super initWithRedirectionFunction:redirectFunction]; 59 | if (self) { 60 | _preFunction = (IMP)THPageVariadicContextPre; 61 | _postFunction = (IMP)THPageVariadicContextPost; 62 | } 63 | return self; 64 | } 65 | 66 | - (void)configurePageLayoutForNewPage:(void *)newPage 67 | { 68 | if (!newPage) return; 69 | 70 | THVariadicDynamicPage *page = (THVariadicDynamicPage *)newPage; 71 | page->dataPage.redirectFunction = self.redirectFunction; 72 | page->dataPage.preFunction = self.preFunction; 73 | page->dataPage.postFunction = self.postFunction; 74 | } 75 | 76 | - (BOOL)isValidReusablePage:(void *)resuablePage 77 | { 78 | if (!resuablePage) return FALSE; 79 | 80 | THVariadicDynamicPage *page = (THVariadicDynamicPage *)resuablePage; 81 | if (page->dataPage.nextAvailableIndex == THNumberOfDataPerVariadicPage) return FALSE; 82 | return YES; 83 | } 84 | 85 | - (void *)templatePageAddress 86 | { 87 | return &th_dynamic_page_var; 88 | } 89 | 90 | - (IMP)replaceAddress:(IMP)functionAddress inPage:(void *)page 91 | { 92 | if (!page) return NULL; 93 | 94 | THVariadicDynamicPage *dynamicPage = (THVariadicDynamicPage *)page; 95 | 96 | int slot = dynamicPage->dataPage.nextAvailableIndex; 97 | dynamicPage->dataPage.dynamicData[slot].originIMP = (IMP)functionAddress; 98 | dynamicPage->dataPage.nextAvailableIndex++; 99 | 100 | return (IMP)&dynamicPage->codePage.jumpInstructions[slot]; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /TrampolineHook/THInterceptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // THInterceptor.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(NSUInteger, THInterceptState) { 14 | THInterceptStateSuccess = 0, 15 | THInterceptStateFailed = 1 16 | }; 17 | 18 | @interface THInterceptorResult : NSObject 19 | - (instancetype)init NS_UNAVAILABLE; 20 | @property (nonatomic, unsafe_unretained, readonly) IMP replacedAddress; 21 | @property (nonatomic, readonly) THInterceptState state; 22 | @end 23 | 24 | 25 | @interface THInterceptor : NSObject 26 | 27 | - (instancetype)init NS_UNAVAILABLE; 28 | - (instancetype)initWithRedirectionFunction:(IMP)redirectFunction; 29 | 30 | @property (nonatomic, unsafe_unretained, readonly) IMP redirectFunction; 31 | 32 | - (THInterceptorResult *)interceptFunction:(IMP)function; 33 | + (Class)pageAllocatorClass; 34 | 35 | @end 36 | 37 | 38 | @interface THVariadicInterceptor : THInterceptor 39 | - (instancetype)init NS_UNAVAILABLE; 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /TrampolineHook/THInterceptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // THInterceptor.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "THInterceptor.h" 10 | #import "THSimplePageAllocator.h" 11 | #import "THVariadicPageAllocator.h" 12 | 13 | #define THInterceptorResultFail \ 14 | [[THInterceptorResult alloc] initWithReplacedAddress:NULL state:THInterceptStateFailed] 15 | 16 | @implementation THInterceptorResult 17 | 18 | - (instancetype)initWithReplacedAddress:(IMP)address state:(THInterceptState)state 19 | { 20 | self = [super init]; 21 | if (self) { 22 | _replacedAddress = address; 23 | _state = state; 24 | } 25 | return self; 26 | } 27 | 28 | @end 29 | 30 | @interface THInterceptor() 31 | @property (nonatomic, strong) id pageAllactor; 32 | @property (nonatomic, unsafe_unretained, readwrite) IMP redirectFunction; 33 | @end 34 | 35 | @implementation THInterceptor 36 | 37 | - (instancetype)initWithRedirectionFunction:(IMP)redirectFunction 38 | { 39 | self = [super init]; 40 | if (self) { 41 | _redirectFunction = redirectFunction; 42 | } 43 | return self; 44 | } 45 | 46 | #pragma mark - Public API 47 | - (THInterceptorResult *)interceptFunction:(IMP)function 48 | { 49 | if (function == NULL) return THInterceptorResultFail; 50 | 51 | IMP jumpAddress = [self.pageAllactor allocateDynamicPageForFunction:function]; 52 | if (!jumpAddress) { 53 | NSAssert(jumpAddress != NULL, @"[THInterceptor]::Allocate dynamic page failed"); 54 | return THInterceptorResultFail; 55 | } 56 | 57 | return [[THInterceptorResult alloc] initWithReplacedAddress:jumpAddress 58 | state:THInterceptStateSuccess]; 59 | } 60 | 61 | #pragma mark - Getter 62 | 63 | - (id)pageAllactor 64 | { 65 | if (!_pageAllactor) { 66 | Class cls = [[self class] pageAllocatorClass]; 67 | _pageAllactor = [[cls alloc] initWithRedirectionFunction:self.redirectFunction]; 68 | } 69 | return _pageAllactor; 70 | } 71 | 72 | + (Class)pageAllocatorClass 73 | { 74 | return [THSimplePageAllocator class]; 75 | } 76 | 77 | @end 78 | 79 | 80 | #pragma mark - THVariadicInterceptor Implementation 81 | @implementation THVariadicInterceptor 82 | 83 | - (instancetype)initWithRedirectionFunction:(IMP)redirectFunction 84 | { 85 | return [super initWithRedirectionFunction:redirectFunction]; 86 | } 87 | 88 | + (Class)pageAllocatorClass 89 | { 90 | return [THVariadicPageAllocator class]; 91 | } 92 | 93 | 94 | @end 95 | 96 | -------------------------------------------------------------------------------- /TrampolineHook/THPageDefinition.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPageLayout.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/18. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #ifndef THPageLayout_h 10 | #define THPageLayout_h 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | #if defined(__arm64__) 17 | #import "THPageDefinition_arm64.h" 18 | #else 19 | #error x86_64 to be supported 20 | #endif 21 | 22 | typedef struct { 23 | IMP originIMP; 24 | } THDynamicData; 25 | 26 | FOUNDATION_EXTERN void *THCreateDynamicePage(void *toMapAddress); 27 | 28 | #endif /* THPageLayout_h */ 29 | -------------------------------------------------------------------------------- /TrampolineHook/THPageDefinition.m: -------------------------------------------------------------------------------- 1 | // 2 | // THPageLayout.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/18. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "THPageDefinition.h" 10 | 11 | void *THCreateDynamicePage(void *toMapAddress) 12 | { 13 | if (!toMapAddress) return NULL; 14 | 15 | vm_address_t fixedPage = (vm_address_t)toMapAddress; 16 | 17 | vm_address_t newDynamicPage = 0; 18 | kern_return_t kernResult = KERN_SUCCESS; 19 | 20 | kernResult = vm_allocate(current_task(), &newDynamicPage, PAGE_SIZE * 2, VM_FLAGS_ANYWHERE); 21 | NSCAssert1(kernResult == KERN_SUCCESS, @"[THDynamicPage]::vm_allocate failed", kernResult); 22 | 23 | vm_address_t newCodePageAddress = newDynamicPage + PAGE_SIZE; 24 | kernResult = vm_deallocate(current_task(), newCodePageAddress, PAGE_SIZE); 25 | NSCAssert1(kernResult == KERN_SUCCESS, @"[THDynamicPage]::vm_deallocate failed", kernResult); 26 | 27 | vm_prot_t currentProtection, maxProtection; 28 | kernResult = vm_remap(current_task(), &newCodePageAddress, PAGE_SIZE, 0, 0, current_task(), fixedPage, FALSE, ¤tProtection, &maxProtection, VM_INHERIT_SHARE); 29 | NSCAssert1(kernResult == KERN_SUCCESS, @"[THDynamicPage]::vm_remap failed", kernResult); 30 | 31 | return (void *)newDynamicPage; 32 | } 33 | -------------------------------------------------------------------------------- /TrampolineHook/arm64/THPageDefinition_arm64.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPageDefinition_arm64.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/4/25. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #ifndef THPageDefinition_arm64_h 10 | #define THPageDefinition_arm64_h 11 | 12 | #import 13 | 14 | typedef int32_t THDynamicPageEntryGroup[2]; 15 | static const int32_t THPageSize = 0x4000; 16 | 17 | 18 | #endif /* THPageDefinition_arm64_h */ 19 | -------------------------------------------------------------------------------- /TrampolineHook/arm64/THPageVar_arm64.s: -------------------------------------------------------------------------------- 1 | #if defined(__arm64__) 2 | 3 | // data page 4 | .text 5 | .align 14 6 | .globl _th_dynamic_page_var 7 | 8 | interceptor: 9 | .quad 0 10 | 11 | pre: 12 | .quad 0 13 | 14 | post: 15 | .quad 0 16 | 17 | // code page 18 | 19 | .align 14 20 | _th_dynamic_page_var: 21 | 22 | _th_entry_var: 23 | 24 | nop 25 | 26 | sub x12, lr, #0x8 27 | sub x12, x12, #0x4000 28 | 29 | ldr x10, [x12] 30 | 31 | ldr x8, pre 32 | blr x8 33 | 34 | ldr x8, interceptor 35 | blr x8 36 | 37 | ldr x8, post 38 | br x8 39 | 40 | .rept 2043 41 | mov x13, lr 42 | bl _th_entry_var; 43 | .endr 44 | 45 | #endif 46 | 47 | 48 | -------------------------------------------------------------------------------- /TrampolineHook/arm64/THPageVariadicContext_arm64.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPageVariadicContext.h 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/17. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #ifndef THPageVariadicContext_arm64_h 10 | #define THPageVariadicContext_arm64_h 11 | 12 | #import 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | // No use. Just for easy understanding of the memory layout 19 | typedef struct _THPageVariadicContext { 20 | int64_t gR[10]; // general registers x0-x8 + x13 21 | int64_t vR[16]; // float registers q0-q7 22 | int64_t linkRegister; // lr 23 | int64_t originIMPRegister; // origin 24 | } THPageVariadicContext; 25 | 26 | void THPageVariadicContextPre(void); 27 | void THPageVariadicContextPost(void); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | 34 | #endif /* THPageVariadicContext_arm64_h */ 35 | -------------------------------------------------------------------------------- /TrampolineHook/arm64/THPageVariadicContext_arm64.m: -------------------------------------------------------------------------------- 1 | // 2 | // THPageVariadicContext.m 3 | // TrampolineHook 4 | // 5 | // Created by z on 2020/5/17. 6 | // Copyright © 2020 SatanWoo. All rights reserved. 7 | // 8 | 9 | #import "THPageVariadicContext_arm64.h" 10 | 11 | #if defined(__arm64__) 12 | 13 | // lr - 跳向 interceptor 的下一跳地址 14 | // x13 - 替换前原调用函数的下一跳地址 15 | 16 | #define saveRegs() \ 17 | __asm volatile ( \ 18 | "stp q0, q1, [sp, #-32]!\n" \ 19 | "stp q2, q3, [sp, #-32]!\n" \ 20 | "stp q4, q5, [sp, #-32]!\n" \ 21 | "stp q6, q7, [sp, #-32]!\n" \ 22 | "stp lr, x10, [sp, #-16]!\n" \ 23 | "stp x0, x1, [sp, #-16]!\n" \ 24 | "stp x2, x3, [sp, #-16]!\n" \ 25 | "stp x4, x5, [sp, #-16]!\n" \ 26 | "stp x6, x7, [sp, #-16]!\n" \ 27 | "stp x8, x13, [sp, #-16]!\n" \ 28 | ) 29 | 30 | #define restoreRegs() \ 31 | __asm volatile( \ 32 | "ldp x8, x13, [sp], #16\n" \ 33 | "ldp x6, x7, [sp], #16\n" \ 34 | "ldp x4, x5, [sp], #16\n" \ 35 | "ldp x2, x3, [sp], #16\n" \ 36 | "ldp x0, x1, [sp], #16\n" \ 37 | "ldp lr, x10, [sp], #16\n" \ 38 | "ldp q6, q7, [sp], #32\n" \ 39 | "ldp q4, q5, [sp], #32\n" \ 40 | "ldp q2, q3, [sp], #32\n" \ 41 | "ldp q0, q1, [sp], #32\n" \ 42 | ) 43 | 44 | __attribute__((__naked__)) 45 | void THPageVariadicContextPre(void) 46 | { 47 | // 先保存,避免调用 malloc 破坏寄存器 48 | saveRegs(); 49 | 50 | // 分配堆上内存 extra 16 byte + sizeof(THPageVariadicContext) 51 | __asm volatile ("mov x0, #0xF0"); 52 | __asm volatile ("bl _malloc"); 53 | 54 | // 返回的分配内存地址保存起来 callee-saved 55 | __asm volatile ("str x19, [x0]"); 56 | __asm volatile ("mov x19, x0"); 57 | 58 | // 恢复堆栈,避免影响变参所处在的堆栈 59 | restoreRegs(); 60 | 61 | // 用堆上空间保存数据 62 | __asm volatile ("stp x0, x1, [x19, #(16 + 0 * 16)]"); 63 | __asm volatile ("stp x2, x3, [x19, #(16 + 1 * 16)]"); 64 | __asm volatile ("stp x4, x5, [x19, #(16 + 2 * 16)]"); 65 | __asm volatile ("stp x6, x7, [x19, #(16 + 3 * 16)]"); 66 | __asm volatile ("stp x8, x13, [x19, #(16 + 4 * 16)]"); 67 | 68 | __asm volatile ("stp q0, q1, [x19, #(16 + 5 * 16 + 0 * 32)]"); 69 | __asm volatile ("stp q2, q3, [x19, #(16 + 5 * 16 + 1 * 32)]"); 70 | __asm volatile ("stp q4, q5, [x19, #(16 + 5 * 16 + 2 * 32)]"); 71 | __asm volatile ("stp q6, q7, [x19, #(16 + 5 * 16 + 3 * 32)]"); 72 | 73 | __asm volatile ("stp lr, x10, [x19, #(16 + 5 * 16 + 4 * 32)]"); 74 | 75 | __asm volatile ("ret"); 76 | } 77 | 78 | __attribute__((__naked__)) 79 | void THPageVariadicContextPost(void) 80 | { 81 | // x19 肯定是正确的地址,使用x19恢复对应的数据 82 | __asm volatile ("ldp lr, x10, [x19, #(16 + 5 * 16 + 4 * 32)]"); 83 | __asm volatile ("ldp q6, q7, [x19, #(16 + 5 * 16 + 3 * 32)]"); 84 | __asm volatile ("ldp q4, q5, [x19, #(16 + 5 * 16 + 2 * 32)]"); 85 | __asm volatile ("ldp q2, q3, [x19, #(16 + 5 * 16 + 1 * 32)]"); 86 | __asm volatile ("ldp q0, q1, [x19, #(16 + 5 * 16 + 0 * 32)]"); 87 | 88 | __asm volatile ("ldp x8, x13, [x19, #(16 + 4 * 16)]"); 89 | __asm volatile ("ldp x6, x7, [x19, #(16 + 3 * 16)]"); 90 | __asm volatile ("ldp x4, x5, [x19, #(16 + 2 * 16)]"); 91 | __asm volatile ("ldp x2, x3, [x19, #(16 + 1 * 16)]"); 92 | __asm volatile ("ldp x0, x1, [x19, #(16 + 0 * 16)]"); 93 | 94 | // 保存一下,避免 free 的影响。 95 | saveRegs(); 96 | 97 | // 恢复原先的 x19, 调用free 98 | __asm volatile ("mov x0, x19"); 99 | __asm volatile ("ldr x19, [x19]"); 100 | __asm volatile ("bl _free"); 101 | 102 | // 恢复堆栈 103 | restoreRegs(); 104 | 105 | __asm volatile ("mov lr, x13"); 106 | __asm volatile ("br x10"); 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /TrampolineHook/arm64/THPage_arm64.s: -------------------------------------------------------------------------------- 1 | #if defined(__arm64__) 2 | 3 | .text 4 | .align 14 5 | .globl _th_dynamic_page 6 | 7 | interceptor: 8 | .quad 0 9 | 10 | .align 14 11 | _th_dynamic_page: 12 | 13 | _th_entry: 14 | 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | 21 | sub x12, lr, #0x8 22 | sub x12, x12, #0x4000 23 | mov lr, x13 24 | 25 | ldr x10, [x12] 26 | 27 | stp q0, q1, [sp, #-32]! 28 | stp q2, q3, [sp, #-32]! 29 | stp q4, q5, [sp, #-32]! 30 | stp q6, q7, [sp, #-32]! 31 | 32 | stp lr, x10, [sp, #-16]! 33 | stp x0, x1, [sp, #-16]! 34 | stp x2, x3, [sp, #-16]! 35 | stp x4, x5, [sp, #-16]! 36 | stp x6, x7, [sp, #-16]! 37 | str x8, [sp, #-16]! 38 | 39 | ldr x8, interceptor 40 | blr x8 41 | 42 | ldr x8, [sp], #16 43 | ldp x6, x7, [sp], #16 44 | ldp x4, x5, [sp], #16 45 | ldp x2, x3, [sp], #16 46 | ldp x0, x1, [sp], #16 47 | ldp lr, x10, [sp], #16 48 | 49 | ldp q6, q7, [sp], #32 50 | ldp q4, q5, [sp], #32 51 | ldp q2, q3, [sp], #32 52 | ldp q0, q1, [sp], #32 53 | 54 | br x10 55 | 56 | .rept 2032 57 | mov x13, lr 58 | bl _th_entry; 59 | .endr 60 | 61 | #endif 62 | 63 | 64 | --------------------------------------------------------------------------------