├── .gitignore ├── ObjCHook.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── ObjCHook ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m ├── hook │ ├── fishhook.c │ ├── fishhook.h │ ├── hook.c │ ├── hook.h │ ├── hook_core.c │ ├── hook_core.h │ ├── hook_core_arm64.c │ ├── hook_core_arm64.h │ ├── hook_core_x64.c │ ├── hook_core_x64.h │ ├── hook_utils.c │ └── hook_utils.h └── main.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/swift,objective-c,oc,c 3 | # Edit at https://www.gitignore.io/?templates=swift,objective-c,oc,c 4 | 5 | ### C ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | 59 | ### Objective-C ### 60 | # Xcode 61 | # 62 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 63 | 64 | ## Build generated 65 | build/ 66 | DerivedData/ 67 | 68 | ## Various settings 69 | *.pbxuser 70 | !default.pbxuser 71 | *.mode1v3 72 | !default.mode1v3 73 | *.mode2v3 74 | !default.mode2v3 75 | *.perspectivev3 76 | !default.perspectivev3 77 | xcuserdata/ 78 | 79 | ## Other 80 | *.moved-aside 81 | *.xccheckout 82 | *.xcscmblueprint 83 | 84 | ## Obj-C/Swift specific 85 | *.hmap 86 | *.ipa 87 | *.dSYM.zip 88 | *.dSYM 89 | 90 | # CocoaPods 91 | # We recommend against adding the Pods directory to your .gitignore. However 92 | # you should judge for yourself, the pros and cons are mentioned at: 93 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 94 | # Pods/ 95 | # Add this line if you want to avoid checking in source code from the Xcode workspace 96 | # *.xcworkspace 97 | 98 | # Carthage 99 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 100 | # Carthage/Checkouts 101 | 102 | Carthage/Build 103 | 104 | # fastlane 105 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 106 | # screenshots whenever they are needed. 107 | # For more information about the recommended setup visit: 108 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 109 | 110 | fastlane/report.xml 111 | fastlane/Preview.html 112 | fastlane/screenshots/**/*.png 113 | fastlane/test_output 114 | 115 | # Code Injection 116 | # After new code Injection tools there's a generated folder /iOSInjectionProject 117 | # https://github.com/johnno1962/injectionforxcode 118 | 119 | iOSInjectionProject/ 120 | 121 | ### Objective-C Patch ### 122 | 123 | #!! ERROR: oc is undefined. Use list command to see defined gitignore types !!# 124 | 125 | ### Swift ### 126 | # Xcode 127 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 128 | 129 | 130 | 131 | 132 | 133 | ## Playgrounds 134 | timeline.xctimeline 135 | playground.xcworkspace 136 | 137 | # Swift Package Manager 138 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 139 | # Packages/ 140 | # Package.pins 141 | # Package.resolved 142 | .build/ 143 | 144 | # CocoaPods 145 | # We recommend against adding the Pods directory to your .gitignore. However 146 | # you should judge for yourself, the pros and cons are mentioned at: 147 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 148 | # Pods/ 149 | # Add this line if you want to avoid checking in source code from the Xcode workspace 150 | # *.xcworkspace 151 | 152 | # Carthage 153 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 154 | # Carthage/Checkouts 155 | 156 | 157 | # fastlane 158 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 159 | # screenshots whenever they are needed. 160 | # For more information about the recommended setup visit: 161 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 162 | 163 | 164 | # Code Injection 165 | # After new code Injection tools there's a generated folder /iOSInjectionProject 166 | # https://github.com/johnno1962/injectionforxcode 167 | 168 | 169 | # End of https://www.gitignore.io/api/swift,objective-c,oc,c 170 | -------------------------------------------------------------------------------- /ObjCHook.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C34079BE2251B41500F22E1A /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = C34079BA2251B41500F22E1A /* fishhook.c */; }; 11 | C34079BF2251B41500F22E1A /* hook.c in Sources */ = {isa = PBXBuildFile; fileRef = C34079BC2251B41500F22E1A /* hook.c */; }; 12 | C34079C22251BB3C00F22E1A /* hook_core_arm64.c in Sources */ = {isa = PBXBuildFile; fileRef = C34079C12251BB3C00F22E1A /* hook_core_arm64.c */; }; 13 | C3A66B36225205490009BE77 /* hook_core_x64.c in Sources */ = {isa = PBXBuildFile; fileRef = C3A66B35225205490009BE77 /* hook_core_x64.c */; }; 14 | C3A66B3922520ABC0009BE77 /* hook_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = C3A66B3822520ABC0009BE77 /* hook_utils.c */; }; 15 | C3A66B3C22520B9C0009BE77 /* hook_core.c in Sources */ = {isa = PBXBuildFile; fileRef = C3A66B3B22520B9C0009BE77 /* hook_core.c */; }; 16 | C3CC7D58224A20D500FF9768 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C3CC7D57224A20D500FF9768 /* AppDelegate.m */; }; 17 | C3CC7D5B224A20D500FF9768 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3CC7D5A224A20D500FF9768 /* ViewController.m */; }; 18 | C3CC7D5E224A20D500FF9768 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C3CC7D5C224A20D500FF9768 /* Main.storyboard */; }; 19 | C3CC7D60224A20DC00FF9768 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C3CC7D5F224A20DC00FF9768 /* Assets.xcassets */; }; 20 | C3CC7D63224A20DC00FF9768 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C3CC7D61224A20DC00FF9768 /* LaunchScreen.storyboard */; }; 21 | C3CC7D66224A20DC00FF9768 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C3CC7D65224A20DC00FF9768 /* main.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | C34079BA2251B41500F22E1A /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fishhook.c; sourceTree = ""; }; 26 | C34079BB2251B41500F22E1A /* fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = ""; }; 27 | C34079BC2251B41500F22E1A /* hook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hook.c; sourceTree = ""; }; 28 | C34079BD2251B41500F22E1A /* hook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hook.h; sourceTree = ""; }; 29 | C34079C02251BB3C00F22E1A /* hook_core_arm64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hook_core_arm64.h; sourceTree = ""; }; 30 | C34079C12251BB3C00F22E1A /* hook_core_arm64.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hook_core_arm64.c; sourceTree = ""; }; 31 | C3A66B34225205490009BE77 /* hook_core_x64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hook_core_x64.h; sourceTree = ""; }; 32 | C3A66B35225205490009BE77 /* hook_core_x64.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hook_core_x64.c; sourceTree = ""; }; 33 | C3A66B3722520ABC0009BE77 /* hook_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hook_utils.h; sourceTree = ""; }; 34 | C3A66B3822520ABC0009BE77 /* hook_utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hook_utils.c; sourceTree = ""; }; 35 | C3A66B3A22520B9C0009BE77 /* hook_core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hook_core.h; sourceTree = ""; }; 36 | C3A66B3B22520B9C0009BE77 /* hook_core.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hook_core.c; sourceTree = ""; }; 37 | C3CC7D53224A20D400FF9768 /* ObjCHook.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ObjCHook.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | C3CC7D56224A20D500FF9768 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | C3CC7D57224A20D500FF9768 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | C3CC7D59224A20D500FF9768 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 41 | C3CC7D5A224A20D500FF9768 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 42 | C3CC7D5D224A20D500FF9768 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | C3CC7D5F224A20DC00FF9768 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | C3CC7D62224A20DC00FF9768 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | C3CC7D64224A20DC00FF9768 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | C3CC7D65224A20DC00FF9768 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | C3CC7D50224A20D400FF9768 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | C34079B92251B41500F22E1A /* hook */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | C34079BA2251B41500F22E1A /* fishhook.c */, 64 | C34079BB2251B41500F22E1A /* fishhook.h */, 65 | C34079BC2251B41500F22E1A /* hook.c */, 66 | C34079BD2251B41500F22E1A /* hook.h */, 67 | C3A66B3A22520B9C0009BE77 /* hook_core.h */, 68 | C3A66B3B22520B9C0009BE77 /* hook_core.c */, 69 | C34079C02251BB3C00F22E1A /* hook_core_arm64.h */, 70 | C34079C12251BB3C00F22E1A /* hook_core_arm64.c */, 71 | C3A66B34225205490009BE77 /* hook_core_x64.h */, 72 | C3A66B35225205490009BE77 /* hook_core_x64.c */, 73 | C3A66B3722520ABC0009BE77 /* hook_utils.h */, 74 | C3A66B3822520ABC0009BE77 /* hook_utils.c */, 75 | ); 76 | path = hook; 77 | sourceTree = ""; 78 | }; 79 | C3CC7D4A224A20D400FF9768 = { 80 | isa = PBXGroup; 81 | children = ( 82 | C3CC7D55224A20D400FF9768 /* ObjCHook */, 83 | C3CC7D54224A20D400FF9768 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | C3CC7D54224A20D400FF9768 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | C3CC7D53224A20D400FF9768 /* ObjCHook.app */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | C3CC7D55224A20D400FF9768 /* ObjCHook */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | C34079B92251B41500F22E1A /* hook */, 99 | C3CC7D56224A20D500FF9768 /* AppDelegate.h */, 100 | C3CC7D57224A20D500FF9768 /* AppDelegate.m */, 101 | C3CC7D59224A20D500FF9768 /* ViewController.h */, 102 | C3CC7D5A224A20D500FF9768 /* ViewController.m */, 103 | C3CC7D5C224A20D500FF9768 /* Main.storyboard */, 104 | C3CC7D5F224A20DC00FF9768 /* Assets.xcassets */, 105 | C3CC7D61224A20DC00FF9768 /* LaunchScreen.storyboard */, 106 | C3CC7D64224A20DC00FF9768 /* Info.plist */, 107 | C3CC7D65224A20DC00FF9768 /* main.m */, 108 | ); 109 | path = ObjCHook; 110 | sourceTree = ""; 111 | }; 112 | /* End PBXGroup section */ 113 | 114 | /* Begin PBXNativeTarget section */ 115 | C3CC7D52224A20D400FF9768 /* ObjCHook */ = { 116 | isa = PBXNativeTarget; 117 | buildConfigurationList = C3CC7D69224A20DC00FF9768 /* Build configuration list for PBXNativeTarget "ObjCHook" */; 118 | buildPhases = ( 119 | C3CC7D4F224A20D400FF9768 /* Sources */, 120 | C3CC7D50224A20D400FF9768 /* Frameworks */, 121 | C3CC7D51224A20D400FF9768 /* Resources */, 122 | ); 123 | buildRules = ( 124 | ); 125 | dependencies = ( 126 | ); 127 | name = ObjCHook; 128 | productName = ObjCHook; 129 | productReference = C3CC7D53224A20D400FF9768 /* ObjCHook.app */; 130 | productType = "com.apple.product-type.application"; 131 | }; 132 | /* End PBXNativeTarget section */ 133 | 134 | /* Begin PBXProject section */ 135 | C3CC7D4B224A20D400FF9768 /* Project object */ = { 136 | isa = PBXProject; 137 | attributes = { 138 | LastUpgradeCheck = 1010; 139 | ORGANIZATIONNAME = legendry; 140 | TargetAttributes = { 141 | C3CC7D52224A20D400FF9768 = { 142 | CreatedOnToolsVersion = 10.1; 143 | }; 144 | }; 145 | }; 146 | buildConfigurationList = C3CC7D4E224A20D400FF9768 /* Build configuration list for PBXProject "ObjCHook" */; 147 | compatibilityVersion = "Xcode 9.3"; 148 | developmentRegion = en; 149 | hasScannedForEncodings = 0; 150 | knownRegions = ( 151 | en, 152 | Base, 153 | ); 154 | mainGroup = C3CC7D4A224A20D400FF9768; 155 | productRefGroup = C3CC7D54224A20D400FF9768 /* Products */; 156 | projectDirPath = ""; 157 | projectRoot = ""; 158 | targets = ( 159 | C3CC7D52224A20D400FF9768 /* ObjCHook */, 160 | ); 161 | }; 162 | /* End PBXProject section */ 163 | 164 | /* Begin PBXResourcesBuildPhase section */ 165 | C3CC7D51224A20D400FF9768 /* Resources */ = { 166 | isa = PBXResourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | C3CC7D63224A20DC00FF9768 /* LaunchScreen.storyboard in Resources */, 170 | C3CC7D60224A20DC00FF9768 /* Assets.xcassets in Resources */, 171 | C3CC7D5E224A20D500FF9768 /* Main.storyboard in Resources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXResourcesBuildPhase section */ 176 | 177 | /* Begin PBXSourcesBuildPhase section */ 178 | C3CC7D4F224A20D400FF9768 /* Sources */ = { 179 | isa = PBXSourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | C34079C22251BB3C00F22E1A /* hook_core_arm64.c in Sources */, 183 | C3A66B3C22520B9C0009BE77 /* hook_core.c in Sources */, 184 | C3CC7D5B224A20D500FF9768 /* ViewController.m in Sources */, 185 | C3CC7D66224A20DC00FF9768 /* main.m in Sources */, 186 | C34079BF2251B41500F22E1A /* hook.c in Sources */, 187 | C3A66B36225205490009BE77 /* hook_core_x64.c in Sources */, 188 | C3CC7D58224A20D500FF9768 /* AppDelegate.m in Sources */, 189 | C3A66B3922520ABC0009BE77 /* hook_utils.c in Sources */, 190 | C34079BE2251B41500F22E1A /* fishhook.c in Sources */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXSourcesBuildPhase section */ 195 | 196 | /* Begin PBXVariantGroup section */ 197 | C3CC7D5C224A20D500FF9768 /* Main.storyboard */ = { 198 | isa = PBXVariantGroup; 199 | children = ( 200 | C3CC7D5D224A20D500FF9768 /* Base */, 201 | ); 202 | name = Main.storyboard; 203 | sourceTree = ""; 204 | }; 205 | C3CC7D61224A20DC00FF9768 /* LaunchScreen.storyboard */ = { 206 | isa = PBXVariantGroup; 207 | children = ( 208 | C3CC7D62224A20DC00FF9768 /* Base */, 209 | ); 210 | name = LaunchScreen.storyboard; 211 | sourceTree = ""; 212 | }; 213 | /* End PBXVariantGroup section */ 214 | 215 | /* Begin XCBuildConfiguration section */ 216 | C3CC7D67224A20DC00FF9768 /* Debug */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | ALWAYS_SEARCH_USER_PATHS = NO; 220 | CLANG_ANALYZER_NONNULL = YES; 221 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_ENABLE_OBJC_WEAK = YES; 227 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 228 | CLANG_WARN_BOOL_CONVERSION = YES; 229 | CLANG_WARN_COMMA = YES; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 233 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 234 | CLANG_WARN_EMPTY_BODY = YES; 235 | CLANG_WARN_ENUM_CONVERSION = YES; 236 | CLANG_WARN_INFINITE_RECURSION = YES; 237 | CLANG_WARN_INT_CONVERSION = YES; 238 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 239 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 240 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 242 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 243 | CLANG_WARN_STRICT_PROTOTYPES = YES; 244 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 245 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 246 | CLANG_WARN_UNREACHABLE_CODE = YES; 247 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 248 | CODE_SIGN_IDENTITY = "iPhone Developer"; 249 | COPY_PHASE_STRIP = NO; 250 | DEBUG_INFORMATION_FORMAT = dwarf; 251 | ENABLE_STRICT_OBJC_MSGSEND = YES; 252 | ENABLE_TESTABILITY = YES; 253 | GCC_C_LANGUAGE_STANDARD = gnu11; 254 | GCC_DYNAMIC_NO_PIC = NO; 255 | GCC_NO_COMMON_BLOCKS = YES; 256 | GCC_OPTIMIZATION_LEVEL = 0; 257 | GCC_PREPROCESSOR_DEFINITIONS = ( 258 | "DEBUG=1", 259 | "$(inherited)", 260 | ); 261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 263 | GCC_WARN_UNDECLARED_SELECTOR = YES; 264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 265 | GCC_WARN_UNUSED_FUNCTION = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 268 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 269 | MTL_FAST_MATH = YES; 270 | ONLY_ACTIVE_ARCH = YES; 271 | SDKROOT = iphoneos; 272 | }; 273 | name = Debug; 274 | }; 275 | C3CC7D68224A20DC00FF9768 /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ALWAYS_SEARCH_USER_PATHS = NO; 279 | CLANG_ANALYZER_NONNULL = YES; 280 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 282 | CLANG_CXX_LIBRARY = "libc++"; 283 | CLANG_ENABLE_MODULES = YES; 284 | CLANG_ENABLE_OBJC_ARC = YES; 285 | CLANG_ENABLE_OBJC_WEAK = YES; 286 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 287 | CLANG_WARN_BOOL_CONVERSION = YES; 288 | CLANG_WARN_COMMA = YES; 289 | CLANG_WARN_CONSTANT_CONVERSION = YES; 290 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 291 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 292 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 293 | CLANG_WARN_EMPTY_BODY = YES; 294 | CLANG_WARN_ENUM_CONVERSION = YES; 295 | CLANG_WARN_INFINITE_RECURSION = YES; 296 | CLANG_WARN_INT_CONVERSION = YES; 297 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 298 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 299 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 300 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 301 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 302 | CLANG_WARN_STRICT_PROTOTYPES = YES; 303 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 304 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 305 | CLANG_WARN_UNREACHABLE_CODE = YES; 306 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 307 | CODE_SIGN_IDENTITY = "iPhone Developer"; 308 | COPY_PHASE_STRIP = NO; 309 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 310 | ENABLE_NS_ASSERTIONS = NO; 311 | ENABLE_STRICT_OBJC_MSGSEND = YES; 312 | GCC_C_LANGUAGE_STANDARD = gnu11; 313 | GCC_NO_COMMON_BLOCKS = YES; 314 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 315 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 316 | GCC_WARN_UNDECLARED_SELECTOR = YES; 317 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 318 | GCC_WARN_UNUSED_FUNCTION = YES; 319 | GCC_WARN_UNUSED_VARIABLE = YES; 320 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 321 | MTL_ENABLE_DEBUG_INFO = NO; 322 | MTL_FAST_MATH = YES; 323 | SDKROOT = iphoneos; 324 | VALIDATE_PRODUCT = YES; 325 | }; 326 | name = Release; 327 | }; 328 | C3CC7D6A224A20DC00FF9768 /* Debug */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 332 | CODE_SIGN_IDENTITY = "iPhone Developer"; 333 | CODE_SIGN_STYLE = Manual; 334 | DEVELOPMENT_TEAM = ""; 335 | INFOPLIST_FILE = ObjCHook/Info.plist; 336 | LD_RUNPATH_SEARCH_PATHS = ( 337 | "$(inherited)", 338 | "@executable_path/Frameworks", 339 | ); 340 | PRODUCT_BUNDLE_IDENTIFIER = com.jd.ObjCHook; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | PROVISIONING_PROFILE_SPECIFIER = ""; 343 | TARGETED_DEVICE_FAMILY = "1,2"; 344 | }; 345 | name = Debug; 346 | }; 347 | C3CC7D6B224A20DC00FF9768 /* Release */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 351 | CODE_SIGN_IDENTITY = "iPhone Developer"; 352 | CODE_SIGN_STYLE = Manual; 353 | DEVELOPMENT_TEAM = ""; 354 | INFOPLIST_FILE = ObjCHook/Info.plist; 355 | LD_RUNPATH_SEARCH_PATHS = ( 356 | "$(inherited)", 357 | "@executable_path/Frameworks", 358 | ); 359 | PRODUCT_BUNDLE_IDENTIFIER = com.jd.ObjCHook; 360 | PRODUCT_NAME = "$(TARGET_NAME)"; 361 | PROVISIONING_PROFILE_SPECIFIER = ""; 362 | TARGETED_DEVICE_FAMILY = "1,2"; 363 | }; 364 | name = Release; 365 | }; 366 | /* End XCBuildConfiguration section */ 367 | 368 | /* Begin XCConfigurationList section */ 369 | C3CC7D4E224A20D400FF9768 /* Build configuration list for PBXProject "ObjCHook" */ = { 370 | isa = XCConfigurationList; 371 | buildConfigurations = ( 372 | C3CC7D67224A20DC00FF9768 /* Debug */, 373 | C3CC7D68224A20DC00FF9768 /* Release */, 374 | ); 375 | defaultConfigurationIsVisible = 0; 376 | defaultConfigurationName = Release; 377 | }; 378 | C3CC7D69224A20DC00FF9768 /* Build configuration list for PBXNativeTarget "ObjCHook" */ = { 379 | isa = XCConfigurationList; 380 | buildConfigurations = ( 381 | C3CC7D6A224A20DC00FF9768 /* Debug */, 382 | C3CC7D6B224A20DC00FF9768 /* Release */, 383 | ); 384 | defaultConfigurationIsVisible = 0; 385 | defaultConfigurationName = Release; 386 | }; 387 | /* End XCConfigurationList section */ 388 | }; 389 | rootObject = C3CC7D4B224A20D400FF9768 /* Project object */; 390 | } 391 | -------------------------------------------------------------------------------- /ObjCHook.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ObjCHook/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/26. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ObjCHook/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/26. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ObjCHook/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /ObjCHook/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ObjCHook/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 | -------------------------------------------------------------------------------- /ObjCHook/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 | -------------------------------------------------------------------------------- /ObjCHook/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ObjCHook/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/26. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ObjCHook/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/26. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | NSLog(@"............"); 20 | [self test]; 21 | } 22 | - (void)test { 23 | // [NSThread sleepForTimeInterval:1]; 24 | NSLog(@"test"); 25 | [self foo]; 26 | // for (int i = 0 ; i < 800000; i ++) { 27 | // NSString *s = @"111"; 28 | // int _st = s.integerValue; 29 | // } 30 | 31 | } 32 | - (void)foo { 33 | for (int i = 0 ; i < 1000000; i ++) { 34 | NSString *s = @"111"; 35 | int _st = s.integerValue; 36 | } 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ObjCHook/hook/fishhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #include "fishhook.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef __LP64__ 40 | typedef struct mach_header_64 mach_header_t; 41 | typedef struct segment_command_64 segment_command_t; 42 | typedef struct section_64 section_t; 43 | typedef struct nlist_64 nlist_t; 44 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 45 | #else 46 | typedef struct mach_header mach_header_t; 47 | typedef struct segment_command segment_command_t; 48 | typedef struct section section_t; 49 | typedef struct nlist nlist_t; 50 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 51 | #endif 52 | 53 | #ifndef SEG_DATA_CONST 54 | #define SEG_DATA_CONST "__DATA_CONST" 55 | #endif 56 | 57 | struct rebindings_entry { 58 | struct rebinding *rebindings; 59 | size_t rebindings_nel; 60 | struct rebindings_entry *next; 61 | }; 62 | 63 | static struct rebindings_entry *_rebindings_head; 64 | 65 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 66 | struct rebinding rebindings[], 67 | size_t nel) { 68 | struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); 69 | if (!new_entry) { 70 | return -1; 71 | } 72 | new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); 73 | if (!new_entry->rebindings) { 74 | free(new_entry); 75 | return -1; 76 | } 77 | memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); 78 | new_entry->rebindings_nel = nel; 79 | new_entry->next = *rebindings_head; 80 | *rebindings_head = new_entry; 81 | return 0; 82 | } 83 | 84 | #if 0 85 | static int get_protection(void *addr, vm_prot_t *prot, vm_prot_t *max_prot) { 86 | mach_port_t task = mach_task_self(); 87 | vm_size_t size = 0; 88 | vm_address_t address = (vm_address_t)addr; 89 | memory_object_name_t object; 90 | #ifdef __LP64__ 91 | mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; 92 | vm_region_basic_info_data_64_t info; 93 | kern_return_t info_ret = vm_region_64( 94 | task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object); 95 | #else 96 | mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT; 97 | vm_region_basic_info_data_t info; 98 | kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object); 99 | #endif 100 | if (info_ret == KERN_SUCCESS) { 101 | if (prot != NULL) 102 | *prot = info.protection; 103 | 104 | if (max_prot != NULL) 105 | *max_prot = info.max_protection; 106 | 107 | return 0; 108 | } 109 | 110 | return -1; 111 | } 112 | #endif 113 | 114 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 115 | section_t *section, 116 | intptr_t slide, 117 | nlist_t *symtab, 118 | char *strtab, 119 | uint32_t *indirect_symtab) { 120 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 121 | void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); 122 | 123 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 124 | uint32_t symtab_index = indirect_symbol_indices[i]; 125 | if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || 126 | symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 127 | continue; 128 | } 129 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 130 | char *symbol_name = strtab + strtab_offset; 131 | bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; 132 | struct rebindings_entry *cur = rebindings; 133 | while (cur) { 134 | for (uint j = 0; j < cur->rebindings_nel; j++) { 135 | if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { 136 | kern_return_t err; 137 | 138 | if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement) 139 | *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; 140 | 141 | /** 142 | * 1. Moved the vm protection modifying codes to here to reduce the 143 | * changing scope. 144 | * 2. Adding VM_PROT_WRITE mode unconditionally because vm_region 145 | * API on some iOS/Mac reports mismatch vm protection attributes. 146 | * -- Lianfu Hao Jun 16th, 2021 147 | **/ 148 | err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); 149 | if (err == KERN_SUCCESS) { 150 | /** 151 | * Once we failed to change the vm protection, we 152 | * MUST NOT continue the following write actions! 153 | * iOS 15 has corrected the const segments prot. 154 | * -- Lionfore Hao Jun 11th, 2021 155 | **/ 156 | indirect_symbol_bindings[i] = cur->rebindings[j].replacement; 157 | } 158 | goto symbol_loop; 159 | } 160 | } 161 | cur = cur->next; 162 | } 163 | symbol_loop:; 164 | } 165 | } 166 | 167 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 168 | const struct mach_header *header, 169 | intptr_t slide) { 170 | Dl_info info; 171 | if (dladdr(header, &info) == 0) { 172 | return; 173 | } 174 | 175 | segment_command_t *cur_seg_cmd; 176 | segment_command_t *linkedit_segment = NULL; 177 | struct symtab_command* symtab_cmd = NULL; 178 | struct dysymtab_command* dysymtab_cmd = NULL; 179 | 180 | uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); 181 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 182 | cur_seg_cmd = (segment_command_t *)cur; 183 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 184 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 185 | linkedit_segment = cur_seg_cmd; 186 | } 187 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 188 | symtab_cmd = (struct symtab_command*)cur_seg_cmd; 189 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 190 | dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; 191 | } 192 | } 193 | 194 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || 195 | !dysymtab_cmd->nindirectsyms) { 196 | return; 197 | } 198 | 199 | // Find base symbol/string table addresses 200 | uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; 201 | nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); 202 | char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); 203 | 204 | // Get indirect symbol table (array of uint32_t indices into symbol table) 205 | uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); 206 | 207 | cur = (uintptr_t)header + sizeof(mach_header_t); 208 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 209 | cur_seg_cmd = (segment_command_t *)cur; 210 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 211 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && 212 | strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 213 | continue; 214 | } 215 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 216 | section_t *sect = 217 | (section_t *)(cur + sizeof(segment_command_t)) + j; 218 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 219 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 220 | } 221 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 222 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | static void _rebind_symbols_for_image(const struct mach_header *header, 230 | intptr_t slide) { 231 | rebind_symbols_for_image(_rebindings_head, header, slide); 232 | } 233 | 234 | int rebind_symbols_image(void *header, 235 | intptr_t slide, 236 | struct rebinding rebindings[], 237 | size_t rebindings_nel) { 238 | struct rebindings_entry *rebindings_head = NULL; 239 | int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); 240 | rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); 241 | if (rebindings_head) { 242 | free(rebindings_head->rebindings); 243 | } 244 | free(rebindings_head); 245 | return retval; 246 | } 247 | 248 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { 249 | int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); 250 | if (retval < 0) { 251 | return retval; 252 | } 253 | // If this was the first call, register callback for image additions (which is also invoked for 254 | // existing images, otherwise, just run on existing images 255 | if (!_rebindings_head->next) { 256 | _dyld_register_func_for_add_image(_rebind_symbols_for_image); 257 | } else { 258 | uint32_t c = _dyld_image_count(); 259 | for (uint32_t i = 0; i < c; i++) { 260 | _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); 261 | } 262 | } 263 | return retval; 264 | } 265 | -------------------------------------------------------------------------------- /ObjCHook/hook/fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef fishhook_h 25 | #define fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #if !defined(FISHHOOK_EXPORT) 31 | #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) 32 | #else 33 | #define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif //__cplusplus 39 | 40 | /* 41 | * A structure representing a particular intended rebinding from a symbol 42 | * name to its replacement 43 | */ 44 | struct rebinding { 45 | const char *name; 46 | void *replacement; 47 | void **replaced; 48 | }; 49 | 50 | /* 51 | * For each rebinding in rebindings, rebinds references to external, indirect 52 | * symbols with the specified name to instead point at replacement for each 53 | * image in the calling process as well as for all future images that are loaded 54 | * by the process. If rebind_functions is called more than once, the symbols to 55 | * rebind are added to the existing list of rebindings, and if a given symbol 56 | * is rebound more than once, the later rebinding will take precedence. 57 | */ 58 | FISHHOOK_VISIBILITY 59 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); 60 | 61 | /* 62 | * Rebinds as above, but only in the specified image. The header should point 63 | * to the mach-o header, the slide should be the slide offset. Others as above. 64 | */ 65 | FISHHOOK_VISIBILITY 66 | int rebind_symbols_image(void *header, 67 | intptr_t slide, 68 | struct rebinding rebindings[], 69 | size_t rebindings_nel); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif //__cplusplus 74 | 75 | #endif //fishhook_h 76 | 77 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook.c: -------------------------------------------------------------------------------- 1 | // 2 | // hook_.c 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/28. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #include "hook.h" 10 | #if __aarch64__ 11 | #include "hook_core_arm64.h" 12 | #else 13 | #include "hook_core_x64.h" 14 | #endif 15 | 16 | /// 开始hook 17 | void start_objc_msgSend_hook(void) { 18 | #if __aarch64__ 19 | arm64_start_objc_msgSend_hook(); 20 | #else 21 | x64_start_objc_msgSend_hook(); 22 | #endif 23 | } 24 | /// 停止 25 | void stop_objc_msgSend_hook(void) { 26 | #if __aarch64__ 27 | arm64_stop_objc_msgSend_hook(); 28 | #else 29 | x64_stop_objc_msgSend_hook(); 30 | #endif 31 | } 32 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook.h: -------------------------------------------------------------------------------- 1 | // 2 | // hook_.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/28. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #ifndef hook__h 10 | #define hook__h 11 | 12 | #include 13 | 14 | /// 开始hook 15 | void start_objc_msgSend_hook(void); 16 | /// 停止 17 | void stop_objc_msgSend_hook(void); 18 | 19 | #endif /* hook__h */ 20 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_core.c: -------------------------------------------------------------------------------- 1 | // 2 | // hook_core.c 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #include "hook_core.h" 10 | #include "hook_utils.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "fishhook.h" 22 | 23 | static pthread_key_t _thread_key; 24 | 25 | typedef struct { 26 | uintptr_t lr; 27 | SEL _cmd; 28 | /// 开始时间 29 | uint64_t start_time; 30 | /// 结束时间 31 | uint64_t end_time; 32 | /// 类名 33 | char *class_name; 34 | /// 函数名 35 | char *method_name; 36 | /// 调用深度 37 | uint8_t dept; 38 | /// 是否是类方法 39 | bool is_class_method; 40 | }call_stack; 41 | 42 | typedef struct { 43 | /// 调用栈 44 | call_stack *cs; 45 | /// 调用栈第一个: 每一个objc_msgSend开始 46 | call_stack *first; 47 | uint index; 48 | /// 当前数据总大小 49 | uint size; 50 | /// 总开销 51 | uint64_t cost; 52 | /// 是否是主线程 53 | bool is_main_thread; 54 | // 调用栈信息 55 | char *stack_info; 56 | uint64_t stack_info_size; 57 | }common_data; 58 | 59 | ///获取线程共享数据 60 | common_data *get_thread_call_stack() { 61 | /// pthread_getspecific 获取指定key的同一线程中不同方法的共享数据 62 | common_data *_cd = (common_data *)pthread_getspecific(_thread_key); 63 | if (_cd == NULL) { 64 | _cd = (common_data *)malloc(sizeof(common_data)); 65 | _cd->size = 10; 66 | /// 调用栈 67 | _cd->cs = (call_stack *)malloc(sizeof(call_stack) * _cd->size); 68 | /// 当前调用栈索引 69 | _cd->index = 0; 70 | _cd->cost = 0; 71 | /// 防止野指针的出现 72 | /// https://github.com/czqasngit/objc_msgSend_hook/issues/1 73 | _cd->stack_info = NULL; 74 | /// 存放共享数据 75 | pthread_setspecific(_thread_key, (void *)_cd); 76 | } 77 | _cd->is_main_thread = pthread_main_np(); 78 | return _cd; 79 | } 80 | /// 如果调用栈大小不够,重新分配空间 81 | void realloc_call_stack_memery_ifneed() { 82 | common_data *_cd = get_thread_call_stack(); 83 | /// 重新分配空间 84 | if (_cd->index >= _cd->size) { 85 | _cd->size += 10; 86 | _cd->cs = (call_stack *)realloc(_cd->cs, sizeof(call_stack) * _cd->size); 87 | } 88 | } 89 | /// 打印调用信息 90 | char *dump_call_stack(call_stack *_cs) { 91 | uint64_t cost = _cs->end_time - _cs->start_time; 92 | if (cost > 100 && !hook_has_prefix(_cs->class_name, "OS_")) { 93 | int _count = _cs->dept; 94 | while (_count > 0) { 95 | printf(" "); 96 | _count --; 97 | } 98 | char *space = (char *)malloc(_cs->dept); 99 | memset(space, ' ', _cs->dept); 100 | char *str = (char *)malloc(1024); 101 | sprintf(str, "%s %c [%s %s] %lld ms\n", space, '-', _cs->class_name, _cs->method_name, cost); 102 | free(space); 103 | return str; 104 | } 105 | return NULL; 106 | } 107 | /// 打印一个objc_msgSend完整调用信息 108 | void dump_method() { 109 | common_data *_cd = get_thread_call_stack(); 110 | _cd->cost = _cd->first->end_time - _cd->first->start_time; 111 | if(_cd->cost > 400 && _cd->first && !hook_has_prefix(_cd->first->class_name, "OS_")) { 112 | printf("[%s][%s: %s]: %lld ms\n", _cd->is_main_thread ? "主" : "子", _cd->first->class_name, _cd->first->method_name, _cd->cost); 113 | printf("%s \n", _cd->stack_info); 114 | } 115 | 116 | } 117 | /// hook 之前 118 | void before_objc_msgSend(id object, SEL _cmd, uintptr_t lr) { 119 | realloc_call_stack_memery_ifneed(); 120 | common_data *_cd = get_thread_call_stack(); 121 | /// 保存当前调用栈下一个函数方法执行地址 122 | call_stack *_cs = &(_cd->cs[_cd->index]); 123 | /// 正常执行程序的下一条指令,保存起来在hook完之后需要恢复到hook前的状态,程序lr寄存器能正常执行 124 | _cs->lr = lr; 125 | /// 保存执行的SEL 126 | _cs->_cmd = _cmd; 127 | /// 保存执行前的时间 128 | _cs->start_time = hook_getMillisecond(); 129 | /// 保存当前调用栈的深度 130 | _cs->dept = _cd->index; 131 | /// 保存当前对象的类名 132 | _cs->class_name = (char *)hook_getObjectClassName(object); 133 | /// 保存当前调用的方法名 134 | _cs->method_name = (char *)_cs->_cmd; 135 | /// 判断是否是元类的实例方法,即类的类方法 136 | // Class __cls = object_getClass(object); 137 | // _cs->is_class_method = class_isMetaClass(__cls); 138 | /// index为0表示调用栈顶,即objc_msgSend初始调用(objc_msgSend内部还会调用其它objc_msgSend) 139 | if(_cd->index == 0) { 140 | _cd->first = _cs; 141 | _cd->stack_info_size = 1024; 142 | if(_cd->stack_info) free(_cd->stack_info); 143 | _cd->stack_info = (char *)malloc(1024); 144 | } 145 | /// 入栈 146 | /// 调用栈前进 147 | _cd->index ++; 148 | } 149 | /// hook 之后 150 | uintptr_t after_objc_msgSend() { 151 | common_data *_cd = (common_data *)pthread_getspecific(_thread_key); 152 | /// 后退 153 | _cd->index --; 154 | /// 获取即将完成的调用 155 | call_stack *stack = &(_cd->cs[_cd->index]); 156 | stack->end_time = hook_getMillisecond(); 157 | // _cd->cost += (stack->end_time - stack->start_time); 158 | /// 打印单个方法执行信息 159 | if(_cd->index > 0) { 160 | char *_stack_info = dump_call_stack(stack); 161 | if(_stack_info) { 162 | uint64_t _stack_info_count = strlen(_stack_info); 163 | uint64_t cur_stack_info_size = strlen(_cd->stack_info); 164 | // 判断是否超出当前容量 165 | if(_stack_info_count + cur_stack_info_size > _cd->stack_info_size) { 166 | //扩容 167 | _cd->stack_info_size = _stack_info_count + cur_stack_info_size + 1024; 168 | _cd->stack_info = (char *)realloc(_cd->stack_info, _cd->stack_info_size); 169 | } 170 | memcpy(_cd->stack_info + cur_stack_info_size, _stack_info, _stack_info_count); 171 | free(_stack_info); 172 | } 173 | } 174 | /// 一个函数调用完成 175 | /// 打印所有方法执行信息 176 | if (_cd->index == 0){ 177 | dump_method(); 178 | _cd->stack_info_size = 1024; 179 | if(_cd->stack_info) free(_cd->stack_info); 180 | _cd->stack_info = (char *)malloc(_cd->stack_info_size); 181 | } 182 | /// 将下一条函数指令返回,并存放到寄存器x0 183 | return stack->lr; 184 | } 185 | /// 释放与线程共享数据的相关资源 186 | void release_thread_stack() { 187 | printf("release \n"); 188 | common_data *_cd = (common_data *)pthread_getspecific(_thread_key); 189 | if(!_cd) return; 190 | if (_cd->cs) free(_cd->cs); 191 | if (_cd->stack_info) free(_cd->stack_info); 192 | free(_cd); 193 | } 194 | 195 | void start_hook(void *hook_objc_msgSend, void **origin_objc_msgSend) { 196 | printf("hook_start\n"); 197 | static dispatch_once_t onceToken; 198 | dispatch_once(&onceToken, ^{ 199 | pthread_key_create(&_thread_key, &release_thread_stack); 200 | /// 替换系统的函数: 因为apple使用了系统函数动态绑定技术,所以可以hook系统函数,本地函数编译时地址已固定 201 | /// rebinding: 第一个参数,需要hook的系统函数名称 202 | /// 第二个参数,替换系统的函数 203 | /// 第三个参数,指向被hook函数,调用orig_objc_msgSend相当于调用原始函数 204 | rebind_symbols((struct rebinding[1]){ "objc_msgSend", hook_objc_msgSend, origin_objc_msgSend}, 1); 205 | }); 206 | } 207 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_core.h: -------------------------------------------------------------------------------- 1 | // 2 | // hook_core.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #ifndef hook_core_h 10 | #define hook_core_h 11 | #include 12 | #include 13 | ///hook_core 实现了hook的方法统计,具体的hook在不同的CPU下面完成 14 | ///intel x64在llvm 2.0之后不再支持嵌入式asm代码 15 | /// 后 16 | uintptr_t after_objc_msgSend(void); 17 | /// 前 18 | void before_objc_msgSend(id SELF, SEL _cmd, uintptr_t lr); 19 | /// 开始hook 20 | void start_hook(void *hook_objc_msgSend, void **origin_objc_msgSend); 21 | #endif /* hook_core_h */ 22 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_core_arm64.c: -------------------------------------------------------------------------------- 1 | // 2 | // hook_core_arm64.c 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #include "hook_core_arm64.h" 10 | #import "fishhook.h" 11 | #import 12 | #import 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "hook_utils.h" 23 | #include "hook_core.h" 24 | 25 | #if __aarch64__ 26 | static id (*orig_objc_msgSend)(void); 27 | // 将参数value(地址)传递给x12寄存器 28 | // blr开始执行 29 | #define call(b, value) \ 30 | __asm volatile ("mov x12, %0\n" :: "r"(value)); \ 31 | __asm volatile (#b " x12\n"); 32 | 33 | /// 依次将寄存器数据入栈 34 | #define save() \ 35 | __asm volatile ( \ 36 | "stp x8, x9, [sp, #-16]!\n" \ 37 | "stp x6, x7, [sp, #-16]!\n" \ 38 | "stp x4, x5, [sp, #-16]!\n" \ 39 | "stp x2, x3, [sp, #-16]!\n" \ 40 | "stp x0, x1, [sp, #-16]!\n" \ 41 | ); 42 | 43 | /// 依次将寄存器数据出栈 44 | #define load() \ 45 | __asm volatile ( \ 46 | "ldp x0, x1, [sp], #16\n" \ 47 | "ldp x2, x3, [sp], #16\n" \ 48 | "ldp x4, x5, [sp], #16\n" \ 49 | "ldp x6, x7, [sp], #16\n" \ 50 | "ldp x8, x9, [sp], #16\n" ); 51 | 52 | /// 程序执行完成,返回将继续执行lr中的函数 53 | #define ret() __asm volatile ("ret\n"); 54 | 55 | static void hook_objc_msgSend() { 56 | /// before之前保存objc_msgSend的参数 57 | save() 58 | /// 将objc_msgSend执行的下一个函数地址传递给before_objc_msgSend的第二个参数x0 self, x1 _cmd, x2: lr address 59 | __asm volatile ("mov x2, lr\n"); 60 | /// 执行before_objc_msgSend 61 | call(blr, &before_objc_msgSend) 62 | /// 恢复objc_msgSend参数,并执行 63 | load() 64 | call(blr, orig_objc_msgSend) 65 | /// after之前保存objc_msgSend执行完成的参数 66 | save() 67 | /// 调用 after_objc_msgSend 68 | call(blr, &after_objc_msgSend) 69 | /// 将after_objc_msgSend返回的参数放入lr,恢复调用before_objc_msgSend前的lr地址 70 | __asm volatile ("mov lr, x0\n"); 71 | /// 恢复objc_msgSend执行完成的参数 72 | load() 73 | /// 方法结束,继续执行lr 74 | ret() 75 | } 76 | 77 | ///开始 78 | void arm64_start_objc_msgSend_hook() { 79 | start_hook(hook_objc_msgSend, (void *)(&orig_objc_msgSend)); 80 | } 81 | void arm64_stop_objc_msgSend_hook() { 82 | 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_core_arm64.h: -------------------------------------------------------------------------------- 1 | // 2 | // hook_core_arm64.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #ifndef hook_core_arm64_h 10 | #define hook_core_arm64_h 11 | 12 | #include 13 | /// arm64 14 | void arm64_start_objc_msgSend_hook(void); 15 | void arm64_stop_objc_msgSend_hook(void); 16 | 17 | #endif /* hook_core_arm64_h */ 18 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_core_x64.c: -------------------------------------------------------------------------------- 1 | // 2 | // hook_core_x64.c 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #include "hook_core_x64.h" 10 | 11 | 12 | void x64_start_objc_msgSend_hook() { 13 | printf("not support intel x64 \n"); 14 | } 15 | void x64_stop_objc_msgSend_hook() { 16 | printf("not support intel x64 \n"); 17 | } 18 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_core_x64.h: -------------------------------------------------------------------------------- 1 | // 2 | // hook_core_x64.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #ifndef hook_core_x64_h 10 | #define hook_core_x64_h 11 | 12 | #include 13 | /// intel x64 hook 14 | void x64_start_objc_msgSend_hook(void); 15 | void x64_stop_objc_msgSend_hook(void); 16 | #endif /* hook_core_x64_h */ 17 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_utils.c: -------------------------------------------------------------------------------- 1 | // 2 | // hook_utils.c 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #include "hook_utils.h" 10 | #include 11 | #include 12 | #include 13 | 14 | const char *hook_getObjectClassName(void *_target) { 15 | return class_getName(object_getClass(_target)); 16 | } 17 | const char *hook_getMethodName(Class cls, SEL _cmd) { 18 | Method m = class_getClassMethod(cls, _cmd); 19 | struct objc_method_description *desc = method_getDescription(m); 20 | return ""; 21 | } 22 | /// 获取当前系统时间 23 | uint64_t hook_getMillisecond() { 24 | struct timeval tv; 25 | gettimeofday(&tv,NULL); 26 | return tv.tv_usec / 1000 + tv.tv_sec * 1000; 27 | } 28 | 29 | bool hook_has_prefix(char *target, char *prefix) { 30 | uint64_t count = strlen(prefix); 31 | bool ret = true; 32 | if(strlen(target) < count) 33 | return false; 34 | for (int i = 0; i < count; i ++) { 35 | if (target[i] != prefix[i]) { 36 | ret = false; 37 | break; 38 | } 39 | } 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /ObjCHook/hook/hook_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // hook_utils.h 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/4/1. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #ifndef hook_utils_h 10 | #define hook_utils_h 11 | 12 | #include 13 | #include 14 | 15 | /// 返回对象的类名 16 | /// _target: NSObject 对象 17 | const char *hook_getObjectClassName(void *_target); 18 | /// 获取类名 19 | const char *hook_getMethodName(Class cls, SEL _cmd); 20 | /// 获取当前系统时间 21 | uint64_t hook_getMillisecond(void); 22 | /// 判断target是否包含有prefix字符串 23 | bool hook_has_prefix(char *target, char *prefix) ; 24 | #endif /* hook_utils_h */ 25 | -------------------------------------------------------------------------------- /ObjCHook/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ObjCHook 4 | // 5 | // Created by legendry on 2019/3/26. 6 | // Copyright © 2019 legendry. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | #import "hook.h" 12 | #import 13 | 14 | int main(int argc, char * argv[]) { 15 | start_objc_msgSend_hook(); 16 | @autoreleasepool { 17 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 精简详解,完整核心功能 2 | 3 | ![](https://ws3.sinaimg.cn/large/006tNc79gy1g2j825epgoj31jk0dw4qp.jpg) 4 | 5 | ## 1.汇编入门: 6 | 10分钟入门 7 | - https://blackteachinese.github.io/2017/07/12/arm64/ 8 | iOS开发汇编入门 9 | - https://blog.cnbluebox.com/blog/2017/07/24/arm64-start/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io 10 | 小例子入门 11 | - https://juejin.im/post/5aabcae1f265da238d507a68 12 | 理解 iOS ARM 13 | - https://www.jianshu.com/p/544464a5e630?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation 14 | 15 | ## 2.Mach-O 理解: 16 | Mach-O文件结构 17 | - https://hawk0620.github.io/blog/2018/03/22/study-mach-o-file/ 18 | 探秘Mach-O 19 | - https://www.jianshu.com/p/1f22d1e667e3 20 | 21 | ## 3. fishhook 22 | - https://www.jianshu.com/p/4d86de908721 23 | 24 | ## 4. objc_msgSend hook 完整精简代码(详细的注释) 25 | 26 | ### `hook_core_arm64.c`实现了对 arm64架构的objc_msgSend hook 27 | 28 | ``` 29 | 30 | // 31 | // hook_core_arm64.c 32 | // ObjCHook 33 | // 34 | // Created by legendry on 2019/4/1. 35 | // Copyright © 2019 legendry. All rights reserved. 36 | // 37 | 38 | #include "hook_core_arm64.h" 39 | #import "fishhook.h" 40 | #import 41 | #import 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include "hook_utils.h" 52 | #include "hook_core.h" 53 | 54 | #if __aarch64__ 55 | static id (*orig_objc_msgSend)(void); 56 | // 将参数value(地址)传递给x12寄存器 57 | // blr开始执行 58 | #define call(b, value) \ 59 | __asm volatile ("mov x12, %0\n" :: "r"(value)); \ 60 | __asm volatile (#b " x12\n"); 61 | 62 | /// 依次将寄存器数据入栈 63 | #define save() \ 64 | __asm volatile ( \ 65 | "stp x8, x9, [sp, #-16]!\n" \ 66 | "stp x6, x7, [sp, #-16]!\n" \ 67 | "stp x4, x5, [sp, #-16]!\n" \ 68 | "stp x2, x3, [sp, #-16]!\n" \ 69 | "stp x0, x1, [sp, #-16]!\n" \ 70 | ); 71 | 72 | /// 依次将寄存器数据出栈 73 | #define load() \ 74 | __asm volatile ( \ 75 | "ldp x0, x1, [sp], #16\n" \ 76 | "ldp x2, x3, [sp], #16\n" \ 77 | "ldp x4, x5, [sp], #16\n" \ 78 | "ldp x6, x7, [sp], #16\n" \ 79 | "ldp x8, x9, [sp], #16\n" ); 80 | 81 | /// 程序执行完成,返回将继续执行lr中的函数 82 | #define ret() __asm volatile ("ret\n"); 83 | 84 | static void hook_objc_msgSend() { 85 | /// before之前保存objc_msgSend的参数 86 | save() 87 | /// 将objc_msgSend执行的下一个函数地址传递给before_objc_msgSend的第二个参数x0 self, x1 _cmd, x2: lr address 88 | __asm volatile ("mov x2, lr\n"); 89 | /// 执行before_objc_msgSend 90 | call(blr, &before_objc_msgSend) 91 | /// 恢复objc_msgSend参数,并执行 92 | load() 93 | call(blr, orig_objc_msgSend) 94 | /// after之前保存objc_msgSend执行完成的参数 95 | save() 96 | /// 调用 after_objc_msgSend 97 | call(blr, &after_objc_msgSend) 98 | /// 将after_objc_msgSend返回的参数放入lr,恢复调用before_objc_msgSend前的lr地址 99 | __asm volatile ("mov lr, x0\n"); 100 | /// 恢复objc_msgSend执行完成的参数 101 | load() 102 | /// 方法结束,继续执行lr 103 | ret() 104 | } 105 | 106 | ///开始 107 | void arm64_start_objc_msgSend_hook() { 108 | start_hook(hook_objc_msgSend, (void *)(&orig_objc_msgSend)); 109 | } 110 | void arm64_stop_objc_msgSend_hook() { 111 | 112 | } 113 | #endif 114 | 115 | ``` 116 | 117 | ### `hook_core.c` 实现了对调用堆栈的统计与打印 118 | 119 | ``` 120 | // 121 | // hook_core.c 122 | // ObjCHook 123 | // 124 | // Created by legendry on 2019/4/1. 125 | // Copyright © 2019 legendry. All rights reserved. 126 | // 127 | 128 | #include "hook_core.h" 129 | #include "hook_utils.h" 130 | #include 131 | #include 132 | #include 133 | #include 134 | #include 135 | #include 136 | #include 137 | #include 138 | #include 139 | #include 140 | #include "fishhook.h" 141 | 142 | static pthread_key_t _thread_key; 143 | 144 | typedef struct { 145 | uintptr_t lr; 146 | SEL _cmd; 147 | /// 开始时间 148 | uint64_t start_time; 149 | /// 结束时间 150 | uint64_t end_time; 151 | /// 类名 152 | char *class_name; 153 | /// 函数名 154 | char *method_name; 155 | /// 调用深度 156 | uint8_t dept; 157 | /// 是否是类方法 158 | bool is_class_method; 159 | }call_stack; 160 | 161 | typedef struct { 162 | /// 调用栈 163 | call_stack *cs; 164 | /// 调用栈第一个: 每一个objc_msgSend开始 165 | call_stack *first; 166 | uint index; 167 | /// 当前数据总大小 168 | uint size; 169 | /// 总开销 170 | uint64_t cost; 171 | /// 是否是主线程 172 | bool is_main_thread; 173 | // 调用栈信息 174 | char *stack_info; 175 | uint64_t stack_info_size; 176 | }common_data; 177 | 178 | ///获取线程共享数据 179 | common_data *get_thread_call_stack() { 180 | /// pthread_getspecific 获取指定key的同一线程中不同方法的共享数据 181 | common_data *_cd = (common_data *)pthread_getspecific(_thread_key); 182 | if (_cd == NULL) { 183 | _cd = (common_data *)malloc(sizeof(common_data)); 184 | _cd->size = 10; 185 | /// 调用栈 186 | _cd->cs = (call_stack *)malloc(sizeof(call_stack) * _cd->size); 187 | /// 当前调用栈索引 188 | _cd->index = 0; 189 | _cd->cost = 0; 190 | /// 存放共享数据 191 | pthread_setspecific(_thread_key, (void *)_cd); 192 | } 193 | _cd->is_main_thread = pthread_main_np(); 194 | return _cd; 195 | } 196 | /// 如果调用栈大小不够,重新分配空间 197 | void realloc_call_stack_memery_ifneed() { 198 | common_data *_cd = get_thread_call_stack(); 199 | /// 重新分配空间 200 | if (_cd->index >= _cd->size) { 201 | _cd->size += 10; 202 | _cd->cs = (call_stack *)realloc(_cd->cs, sizeof(call_stack) * _cd->size); 203 | } 204 | } 205 | /// 打印调用信息 206 | char *dump_call_stack(call_stack *_cs) { 207 | uint64_t cost = _cs->end_time - _cs->start_time; 208 | if (cost > 100 && !hook_has_prefix(_cs->class_name, "OS_")) { 209 | int _count = _cs->dept; 210 | while (_count > 0) { 211 | printf(" "); 212 | _count --; 213 | } 214 | char *space = (char *)malloc(_cs->dept); 215 | memset(space, ' ', _cs->dept); 216 | char *str = (char *)malloc(1024); 217 | sprintf(str, "%s %c [%s %s] %lld ms\n", space, '-', _cs->class_name, _cs->method_name, cost); 218 | free(space); 219 | return str; 220 | } 221 | return NULL; 222 | } 223 | /// 打印一个objc_msgSend完整调用信息 224 | void dump_method() { 225 | common_data *_cd = get_thread_call_stack(); 226 | _cd->cost = _cd->first->end_time - _cd->first->start_time; 227 | if(_cd->cost > 400 && _cd->first && !hook_has_prefix(_cd->first->class_name, "OS_")) { 228 | printf("[%s][%s: %s]: %lld ms\n", _cd->is_main_thread ? "主" : "子", _cd->first->class_name, _cd->first->method_name, _cd->cost); 229 | printf("%s \n", _cd->stack_info); 230 | } 231 | 232 | } 233 | /// hook 之前 234 | void before_objc_msgSend(id object, SEL _cmd, uintptr_t lr) { 235 | realloc_call_stack_memery_ifneed(); 236 | common_data *_cd = get_thread_call_stack(); 237 | /// 保存当前调用栈下一个函数方法执行地址 238 | call_stack *_cs = &(_cd->cs[_cd->index]); 239 | /// 正常执行程序的下一条指令,保存起来在hook完之后需要恢复到hook前的状态,程序lr寄存器能正常执行 240 | _cs->lr = lr; 241 | /// 保存执行的SEL 242 | _cs->_cmd = _cmd; 243 | /// 保存执行前的时间 244 | _cs->start_time = hook_getMillisecond(); 245 | /// 保存当前调用栈的深度 246 | _cs->dept = _cd->index; 247 | /// 保存当前对象的类名 248 | _cs->class_name = (char *)hook_getObjectClassName(object); 249 | /// 保存当前调用的方法名 250 | _cs->method_name = (char *)_cs->_cmd; 251 | /// 判断是否是元类的实例方法,即类的类方法 252 | // Class __cls = object_getClass(object); 253 | // _cs->is_class_method = class_isMetaClass(__cls); 254 | /// index为0表示调用栈顶,即objc_msgSend初始调用(objc_msgSend内部还会调用其它objc_msgSend) 255 | if(_cd->index == 0) { 256 | _cd->first = _cs; 257 | _cd->stack_info_size = 1024; 258 | if(_cd->stack_info) free(_cd->stack_info); 259 | _cd->stack_info = (char *)malloc(1024); 260 | } 261 | /// 入栈 262 | /// 调用栈前进 263 | _cd->index ++; 264 | } 265 | /// hook 之后 266 | uintptr_t after_objc_msgSend() { 267 | common_data *_cd = (common_data *)pthread_getspecific(_thread_key); 268 | /// 后退 269 | _cd->index --; 270 | /// 获取即将完成的调用 271 | call_stack *stack = &(_cd->cs[_cd->index]); 272 | stack->end_time = hook_getMillisecond(); 273 | // _cd->cost += (stack->end_time - stack->start_time); 274 | /// 打印单个方法执行信息 275 | if(_cd->index > 0) { 276 | char *_stack_info = dump_call_stack(stack); 277 | if(_stack_info) { 278 | uint64_t _stack_info_count = strlen(_stack_info); 279 | uint64_t cur_stack_info_size = strlen(_cd->stack_info); 280 | // 判断是否超出当前容量 281 | if(_stack_info_count + cur_stack_info_size > _cd->stack_info_size) { 282 | //扩容 283 | _cd->stack_info_size = _stack_info_count + cur_stack_info_size + 1024; 284 | _cd->stack_info = (char *)realloc(_cd->stack_info, _cd->stack_info_size); 285 | } 286 | memcpy(_cd->stack_info + cur_stack_info_size, _stack_info, _stack_info_count); 287 | free(_stack_info); 288 | } 289 | } 290 | /// 一个函数调用完成 291 | /// 打印所有方法执行信息 292 | if (_cd->index == 0){ 293 | dump_method(); 294 | _cd->stack_info_size = 1024; 295 | if(_cd->stack_info) free(_cd->stack_info); 296 | _cd->stack_info = (char *)malloc(_cd->stack_info_size); 297 | } 298 | /// 将下一条函数指令返回,并存放到寄存器x0 299 | return stack->lr; 300 | } 301 | /// 释放与线程共享数据的相关资源 302 | void release_thread_stack() { 303 | printf("release \n"); 304 | common_data *_cd = (common_data *)pthread_getspecific(_thread_key); 305 | if(!_cd) return; 306 | if (_cd->cs) free(_cd->cs); 307 | if (_cd->stack_info) free(_cd->stack_info); 308 | free(_cd); 309 | } 310 | 311 | void start_hook(void *hook_objc_msgSend, void **origin_objc_msgSend) { 312 | printf("hook_start\n"); 313 | static dispatch_once_t onceToken; 314 | dispatch_once(&onceToken, ^{ 315 | pthread_key_create(&_thread_key, &release_thread_stack); 316 | /// 替换系统的函数: 因为apple使用了系统函数动态绑定技术,所以可以hook系统函数,本地函数编译时地址已固定 317 | /// rebinding: 第一个参数,需要hook的系统函数名称 318 | /// 第二个参数,替换系统的函数 319 | /// 第三个参数,指向被hook函数,调用orig_objc_msgSend相当于调用原始函数 320 | rebind_symbols((struct rebinding[1]){ "objc_msgSend", hook_objc_msgSend, origin_objc_msgSend}, 1); 321 | }); 322 | } 323 | 324 | ``` 325 | 326 | --------------------------------------------------------------------------------