├── .gitignore ├── .gitmodules ├── README.md ├── makefile ├── picture └── after_restore.jpeg ├── restore-symbol.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── search_oc_block └── ida_search_block.py └── source ├── RSScanMethodVisitor.h ├── RSScanMethodVisitor.m ├── RSSymbol.h ├── RSSymbol.m ├── RSSymbolCollector.h ├── RSSymbolCollector.m ├── main.m ├── restore-symbol.m └── restore-symbol.pch /.gitignore: -------------------------------------------------------------------------------- 1 | restore-symbol 2 | 3 | 4 | # svn 5 | *.svn* 6 | 7 | # mac 8 | __MACOSX 9 | *.DS_Store* 10 | *._* 11 | *.lock 12 | *.Spotlight-V100 13 | *.Trashes 14 | *ehthumbs.db 15 | *Thumbs.db 16 | 17 | # Python 18 | *.pyc 19 | __pycache__/ 20 | 21 | 22 | # Vim 23 | *.swp 24 | *.swo 25 | *.swn 26 | 27 | # Xcode 28 | *~.nib 29 | build/ 30 | *.pbxuser 31 | !default.pbxuser 32 | *.mode1v3 33 | !default.mode1v3 34 | *.mode2v3 35 | !default.mode2v3 36 | *.perspectivev3 37 | !default.perspectivev3 38 | xcuserdata 39 | *.xccheckout 40 | *.xcscmblueprint 41 | *.moved-aside 42 | DerivedData 43 | *.hmap 44 | *.ipa 45 | *.xcuserstate 46 | 47 | # generated files 48 | bin/ 49 | gen/ 50 | 51 | # built application files 52 | *.apk 53 | *.ap_ 54 | 55 | # files for the dex VM 56 | *.dex 57 | 58 | # Java class files 59 | *.class 60 | 61 | # Local configuration file (sdk path, etc) 62 | local.properties 63 | 64 | # Eclipse project files 65 | .classpath 66 | .project 67 | .settings/ 68 | 69 | # Proguard folder generated by Eclipse 70 | proguard/ 71 | 72 | # Intellij project files 73 | *.iml 74 | *.ipr 75 | *.iws 76 | .idea/ 77 | 78 | #Pod 79 | Pods/ 80 | !podfile 81 | !podfile.lock 82 | 83 | # Gradle files 84 | .gradle 85 | build/ 86 | 87 | # Ignore Gradle GUI config 88 | gradle-app.setting 89 | 90 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 91 | !gradle-wrapper.jar 92 | 93 | #Android studio For eclipse 94 | #build.gradle 95 | #gradle/ 96 | #gradlew 97 | #gradlew.bat 98 | #.gradle/ 99 | # 100 | 101 | 102 | 103 | #tweak 104 | _/ 105 | .theos/ 106 | obj/ 107 | *.deb 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "class-dump"] 2 | path = class-dump 3 | url = https://github.com/HeiTanBc/class-dump 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # restore-symbol 2 | 3 | A reverse engineering tool to restore stripped symbol table for iOS app. 4 | 5 | Example: restore symbol for Alipay 6 | ![](https://raw.githubusercontent.com/tobefuturer/restore-symbol/master/picture/after_restore.jpeg) 7 | 8 | 9 | ## How to use 10 | 11 | ### Just restore symbol of oc method 12 | 13 | - 1. Download source code and compile. 14 | 15 | ``` 16 | 17 | git clone --recursive https://github.com/tobefuturer/restore-symbol.git 18 | cd restore-symbol && make 19 | ./restore-symbol 20 | 21 | ``` 22 | 23 | - 2. Restore symbol using this command. It will output a new mach-o file with symbol. 24 | 25 | ``` 26 | 27 | ./restore-symbol /pathto/origin_mach_o_file -o /pathto/mach_o_with_symbol 28 | 29 | ``` 30 | 31 | - 3. Copy the new mach-o file (with symbol) to app bundle, replace the origin mach-o file with new mach-o file. Resign app bundle. 32 | 33 | ``` 34 | 35 | codesign -f -s "iPhone Developer: XXXXXXX" --signing-time none --entitlement ./xxxx.app.xcent ./xxxx.app 36 | 37 | ``` 38 | 39 | - 4. Install the app bundle to iOS device, and use lldb to debug the app. Maybe you can use the ```ios-deploy```, or other way you like. If you use ```ios-deploy``` , you can execute this command. 40 | 41 | ``` 42 | 43 | brew install ios-deploy 44 | ios-deploy -d -b xxxx.app 45 | 46 | ``` 47 | - 5. Now you can use ```b -[class method]``` to set breakpoint. 48 | 49 | ### Restore symbol of oc block 50 | 51 | - 1. Search block symbol in IDA to get json symbol file, using script([`search_oc_block/ida_search_block.py`](https://github.com/tobefuturer/restore-symbol/blob/master/search_oc_block/ida_search_block.py)) . 52 | 53 | ![](http://blog.imjun.net/posts/restore-symbol-of-iOS-app/ida_result_position.png) 54 | 55 | ![](http://blog.imjun.net/posts/restore-symbol-of-iOS-app/ida_result_sample.jpg) 56 | 57 | - 2. Use command line tool(restore-symbol) to inject oc method symbols and block symbols into mach o file. 58 | 59 | ``` 60 | 61 | ./restore-symbol /pathto/origin_mach_o_file -o /pathto/mach_o_with_symbol -j /pathto/block_symbol.json 62 | 63 | ``` 64 | 65 | - 3. Other steps(resign, install, debug) are samen as above. 66 | 67 | ## Command Line Usage 68 | ``` 69 | Usage: restore-symbol -o [-j ] 70 | 71 | where options are: 72 | -o New mach-o-file path 73 | --disable-oc-detect Disable auto detect and add oc method into symbol table, 74 | only add symbol in json file 75 | --replace-restrict New mach-o-file will replace the LC_SEGMENT(__RESTRICT,__restrict) 76 | with LC_SEGMENT(__restrict,__restrict) to close dylib inject protection 77 | -j Json file containing extra symbol info, the key is "name","address" 78 | like this: 79 | 80 | [ 81 | { 82 | "name": "main", 83 | "address": "0xXXXXXX" 84 | }, 85 | { 86 | "name": "-[XXXX XXXXX]", 87 | "address": "0xXXXXXX" 88 | }, 89 | .... 90 | ] 91 | 92 | ``` 93 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY:restore-symbol 3 | 4 | TMP_FILE := libMachObjC.a restore-symbol.dSYM/ build/ class-dump/build/ 5 | 6 | restore-symbol: 7 | rm -rf $(TMP_FILE) 8 | rm -f restore-symbol 9 | git submodule update --init --recursive 10 | xcodebuild -project "restore-symbol.xcodeproj" -target "restore-symbol" -configuration "Release" CONFIGURATION_BUILD_DIR="$(shell pwd)" -jobs 4 build 11 | rm -rf $(TMP_FILE) 12 | 13 | 14 | clean: 15 | rm -rf restore-symbol $(TMP_FILE) 16 | 17 | -------------------------------------------------------------------------------- /picture/after_restore.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeiTanBc/restore-symbol/57e7577cf5d4e2bcd4cb5906ebb901f97fd22c68/picture/after_restore.jpeg -------------------------------------------------------------------------------- /restore-symbol.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8F92BBCA1D6B7085008EA4C4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBC91D6B7085008EA4C4 /* main.m */; }; 11 | 8F92BBE31D6B70C7008EA4C4 /* libMachObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F92BBDA1D6B70A3008EA4C4 /* libMachObjC.a */; }; 12 | 8F92BBEF1D6B7265008EA4C4 /* restore-symbol.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBE71D6B7265008EA4C4 /* restore-symbol.m */; }; 13 | 8F92BBF01D6B7265008EA4C4 /* RSScanMethodVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBE91D6B7265008EA4C4 /* RSScanMethodVisitor.m */; }; 14 | 8F92BBF11D6B7265008EA4C4 /* RSSymbol.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBEB1D6B7265008EA4C4 /* RSSymbol.m */; }; 15 | 8F92BBF21D6B7265008EA4C4 /* RSSymbolCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBED1D6B7265008EA4C4 /* RSSymbolCollector.m */; }; 16 | 8F92BBF51D6B72E1008EA4C4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F92BBF41D6B72E1008EA4C4 /* Foundation.framework */; }; 17 | 8F92BBF91D6B7445008EA4C4 /* CDClassDump.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBF81D6B7445008EA4C4 /* CDClassDump.m */; }; 18 | 8F92BBFD1D6B750E008EA4C4 /* CDVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBFB1D6B750E008EA4C4 /* CDVisitor.m */; }; 19 | 8F92BBFF1D6B758A008EA4C4 /* CDTypeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BBFE1D6B758A008EA4C4 /* CDTypeController.m */; }; 20 | 8F92BC021D6B78FD008EA4C4 /* CDSearchPathState.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BC011D6B78FD008EA4C4 /* CDSearchPathState.m */; }; 21 | 8F92BC041D6B790C008EA4C4 /* CDStructureTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BC031D6B790C008EA4C4 /* CDStructureTable.m */; }; 22 | 8F92BC061D6B791D008EA4C4 /* CDStructureInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F92BC051D6B791D008EA4C4 /* CDStructureInfo.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 8F92BBD91D6B70A3008EA4C4 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 29 | proxyType = 2; 30 | remoteGlobalIDString = 013D1F1113A5AE5A00BF0A67; 31 | remoteInfo = MachObjC; 32 | }; 33 | 8F92BBDB1D6B70A3008EA4C4 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 36 | proxyType = 2; 37 | remoteGlobalIDString = 01EB825F13A590D9003EDE60; 38 | remoteInfo = "class-dump"; 39 | }; 40 | 8F92BBDD1D6B70A3008EA4C4 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 43 | proxyType = 2; 44 | remoteGlobalIDString = 013D1EFB13A5A0F100BF0A67; 45 | remoteInfo = deprotect; 46 | }; 47 | 8F92BBDF1D6B70A3008EA4C4 /* PBXContainerItemProxy */ = { 48 | isa = PBXContainerItemProxy; 49 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 50 | proxyType = 2; 51 | remoteGlobalIDString = 01B02CFF13A5B0DC0047BC53; 52 | remoteInfo = formatType; 53 | }; 54 | 8F92BBE11D6B70A3008EA4C4 /* PBXContainerItemProxy */ = { 55 | isa = PBXContainerItemProxy; 56 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 57 | proxyType = 2; 58 | remoteGlobalIDString = 0165B8B11827137D00CC647F; 59 | remoteInfo = UnitTests; 60 | }; 61 | 8F92BBF61D6B73BD008EA4C4 /* PBXContainerItemProxy */ = { 62 | isa = PBXContainerItemProxy; 63 | containerPortal = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 64 | proxyType = 1; 65 | remoteGlobalIDString = 013D1F1013A5AE5A00BF0A67; 66 | remoteInfo = MachObjC; 67 | }; 68 | /* End PBXContainerItemProxy section */ 69 | 70 | /* Begin PBXCopyFilesBuildPhase section */ 71 | 8F92BBC41D6B7085008EA4C4 /* CopyFiles */ = { 72 | isa = PBXCopyFilesBuildPhase; 73 | buildActionMask = 2147483647; 74 | dstPath = /usr/share/man/man1/; 75 | dstSubfolderSpec = 0; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 1; 79 | }; 80 | /* End PBXCopyFilesBuildPhase section */ 81 | 82 | /* Begin PBXFileReference section */ 83 | 8F92BBC61D6B7085008EA4C4 /* restore-symbol */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "restore-symbol"; sourceTree = BUILT_PRODUCTS_DIR; }; 84 | 8F92BBC91D6B7085008EA4C4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 85 | 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "class-dump.xcodeproj"; path = "class-dump/class-dump.xcodeproj"; sourceTree = ""; }; 86 | 8F92BBE71D6B7265008EA4C4 /* restore-symbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "restore-symbol.m"; sourceTree = ""; }; 87 | 8F92BBE81D6B7265008EA4C4 /* RSScanMethodVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSScanMethodVisitor.h; sourceTree = ""; }; 88 | 8F92BBE91D6B7265008EA4C4 /* RSScanMethodVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSScanMethodVisitor.m; sourceTree = ""; }; 89 | 8F92BBEA1D6B7265008EA4C4 /* RSSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSSymbol.h; sourceTree = ""; }; 90 | 8F92BBEB1D6B7265008EA4C4 /* RSSymbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSSymbol.m; sourceTree = ""; }; 91 | 8F92BBEC1D6B7265008EA4C4 /* RSSymbolCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSSymbolCollector.h; sourceTree = ""; }; 92 | 8F92BBED1D6B7265008EA4C4 /* RSSymbolCollector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSSymbolCollector.m; sourceTree = ""; }; 93 | 8F92BBF31D6B72C6008EA4C4 /* restore-symbol.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "restore-symbol.pch"; sourceTree = ""; }; 94 | 8F92BBF41D6B72E1008EA4C4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 95 | 8F92BBF81D6B7445008EA4C4 /* CDClassDump.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDClassDump.m; path = "../class-dump/Source/CDClassDump.m"; sourceTree = ""; }; 96 | 8F92BBFB1D6B750E008EA4C4 /* CDVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVisitor.m; path = "../class-dump/Source/CDVisitor.m"; sourceTree = ""; }; 97 | 8F92BBFE1D6B758A008EA4C4 /* CDTypeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDTypeController.m; path = "../class-dump/Source/CDTypeController.m"; sourceTree = ""; }; 98 | 8F92BC011D6B78FD008EA4C4 /* CDSearchPathState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDSearchPathState.m; path = "../class-dump/Source/CDSearchPathState.m"; sourceTree = ""; }; 99 | 8F92BC031D6B790C008EA4C4 /* CDStructureTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDStructureTable.m; path = "../class-dump/Source/CDStructureTable.m"; sourceTree = ""; }; 100 | 8F92BC051D6B791D008EA4C4 /* CDStructureInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDStructureInfo.m; path = "../class-dump/Source/CDStructureInfo.m"; sourceTree = ""; }; 101 | /* End PBXFileReference section */ 102 | 103 | /* Begin PBXFrameworksBuildPhase section */ 104 | 8F92BBC31D6B7085008EA4C4 /* Frameworks */ = { 105 | isa = PBXFrameworksBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | 8F92BBF51D6B72E1008EA4C4 /* Foundation.framework in Frameworks */, 109 | 8F92BBE31D6B70C7008EA4C4 /* libMachObjC.a in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | /* End PBXFrameworksBuildPhase section */ 114 | 115 | /* Begin PBXGroup section */ 116 | 8F92BBBD1D6B7085008EA4C4 = { 117 | isa = PBXGroup; 118 | children = ( 119 | 8F92BBF41D6B72E1008EA4C4 /* Foundation.framework */, 120 | 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */, 121 | 8F92BBC81D6B7085008EA4C4 /* source */, 122 | 8F92BBC71D6B7085008EA4C4 /* Products */, 123 | ); 124 | sourceTree = ""; 125 | }; 126 | 8F92BBC71D6B7085008EA4C4 /* Products */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 8F92BBC61D6B7085008EA4C4 /* restore-symbol */, 130 | ); 131 | name = Products; 132 | sourceTree = ""; 133 | }; 134 | 8F92BBC81D6B7085008EA4C4 /* source */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 8F92BC001D6B789F008EA4C4 /* ref-to-class-dump */, 138 | 8F92BBE81D6B7265008EA4C4 /* RSScanMethodVisitor.h */, 139 | 8F92BBE91D6B7265008EA4C4 /* RSScanMethodVisitor.m */, 140 | 8F92BBEA1D6B7265008EA4C4 /* RSSymbol.h */, 141 | 8F92BBEB1D6B7265008EA4C4 /* RSSymbol.m */, 142 | 8F92BBEC1D6B7265008EA4C4 /* RSSymbolCollector.h */, 143 | 8F92BBED1D6B7265008EA4C4 /* RSSymbolCollector.m */, 144 | 8F92BBC91D6B7085008EA4C4 /* main.m */, 145 | 8F92BBE71D6B7265008EA4C4 /* restore-symbol.m */, 146 | 8F92BBF31D6B72C6008EA4C4 /* restore-symbol.pch */, 147 | ); 148 | path = source; 149 | sourceTree = ""; 150 | }; 151 | 8F92BBD11D6B70A3008EA4C4 /* Products */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 8F92BBDA1D6B70A3008EA4C4 /* libMachObjC.a */, 155 | 8F92BBDC1D6B70A3008EA4C4 /* class-dump */, 156 | 8F92BBDE1D6B70A3008EA4C4 /* deprotect */, 157 | 8F92BBE01D6B70A3008EA4C4 /* formatType */, 158 | 8F92BBE21D6B70A3008EA4C4 /* UnitTests.xctest */, 159 | ); 160 | name = Products; 161 | sourceTree = ""; 162 | }; 163 | 8F92BC001D6B789F008EA4C4 /* ref-to-class-dump */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 8F92BC051D6B791D008EA4C4 /* CDStructureInfo.m */, 167 | 8F92BC031D6B790C008EA4C4 /* CDStructureTable.m */, 168 | 8F92BC011D6B78FD008EA4C4 /* CDSearchPathState.m */, 169 | 8F92BBF81D6B7445008EA4C4 /* CDClassDump.m */, 170 | 8F92BBFE1D6B758A008EA4C4 /* CDTypeController.m */, 171 | 8F92BBFB1D6B750E008EA4C4 /* CDVisitor.m */, 172 | ); 173 | name = "ref-to-class-dump"; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | 8F92BBC51D6B7085008EA4C4 /* restore-symbol */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = 8F92BBCD1D6B7085008EA4C4 /* Build configuration list for PBXNativeTarget "restore-symbol" */; 182 | buildPhases = ( 183 | 8F92BBC21D6B7085008EA4C4 /* Sources */, 184 | 8F92BBC31D6B7085008EA4C4 /* Frameworks */, 185 | 8F92BBC41D6B7085008EA4C4 /* CopyFiles */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | 8F92BBF71D6B73BD008EA4C4 /* PBXTargetDependency */, 191 | ); 192 | name = "restore-symbol"; 193 | productName = "restore-symbol"; 194 | productReference = 8F92BBC61D6B7085008EA4C4 /* restore-symbol */; 195 | productType = "com.apple.product-type.tool"; 196 | }; 197 | /* End PBXNativeTarget section */ 198 | 199 | /* Begin PBXProject section */ 200 | 8F92BBBE1D6B7085008EA4C4 /* Project object */ = { 201 | isa = PBXProject; 202 | attributes = { 203 | LastUpgradeCheck = 0730; 204 | ORGANIZATIONNAME = Jun; 205 | TargetAttributes = { 206 | 8F92BBC51D6B7085008EA4C4 = { 207 | CreatedOnToolsVersion = 7.3.1; 208 | }; 209 | }; 210 | }; 211 | buildConfigurationList = 8F92BBC11D6B7085008EA4C4 /* Build configuration list for PBXProject "restore-symbol" */; 212 | compatibilityVersion = "Xcode 3.2"; 213 | developmentRegion = English; 214 | hasScannedForEncodings = 0; 215 | knownRegions = ( 216 | en, 217 | ); 218 | mainGroup = 8F92BBBD1D6B7085008EA4C4; 219 | productRefGroup = 8F92BBC71D6B7085008EA4C4 /* Products */; 220 | projectDirPath = ""; 221 | projectReferences = ( 222 | { 223 | ProductGroup = 8F92BBD11D6B70A3008EA4C4 /* Products */; 224 | ProjectRef = 8F92BBD01D6B70A3008EA4C4 /* class-dump.xcodeproj */; 225 | }, 226 | ); 227 | projectRoot = ""; 228 | targets = ( 229 | 8F92BBC51D6B7085008EA4C4 /* restore-symbol */, 230 | ); 231 | }; 232 | /* End PBXProject section */ 233 | 234 | /* Begin PBXReferenceProxy section */ 235 | 8F92BBDA1D6B70A3008EA4C4 /* libMachObjC.a */ = { 236 | isa = PBXReferenceProxy; 237 | fileType = archive.ar; 238 | path = libMachObjC.a; 239 | remoteRef = 8F92BBD91D6B70A3008EA4C4 /* PBXContainerItemProxy */; 240 | sourceTree = BUILT_PRODUCTS_DIR; 241 | }; 242 | 8F92BBDC1D6B70A3008EA4C4 /* class-dump */ = { 243 | isa = PBXReferenceProxy; 244 | fileType = "compiled.mach-o.executable"; 245 | path = "class-dump"; 246 | remoteRef = 8F92BBDB1D6B70A3008EA4C4 /* PBXContainerItemProxy */; 247 | sourceTree = BUILT_PRODUCTS_DIR; 248 | }; 249 | 8F92BBDE1D6B70A3008EA4C4 /* deprotect */ = { 250 | isa = PBXReferenceProxy; 251 | fileType = "compiled.mach-o.executable"; 252 | path = deprotect; 253 | remoteRef = 8F92BBDD1D6B70A3008EA4C4 /* PBXContainerItemProxy */; 254 | sourceTree = BUILT_PRODUCTS_DIR; 255 | }; 256 | 8F92BBE01D6B70A3008EA4C4 /* formatType */ = { 257 | isa = PBXReferenceProxy; 258 | fileType = "compiled.mach-o.executable"; 259 | path = formatType; 260 | remoteRef = 8F92BBDF1D6B70A3008EA4C4 /* PBXContainerItemProxy */; 261 | sourceTree = BUILT_PRODUCTS_DIR; 262 | }; 263 | 8F92BBE21D6B70A3008EA4C4 /* UnitTests.xctest */ = { 264 | isa = PBXReferenceProxy; 265 | fileType = wrapper.cfbundle; 266 | path = UnitTests.xctest; 267 | remoteRef = 8F92BBE11D6B70A3008EA4C4 /* PBXContainerItemProxy */; 268 | sourceTree = BUILT_PRODUCTS_DIR; 269 | }; 270 | /* End PBXReferenceProxy section */ 271 | 272 | /* Begin PBXSourcesBuildPhase section */ 273 | 8F92BBC21D6B7085008EA4C4 /* Sources */ = { 274 | isa = PBXSourcesBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | 8F92BBEF1D6B7265008EA4C4 /* restore-symbol.m in Sources */, 278 | 8F92BC041D6B790C008EA4C4 /* CDStructureTable.m in Sources */, 279 | 8F92BBCA1D6B7085008EA4C4 /* main.m in Sources */, 280 | 8F92BBF01D6B7265008EA4C4 /* RSScanMethodVisitor.m in Sources */, 281 | 8F92BC061D6B791D008EA4C4 /* CDStructureInfo.m in Sources */, 282 | 8F92BBF21D6B7265008EA4C4 /* RSSymbolCollector.m in Sources */, 283 | 8F92BBF11D6B7265008EA4C4 /* RSSymbol.m in Sources */, 284 | 8F92BBF91D6B7445008EA4C4 /* CDClassDump.m in Sources */, 285 | 8F92BC021D6B78FD008EA4C4 /* CDSearchPathState.m in Sources */, 286 | 8F92BBFF1D6B758A008EA4C4 /* CDTypeController.m in Sources */, 287 | 8F92BBFD1D6B750E008EA4C4 /* CDVisitor.m in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXSourcesBuildPhase section */ 292 | 293 | /* Begin PBXTargetDependency section */ 294 | 8F92BBF71D6B73BD008EA4C4 /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | name = MachObjC; 297 | targetProxy = 8F92BBF61D6B73BD008EA4C4 /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin XCBuildConfiguration section */ 302 | 8F92BBCB1D6B7085008EA4C4 /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BOOL_CONVERSION = YES; 312 | CLANG_WARN_CONSTANT_CONVERSION = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INT_CONVERSION = YES; 317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 318 | CLANG_WARN_UNREACHABLE_CODE = YES; 319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 320 | CODE_SIGN_IDENTITY = "-"; 321 | COPY_PHASE_STRIP = NO; 322 | DEBUG_INFORMATION_FORMAT = dwarf; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | ENABLE_TESTABILITY = YES; 325 | GCC_C_LANGUAGE_STANDARD = gnu99; 326 | GCC_DYNAMIC_NO_PIC = NO; 327 | GCC_NO_COMMON_BLOCKS = YES; 328 | GCC_OPTIMIZATION_LEVEL = 0; 329 | GCC_PREPROCESSOR_DEFINITIONS = ( 330 | "DEBUG=1", 331 | "$(inherited)", 332 | ); 333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 335 | GCC_WARN_UNDECLARED_SELECTOR = YES; 336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 337 | GCC_WARN_UNUSED_FUNCTION = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | MACOSX_DEPLOYMENT_TARGET = 10.11; 340 | MTL_ENABLE_DEBUG_INFO = YES; 341 | ONLY_ACTIVE_ARCH = YES; 342 | SDKROOT = macosx; 343 | }; 344 | name = Debug; 345 | }; 346 | 8F92BBCC1D6B7085008EA4C4 /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | CLANG_ANALYZER_NONNULL = YES; 351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 352 | CLANG_CXX_LIBRARY = "libc++"; 353 | CLANG_ENABLE_MODULES = YES; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_WARN_BOOL_CONVERSION = YES; 356 | CLANG_WARN_CONSTANT_CONVERSION = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_EMPTY_BODY = YES; 359 | CLANG_WARN_ENUM_CONVERSION = YES; 360 | CLANG_WARN_INT_CONVERSION = YES; 361 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 362 | CLANG_WARN_UNREACHABLE_CODE = YES; 363 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 364 | CODE_SIGN_IDENTITY = "-"; 365 | COPY_PHASE_STRIP = NO; 366 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 367 | ENABLE_NS_ASSERTIONS = NO; 368 | ENABLE_STRICT_OBJC_MSGSEND = YES; 369 | GCC_C_LANGUAGE_STANDARD = gnu99; 370 | GCC_NO_COMMON_BLOCKS = YES; 371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 373 | GCC_WARN_UNDECLARED_SELECTOR = YES; 374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 375 | GCC_WARN_UNUSED_FUNCTION = YES; 376 | GCC_WARN_UNUSED_VARIABLE = YES; 377 | MACOSX_DEPLOYMENT_TARGET = 10.11; 378 | MTL_ENABLE_DEBUG_INFO = NO; 379 | SDKROOT = macosx; 380 | }; 381 | name = Release; 382 | }; 383 | 8F92BBCE1D6B7085008EA4C4 /* Debug */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | GCC_PREFIX_HEADER = "$(SRCROOT)/source/restore-symbol.pch"; 387 | OTHER_LDFLAGS = "-all_load"; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/class-dump/Source/**"; 390 | }; 391 | name = Debug; 392 | }; 393 | 8F92BBCF1D6B7085008EA4C4 /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | GCC_PREFIX_HEADER = "$(SRCROOT)/source/restore-symbol.pch"; 397 | OTHER_LDFLAGS = "-all_load"; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/class-dump/Source/**"; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | 8F92BBC11D6B7085008EA4C4 /* Build configuration list for PBXProject "restore-symbol" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | 8F92BBCB1D6B7085008EA4C4 /* Debug */, 410 | 8F92BBCC1D6B7085008EA4C4 /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | 8F92BBCD1D6B7085008EA4C4 /* Build configuration list for PBXNativeTarget "restore-symbol" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 8F92BBCE1D6B7085008EA4C4 /* Debug */, 419 | 8F92BBCF1D6B7085008EA4C4 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | /* End XCConfigurationList section */ 425 | }; 426 | rootObject = 8F92BBBE1D6B7085008EA4C4 /* Project object */; 427 | } 428 | -------------------------------------------------------------------------------- /restore-symbol.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /search_oc_block/ida_search_block.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import idautils 4 | import idc 5 | from idaapi import PluginForm 6 | import operator 7 | import csv 8 | import sys 9 | import json 10 | 11 | 12 | IS32BIT = not idaapi.get_inf_structure().is_64bit() 13 | 14 | IS_MAC = 'X86_64' in idaapi.get_file_type_name() 15 | 16 | print "Start analyze binary for " + ("Mac" if IS_MAC else "iOS") 17 | 18 | 19 | def isInText(x): 20 | return SegName(x) == '__text' 21 | 22 | 23 | GlobalBlockAddr = LocByName("__NSConcreteGlobalBlock") 24 | 25 | class GlobalBlockInfo: 26 | pass 27 | 28 | AllGlobalBlockMap = {} 29 | for struct in list(DataRefsTo(GlobalBlockAddr)): 30 | func = 0L 31 | FUNC_OFFSET_IN_BLOCK = 12 if IS32BIT else 16 32 | if IS32BIT: 33 | func = Dword(struct + FUNC_OFFSET_IN_BLOCK) 34 | else: 35 | func = Qword(struct + FUNC_OFFSET_IN_BLOCK) 36 | 37 | 38 | info = GlobalBlockInfo() 39 | info.func = func 40 | info.struct = struct 41 | if len(list(DataRefsTo(struct))) == 0: 42 | continue 43 | refTo = list(DataRefsTo(struct))[0] 44 | info.superFuncName = GetFunctionName(refTo) 45 | info.superFunc = LocByName(info.superFuncName) 46 | 47 | AllGlobalBlockMap[func] = info 48 | 49 | def funcIsGlobalBlockFunc(block_func): 50 | return block_func in AllGlobalBlockMap 51 | 52 | 53 | def isPossibleStackBlockForFunc(block_func): 54 | # def superFuncForStackBlock(block_func): 55 | 56 | if not isInText(block_func): 57 | return False 58 | 59 | if GetFunctionAttr(block_func,FUNCATTR_START) != (block_func & ~ 1): 60 | return False 61 | 62 | #block addr cannot be called directly 63 | if len(list(CodeRefsTo(block_func, 0))) !=0 : 64 | # print '%x is not block because be call by %x' % (block_func ,list(CodeRefsTo(block_func, 0))[0]) 65 | return False 66 | 67 | # ref to block should be in text section 68 | refsTo = list(DataRefsTo(block_func)) 69 | for addr in refsTo: 70 | if not isInText(addr): 71 | # print '%x is not block because be ref from %x' % (block_func, addr) 72 | return False 73 | 74 | # block func should be ref in only 1 function 75 | superFuncs = [GetFunctionAttr(x,FUNCATTR_START) for x in refsTo] 76 | superFuncs = list (set (superFuncs)) 77 | if len(superFuncs) != 1: 78 | # print '%x is not block because be not ref from 1 function' % block_func 79 | return False 80 | 81 | return True 82 | 83 | def superFuncForStackBlock(block_func): 84 | refsTo = list(DataRefsTo(block_func)) 85 | superFuncs = [GetFunctionAttr(x,FUNCATTR_START) for x in refsTo] 86 | superFuncs = list (set (superFuncs)) 87 | if len(superFuncs) != 1: 88 | return None 89 | super_func_addr = superFuncs[0] 90 | if IS_MAC: 91 | return super_func_addr 92 | else: 93 | return super_func_addr | GetReg(super_func_addr, "T") # thumb 94 | 95 | 96 | def superFuncForBlockFunc(block_func): 97 | if funcIsGlobalBlockFunc(block_func): 98 | return AllGlobalBlockMap[block_func].superFunc 99 | 100 | superStackFunc = superFuncForStackBlock(block_func) 101 | return superStackFunc # maybe None 102 | 103 | 104 | 105 | resultDict = {} 106 | 107 | 108 | def findBlockName(block_func): 109 | # print "find block name %X" % block_func 110 | funcName = GetFunctionName(block_func) 111 | 112 | if len(funcName) != 0 and funcName[0] in ('-', '+'): 113 | return funcName 114 | 115 | # maybe nested block 116 | superBlockFuncAddr = superFuncForBlockFunc(block_func) 117 | if superBlockFuncAddr == None: 118 | return ""; 119 | if not IS_MAC: 120 | superBlockFuncAddr = superBlockFuncAddr | GetReg(superBlockFuncAddr, "T") # thumb 121 | 122 | superBlockName = findBlockName(superBlockFuncAddr) 123 | 124 | if len(superBlockName) == 0: 125 | return "" 126 | else: 127 | return superBlockName + "_block" 128 | 129 | 130 | 131 | #find all possible Stack Block 132 | allPossibleStackBlockFunc = [] 133 | allRefToBlock=[] 134 | if IS32BIT: 135 | allRefToBlock = list(DataRefsTo(LocByName("__NSConcreteStackBlock"))) 136 | else: 137 | allRefToBlock = list(DataRefsTo(LocByName("__NSConcreteStackBlock_ptr"))) 138 | allRefToBlock.sort() 139 | 140 | ''' 141 | 2 ref (@PAGE , @PAGEOFF) to __NSConcreteStackBlock_ptr , 142 | but once actual 143 | filter the list 144 | __text:0000000102D9979C ADRP X8, #__NSConcreteStackBlock_ptr@PAGE 145 | __text:0000000102D997A0 LDR X8, [X8,#__NSConcreteStackBlock_ptr@PAGEOFF] 146 | ''' 147 | tmp_array = allRefToBlock[:1] 148 | for i in range(1, len(allRefToBlock)): 149 | if allRefToBlock[i] - allRefToBlock[i - 1] <= 8: 150 | pass 151 | else: 152 | tmp_array.append(allRefToBlock[i]) 153 | allRefToBlock = tmp_array 154 | 155 | allRefToBlock = filter(lambda x:isInText(x), allRefToBlock) 156 | 157 | for addr in allRefToBlock: 158 | LineNumAround = 30 #Around 30 arm instruction 159 | scan_addr_min= max (addr - LineNumAround * 4, GetFunctionAttr(addr,FUNCATTR_START)) 160 | scan_addr_max= min (addr + LineNumAround * 4, GetFunctionAttr(addr,FUNCATTR_END)) 161 | for scan_addr in range(scan_addr_min, scan_addr_max): 162 | allPossibleStackBlockFunc += list(DataRefsFrom(scan_addr)) # all function pointer used around __NSConcreteStackBlock 163 | 164 | allPossibleStackBlockFunc = list (set (allPossibleStackBlockFunc)) 165 | 166 | allPossibleStackBlockFunc = filter(lambda x:isPossibleStackBlockForFunc(x) , allPossibleStackBlockFunc ) 167 | 168 | 169 | 170 | 171 | #process all Global Block 172 | for block_func in AllGlobalBlockMap: 173 | block_name = findBlockName(block_func) 174 | resultDict[block_func] = block_name 175 | 176 | for block_func in allPossibleStackBlockFunc: 177 | block_name = findBlockName(block_func) 178 | resultDict[block_func] = block_name 179 | 180 | 181 | output_file = './block_symbol.json' 182 | list_output = [] 183 | error_num = 0 184 | for addr in resultDict: 185 | name = resultDict[addr] 186 | if len(name) == 0 or name[0] not in ('-', '+'): 187 | error_num += 1 188 | continue 189 | 190 | list_output += [{"address":("0x%X" % addr), "name":name}] 191 | 192 | 193 | encodeJson = json.dumps(list_output, indent=1) 194 | f = open(output_file, "w") 195 | f.write(encodeJson) 196 | f.close() 197 | 198 | print 'restore block num %d ' % len(list_output) 199 | print 'origin block num: %d(GlobalBlock: %d, StackBlock: %d)' % (len(allRefToBlock) + len(AllGlobalBlockMap), len(AllGlobalBlockMap), len(allRefToBlock)) 200 | -------------------------------------------------------------------------------- /source/RSScanMethodVisitor.h: -------------------------------------------------------------------------------- 1 | // -*- mode: ObjC -*- 2 | 3 | // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files. 4 | // Copyright (C) 1997-1998, 2000-2001, 2004-2015 Steve Nygard. 5 | 6 | #import "CDVisitor.h" 7 | #import "RSSymbolCollector.h" 8 | 9 | // This limits the output to methods matching the search string. Some context is included, so that you can see which class, category, or protocol 10 | // contains the method. 11 | 12 | @interface RSScanMethodVisitor : CDVisitor 13 | 14 | - (instancetype)initWithSymbolCollector:(RSSymbolCollector*)collector; 15 | 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /source/RSScanMethodVisitor.m: -------------------------------------------------------------------------------- 1 | // -*- mode: ObjC -*- 2 | 3 | // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files. 4 | // Copyright (C) 1997-1998, 2000-2001, 2004-2015 Steve Nygard. 5 | 6 | #import "RSScanMethodVisitor.h" 7 | 8 | #import "CDClassDump.h" 9 | #import "CDObjectiveC1Processor.h" 10 | #import "CDMachOFile.h" 11 | #import "CDOCProtocol.h" 12 | #import "CDLCDylib.h" 13 | #import "CDOCClass.h" 14 | #import "CDOCCategory.h" 15 | #import "CDOCClassReference.h" 16 | #import "CDOCMethod.h" 17 | #import 18 | #import 19 | 20 | //#import "CDTypeController.h" 21 | 22 | @interface RSScanMethodVisitor () 23 | 24 | @property (nonatomic, strong) CDOCProtocol *context; 25 | 26 | @property (nonatomic, weak) RSSymbolCollector * collector; 27 | 28 | @end 29 | 30 | #pragma mark - 31 | 32 | @implementation RSScanMethodVisitor 33 | { 34 | CDOCProtocol *_context; 35 | 36 | } 37 | 38 | - (id)initWithSymbolCollector:(RSSymbolCollector *)collector 39 | { 40 | if ((self = [super init])) { 41 | _context = nil; 42 | _collector = collector; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | #pragma mark - 49 | 50 | - (void)willVisitProtocol:(CDOCProtocol *)protocol; 51 | { 52 | [self setContext:protocol]; 53 | } 54 | 55 | - (void)willVisitClass:(CDOCClass *)aClass; 56 | { 57 | [self setContext:aClass]; 58 | 59 | // 这里生成类的符号,测试发现对于调用栈恢复,不是必要的 60 | 61 | if (aClass.classAddress != 0) { 62 | NSString * name = [NSString stringWithFormat:@"_OBJC_CLASS_$_%@", aClass.name]; 63 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.classAddress type:(N_SECT | N_EXT)]; 64 | [self.collector addSymbol:s]; 65 | 66 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:0 type:N_GSYM]; 67 | [self.collector addSymbol:s1]; 68 | 69 | } 70 | 71 | if (aClass.classRoAddress != 0) { 72 | NSString * name = [NSString stringWithFormat:@"__OBJC_CLASS_RO_$_%@", aClass.name]; 73 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.classRoAddress]; 74 | [self.collector addSymbol:s]; 75 | 76 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:aClass.classRoAddress type:N_STSYM]; 77 | [self.collector addSymbol:s1]; 78 | } 79 | 80 | if (aClass.metaClassAddress != 0) { 81 | NSString * name = [NSString stringWithFormat:@"_OBJC_METACLASS_$_%@", aClass.name]; 82 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.metaClassAddress type:(N_SECT | N_EXT)]; 83 | [self.collector addSymbol:s]; 84 | 85 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:0 type:N_GSYM]; 86 | [self.collector addSymbol:s1]; 87 | } 88 | 89 | if (aClass.metaClassRoAddress != 0) { 90 | NSString * name = [NSString stringWithFormat:@"__OBJC_METACLASS_RO_$_%@", aClass.name]; 91 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.metaClassRoAddress]; 92 | [self.collector addSymbol:s]; 93 | 94 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:aClass.metaClassRoAddress type:N_STSYM]; 95 | [self.collector addSymbol:s1]; 96 | } 97 | 98 | if (aClass.instanceMethodsAddress != 0) { 99 | NSString * name = [NSString stringWithFormat:@"__OBJC_$_INSTANCE_METHODS_%@", aClass.name]; 100 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.instanceMethodsAddress]; 101 | [self.collector addSymbol:s]; 102 | 103 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:aClass.instanceMethodsAddress type:N_STSYM]; 104 | [self.collector addSymbol:s1]; 105 | } 106 | 107 | if (aClass.protocolsAddress != 0) { 108 | NSString * name = [NSString stringWithFormat:@"__OBJC_CLASS_PROTOCOLS_$_%@", aClass.name]; 109 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.protocolsAddress]; 110 | [self.collector addSymbol:s]; 111 | 112 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:aClass.protocolsAddress type:N_STSYM]; 113 | [self.collector addSymbol:s1]; 114 | } 115 | 116 | if (aClass.instanceIvarAddress != 0) { 117 | NSString * name = [NSString stringWithFormat:@"__OBJC_$_INSTANCE_VARIABLES_%@", aClass.name]; 118 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.instanceIvarAddress]; 119 | [self.collector addSymbol:s]; 120 | 121 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:aClass.instanceIvarAddress type:N_STSYM]; 122 | [self.collector addSymbol:s1]; 123 | } 124 | 125 | if (aClass.propertiesAddress != 0) { 126 | NSString * name = [NSString stringWithFormat:@"__OBJC_$_PROP_LIST_%@", aClass.name]; 127 | RSSymbol *s = [RSSymbol symbolWithName:name address:aClass.propertiesAddress]; 128 | [self.collector addSymbol:s]; 129 | 130 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:aClass.propertiesAddress type:N_STSYM]; 131 | [self.collector addSymbol:s1]; 132 | } 133 | 134 | } 135 | 136 | 137 | - (void)willVisitCategory:(CDOCCategory *)category; 138 | { 139 | [self setContext:category]; 140 | } 141 | 142 | 143 | - (NSString *)getCurrentClassName{ 144 | if ([_context isKindOfClass:[CDOCClass class]]) { 145 | return _context.name; 146 | } else if([_context isKindOfClass:[CDOCCategory class]]) { 147 | NSString * className = [[(CDOCCategory *)_context classRef] className]; 148 | if (!className) className = @""; 149 | return [NSString stringWithFormat:@"%@(%@)", className ,_context.name]; 150 | } 151 | return _context.name; 152 | } 153 | 154 | - (void)visitClassMethod:(CDOCMethod *)method; 155 | { 156 | if (method.address == 0 ) { 157 | return; 158 | } 159 | 160 | NSString *name = [NSString stringWithFormat:@"+[%@ %@]", [self getCurrentClassName], method.name]; 161 | 162 | RSSymbol *s = [RSSymbol symbolWithName:name address:method.address]; 163 | 164 | [self.collector addSymbol:s]; 165 | 166 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:method.address type:N_FUN]; 167 | [self.collector addSymbol:s1]; 168 | 169 | } 170 | 171 | - (void)visitInstanceMethod:(CDOCMethod *)method propertyState:(CDVisitorPropertyState *)propertyState; 172 | { 173 | if (method.address == 0 ) { 174 | return; 175 | } 176 | NSString *name = [NSString stringWithFormat:@"-[%@ %@]", [self getCurrentClassName], method.name]; 177 | 178 | RSSymbol *s = [RSSymbol symbolWithName:name address:method.address]; 179 | 180 | [self.collector addSymbol:s]; 181 | 182 | RSSymbol *s1 = [RSSymbol symbolWithName:name address:method.address type:N_FUN]; 183 | [self.collector addSymbol:s1]; 184 | } 185 | 186 | 187 | #pragma mark - 188 | 189 | - (void)setContext:(CDOCProtocol *)newContext; 190 | { 191 | if (newContext != _context) { 192 | _context = newContext; 193 | } 194 | } 195 | 196 | 197 | 198 | @end 199 | -------------------------------------------------------------------------------- /source/RSSymbol.h: -------------------------------------------------------------------------------- 1 | // 2 | // RSSymbol.h 3 | // restore-symbol 4 | // 5 | // Created by EugeneYang on 16/8/19. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | #define RS_JSON_KEY_ADDRESS @"address" 14 | #define RS_JSON_KEY_SYMBOL_NAME @"name" 15 | 16 | @interface RSSymbol : NSObject 17 | 18 | 19 | @property (nonatomic, strong) NSString * name; 20 | @property (nonatomic) uint64 address; 21 | @property (nonatomic) uint8_t type; 22 | 23 | 24 | + (NSArray *)symbolsWithJson:(NSData *)json; 25 | 26 | + (RSSymbol *)symbolWithName:(NSString *)name address:(uint64)addr; 27 | + (RSSymbol *)symbolWithName:(NSString *)name address:(uint64)addr type:(uint8)type; 28 | 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /source/RSSymbol.m: -------------------------------------------------------------------------------- 1 | // 2 | // RSSymbol.m 3 | // restore-symbol 4 | // 5 | // Created by EugeneYang on 16/8/19. 6 | // 7 | // 8 | 9 | #import "RSSymbol.h" 10 | 11 | @implementation RSSymbol 12 | 13 | 14 | + (NSArray *)symbolsWithJson:(NSData *)json{ 15 | NSError * e = nil; 16 | 17 | NSArray *symbols = [NSJSONSerialization JSONObjectWithData:json options:NSJSONReadingMutableContainers error:&e]; 18 | 19 | if (!symbols) { 20 | fprintf(stderr,"Parse json error!\n"); 21 | fprintf(stderr,"%s\n", e.description.UTF8String); 22 | return nil; 23 | } 24 | 25 | NSMutableArray * rt = [NSMutableArray array]; 26 | for (NSDictionary *dict in symbols) { 27 | NSString *addressString = dict[RS_JSON_KEY_ADDRESS]; 28 | unsigned long long address; 29 | NSScanner* scanner = [NSScanner scannerWithString:addressString]; 30 | [scanner scanHexLongLong:&address]; 31 | RSSymbol * symbol = [self symbolWithName:dict[RS_JSON_KEY_SYMBOL_NAME] address:address]; 32 | [rt addObject:symbol]; 33 | } 34 | 35 | return rt; 36 | } 37 | 38 | 39 | + (RSSymbol *)symbolWithName:(NSString *)name address:(uint64)addr{ 40 | RSSymbol * s = [RSSymbol new]; 41 | s.name = name; 42 | s.address = addr; 43 | s.type = N_SECT; 44 | return s; 45 | } 46 | 47 | + (RSSymbol *)symbolWithName:(NSString *)name address:(uint64)addr type:(uint8)type{ 48 | RSSymbol * s = [RSSymbol new]; 49 | s.name = name; 50 | s.address = addr; 51 | s.type = type; 52 | return s; 53 | } 54 | @end 55 | -------------------------------------------------------------------------------- /source/RSSymbolCollector.h: -------------------------------------------------------------------------------- 1 | // 2 | // RSSymbolCollector.h 3 | // restore-symbol 4 | // 5 | // Created by EugeneYang on 16/8/19. 6 | // 7 | // 8 | 9 | #import 10 | #import "RSSymbol.h" 11 | #import "CDMachOFile.h" 12 | 13 | @interface RSSymbolCollector : NSObject 14 | 15 | @property (nonatomic, weak) CDMachOFile * machOFile; 16 | @property (nonatomic, strong) NSMutableArray *symbols; 17 | @property (nonatomic) unsigned long locSymbolSize; 18 | @property (nonatomic) unsigned long extSymbolSize; 19 | 20 | - (void)addSymbol:(RSSymbol *)symbol; 21 | - (void)addSymbols:(NSArray *)symbols; 22 | 23 | 24 | - (void)generateAppendStringTable:(NSData **)stringTable appendSymbolTable:(NSData **)nlist; 25 | @end 26 | -------------------------------------------------------------------------------- /source/RSSymbolCollector.m: -------------------------------------------------------------------------------- 1 | // 2 | // RSSymbolCollector.m 3 | // restore-symbol 4 | // 5 | // Created by EugeneYang on 16/8/19. 6 | // 7 | // 8 | 9 | #import "RSSymbolCollector.h" 10 | 11 | #import 12 | #import "CDLCSymbolTable.h" 13 | #import "CDLCSegment.h" 14 | #import "CDSection.h" 15 | 16 | @implementation RSSymbolCollector 17 | 18 | 19 | 20 | - (instancetype)init 21 | { 22 | self = [super init]; 23 | if (self) { 24 | _symbols = [NSMutableArray array]; 25 | } 26 | return self; 27 | } 28 | 29 | 30 | - (void)addSymbol:(RSSymbol *)symbol{ 31 | if (symbol == nil) { 32 | return ; 33 | } 34 | 35 | if (symbol.type & N_EXT) { 36 | self.extSymbolSize += 1; 37 | } else { 38 | self.locSymbolSize += 1; 39 | } 40 | [_symbols addObject:symbol]; 41 | } 42 | 43 | 44 | - (void)addSymbols:(NSArray *)symbols{ 45 | if (symbols == nil) 46 | return ; 47 | self.locSymbolSize += symbols.count; 48 | [_symbols addObjectsFromArray:symbols]; 49 | } 50 | 51 | 52 | - (void)generateAppendStringTable:(NSData **)stringTable appendSymbolTable:(NSData **)symbolTable{ 53 | 54 | self.symbols = [self.symbols sortedArrayUsingComparator:^NSComparisonResult(RSSymbol * sym1, RSSymbol * sym2) { 55 | if ((sym1.type & N_EXT) && (sym2.type & N_EXT)) { 56 | return sym1.type > sym2.type; 57 | } else if ((sym1.type & N_EXT) || (sym2.type & N_EXT)) { 58 | if (sym1.type & N_EXT) { 59 | return NSOrderedDescending; 60 | } else { 61 | return NSOrderedAscending; 62 | } 63 | } else { 64 | return sym1.type > sym2.type; 65 | } 66 | }]; 67 | 68 | const bool is32Bit = ! _machOFile.uses64BitABI; 69 | 70 | NSMutableData * symbolNames = [NSMutableData new]; 71 | 72 | NSMutableData * nlistsData = [NSMutableData dataWithLength:_symbols.count * ( is32Bit ? sizeof(struct nlist) : sizeof(struct nlist_64))]; 73 | 74 | memset(nlistsData.mutableBytes, 0, nlistsData.length); 75 | 76 | uint32 origin_string_table_size = _machOFile.symbolTable.strsize; 77 | 78 | 79 | for (int i = 0; i < _symbols.count; i ++) { 80 | 81 | 82 | RSSymbol * symbol = _symbols[i]; 83 | // if (symbol.address == 0) { 84 | // continue; 85 | // } 86 | 87 | 88 | if (is32Bit) { 89 | struct nlist * list = nlistsData.mutableBytes; 90 | bool isThumb = symbol.address & 1; 91 | list[i].n_desc = isThumb ? N_ARM_THUMB_DEF : 0; 92 | list[i].n_type = symbol.type; 93 | list[i].n_sect = symbol.address ? [self n_sectForAddress:symbol.address] : 0; 94 | list[i].n_value = (uint32_t)symbol.address & ~ 1; 95 | list[i].n_un.n_strx = origin_string_table_size + (uint32)symbolNames.length; 96 | 97 | } else { 98 | struct nlist_64 * list = nlistsData.mutableBytes; 99 | list[i].n_desc = 0; 100 | list[i].n_type = symbol.type; 101 | list[i].n_sect = symbol.address ? [self n_sectForAddress:symbol.address] : 0; 102 | list[i].n_value = symbol.address; 103 | list[i].n_un.n_strx = origin_string_table_size + (uint32)symbolNames.length; 104 | } 105 | 106 | [symbolNames appendBytes:symbol.name.UTF8String length:symbol.name.length]; 107 | [symbolNames appendBytes:"\0" length:1]; 108 | } 109 | 110 | 111 | *stringTable = symbolNames; 112 | *symbolTable = nlistsData; 113 | 114 | 115 | } 116 | 117 | - (uint8)n_sectForAddress:(uint64)address{ 118 | 119 | 120 | uint8 n_sect = 0; 121 | 122 | for (id loadCommand in _machOFile.loadCommands) { 123 | if ([loadCommand isKindOfClass:[CDLCSegment class]]){ 124 | CDLCSegment * seg = (CDLCSegment *)loadCommand; 125 | if(![loadCommand containsAddress:address]) { 126 | n_sect += [[seg sections] count]; 127 | } else { 128 | for (CDSection * section in [seg sections]){ 129 | n_sect ++; 130 | if ([section containsAddress:address]) { 131 | return n_sect; 132 | } 133 | 134 | } 135 | 136 | } 137 | } 138 | } 139 | 140 | NSLog(@"Address(%llx) not found in the image", address); 141 | exit(1); 142 | return 1; 143 | } 144 | 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /source/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // class-dump 4 | // 5 | // Created by EugeneYang on 16/8/22. 6 | // 7 | // 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | 18 | #define RESTORE_SYMBOL_BASE_VERSION "1.0 (64 bit)" 19 | 20 | #ifdef DEBUG 21 | #define RESTORE_SYMBOL_VERSION RESTORE_SYMBOL_BASE_VERSION //" (Debug version compiled " __DATE__ " " __TIME__ ")" 22 | #else 23 | #define RESTORE_SYMBOL_VERSION RESTORE_SYMBOL_BASE_VERSION 24 | #endif 25 | 26 | #define RS_OPT_DISABLE_OC_DETECT 1 27 | #define RS_OPT_VERSION 2 28 | #define RS_OPT_REPLACE_RESTRICT 3 29 | 30 | 31 | 32 | void print_usage(void) 33 | { 34 | fprintf(stderr, 35 | "\n" 36 | "restore-symbol %s\n" 37 | "\n" 38 | "Usage: restore-symbol -o [-j ] \n" 39 | "\n" 40 | " where options are:\n" 41 | " -o New mach-o-file path\n" 42 | " --disable-oc-detect Disable auto detect and add oc method into symbol table,\n" 43 | " only add symbol in json file\n" 44 | " --replace-restrict New mach-o-file will replace the LC_SEGMENT(__RESTRICT,__restrict)\n" 45 | " with LC_SEGMENT(__restrict,__restrict) to close dylib inject protection\n" 46 | " -j Json file containing extra symbol info, the key is \"name\",\"address\"\n like this:\n \n" 47 | " [\n {\n \"name\": \"main\", \n \"address\": \"0xXXXXXX\"\n }, \n {\n \"name\": \"-[XXXX XXXXX]\", \n \"address\": \"0xXXXXXX\"\n },\n .... \n ]\n" 48 | 49 | , 50 | RESTORE_SYMBOL_VERSION 51 | ); 52 | } 53 | 54 | 55 | 56 | void restore_symbol(NSString * inpath, NSString *outpath, NSString *jsonPath, bool oc_detect_enable, bool replace_restrict); 57 | 58 | int main(int argc, char * argv[]) { 59 | 60 | 61 | 62 | 63 | bool oc_detect_enable = true; 64 | bool replace_restrict = false; 65 | NSString *inpath = nil; 66 | NSString * outpath = nil; 67 | NSString *jsonPath = nil; 68 | 69 | BOOL shouldPrintVersion = NO; 70 | 71 | int ch; 72 | 73 | struct option longopts[] = { 74 | { "disable-oc-detect", no_argument, NULL, RS_OPT_DISABLE_OC_DETECT }, 75 | { "output", required_argument, NULL, 'o' }, 76 | { "json", required_argument, NULL, 'j' }, 77 | { "version", no_argument, NULL, RS_OPT_VERSION }, 78 | { "replace-restrict", no_argument, NULL, RS_OPT_REPLACE_RESTRICT }, 79 | 80 | { NULL, 0, NULL, 0 }, 81 | 82 | }; 83 | 84 | 85 | if (argc == 1) { 86 | print_usage(); 87 | exit(0); 88 | } 89 | 90 | 91 | 92 | while ( (ch = getopt_long(argc, argv, "o:j:", longopts, NULL)) != -1) { 93 | switch (ch) { 94 | case 'o': 95 | outpath = [NSString stringWithUTF8String:optarg]; 96 | break; 97 | case 'j': 98 | jsonPath = [NSString stringWithUTF8String:optarg]; 99 | break; 100 | 101 | case RS_OPT_VERSION: 102 | shouldPrintVersion = YES; 103 | break; 104 | 105 | case RS_OPT_DISABLE_OC_DETECT: 106 | oc_detect_enable = false; 107 | break; 108 | 109 | case RS_OPT_REPLACE_RESTRICT: 110 | replace_restrict = true; 111 | break; 112 | default: 113 | break; 114 | } 115 | } 116 | 117 | if (shouldPrintVersion) { 118 | printf("restore-symbol %s compiled %s\n", RESTORE_SYMBOL_VERSION, __DATE__ " " __TIME__); 119 | exit(0); 120 | } 121 | 122 | if (optind < argc) { 123 | inpath = [NSString stringWithUTF8String:argv[optind]]; 124 | } 125 | 126 | 127 | restore_symbol(inpath, outpath, jsonPath, oc_detect_enable, replace_restrict); 128 | 129 | } -------------------------------------------------------------------------------- /source/restore-symbol.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // restore-symbol 4 | // 5 | // Created by EugeneYang on 16/8/16. 6 | // 7 | // 8 | 9 | #import 10 | #include 11 | #include 12 | #import "CDFile.h" 13 | #import "CDMachOFile.h" 14 | #import "CDLCSymbolTable.h" 15 | #import "CDLCSegment.h" 16 | #import "CDSymbol.h" 17 | #import "CDLCDynamicSymbolTable.h" 18 | #import "CDLCLinkeditData.h" 19 | #import "CDClassDump.h" 20 | #import "CDFindMethodVisitor.h" 21 | #import "RSScanMethodVisitor.h" 22 | #import "CDFatFile.h" 23 | 24 | 25 | 26 | #define IntSize (Is32Bit? sizeof(uint32_t) : sizeof(uint64_t) ) 27 | #define NListSize (Is32Bit? sizeof(struct nlist) : sizeof(struct nlist_64) ) 28 | 29 | 30 | #define vm_addr_round(v,r) ( (v + (r-1) ) & (-r) ) 31 | 32 | 33 | 34 | 35 | 36 | void restore_symbol(NSString * inpath, NSString *outpath, NSString *jsonPath, bool oc_detect_enable, bool replace_restrict){ 37 | 38 | 39 | 40 | 41 | if (![[NSFileManager defaultManager] fileExistsAtPath:inpath]) { 42 | fprintf(stderr, "Error: Input file doesn't exist!\n"); 43 | exit(1); 44 | } 45 | 46 | if (jsonPath.length != 0 && ![[NSFileManager defaultManager] fileExistsAtPath:jsonPath]) { 47 | fprintf(stderr, "Error: Json file doesn't exist!\n"); 48 | exit(1); 49 | } 50 | 51 | 52 | if ([outpath length] == 0) { 53 | fprintf(stderr, "Error: No output file path!\n"); 54 | exit(1); 55 | } 56 | 57 | if ([[NSFileManager defaultManager] fileExistsAtPath:outpath]) { 58 | fprintf(stderr, "Error: Output file has exist!\n"); 59 | exit(1); 60 | } 61 | 62 | fprintf(stderr, "=========== Start =============\n"); 63 | 64 | 65 | NSMutableData * outData = [[NSMutableData alloc] initWithContentsOfFile:inpath]; 66 | 67 | CDFile * ofile = [CDFile fileWithContentsOfFile:inpath searchPathState:nil]; 68 | 69 | if ([ofile isKindOfClass:[CDFatFile class]] ) { 70 | fprintf(stderr,"Restore-symbol supports armv7 and arm64 archtecture, but not support fat file. Please use lipo to thin the image file first."); 71 | exit(1); 72 | } 73 | 74 | 75 | CDMachOFile * machOFile = (CDMachOFile *)ofile; 76 | const bool Is32Bit = ! machOFile.uses64BitABI; 77 | 78 | 79 | RSSymbolCollector *collector = [RSSymbolCollector new]; 80 | collector.machOFile = machOFile; 81 | 82 | if (oc_detect_enable) { 83 | fprintf(stderr, "Scan OC method in mach-o-file.\n"); 84 | 85 | CDClassDump *classDump = [[CDClassDump alloc] init]; 86 | CDArch targetArch; 87 | if ([machOFile bestMatchForLocalArch:&targetArch] == NO) { 88 | fprintf(stderr, "Error: Couldn't get local architecture!\n"); 89 | exit(1); 90 | } 91 | classDump.targetArch = targetArch; 92 | [classDump processObjectiveCData]; 93 | [classDump registerTypes]; 94 | 95 | NSError *error; 96 | if (![classDump loadFile:machOFile error:&error]) { 97 | fprintf(stderr, "Error: %s\n", [[error localizedFailureReason] UTF8String]); 98 | exit(1); 99 | } else { 100 | [classDump processObjectiveCData]; 101 | [classDump registerTypes]; 102 | 103 | RSScanMethodVisitor *visitor = [[RSScanMethodVisitor alloc] initWithSymbolCollector:collector]; 104 | visitor.classDump = classDump; 105 | [classDump recursivelyVisit:visitor]; 106 | 107 | } 108 | 109 | fprintf(stderr, "Scan OC method finish.\n"); 110 | } 111 | fprintf(stderr,"restore %d symbols\n", collector.symbols.count); 112 | 113 | 114 | if (jsonPath != nil && jsonPath.length != 0) { 115 | fprintf(stderr, "Parse symbols in json file.\n"); 116 | NSData * jsonData = [NSData dataWithContentsOfFile:jsonPath]; 117 | if (jsonData == nil) { 118 | fprintf(stderr, "Can't load json data.\n"); 119 | exit(1); 120 | } 121 | NSArray *jsonSymbols = [RSSymbol symbolsWithJson:jsonData]; 122 | if (jsonSymbols == nil) { 123 | fprintf(stderr,"Error: Json file cann't parse!"); 124 | exit(1); 125 | } else { 126 | [collector addSymbols:jsonSymbols]; 127 | } 128 | fprintf(stderr, "Parse finish.\n"); 129 | } 130 | 131 | 132 | NSData *string_table_append_data = nil; 133 | NSData *symbol_table_append_data = nil; 134 | [collector generateAppendStringTable:&string_table_append_data appendSymbolTable:&symbol_table_append_data]; 135 | 136 | 137 | uint32 increase_symbol_num = (uint32)collector.symbols.count; 138 | uint32 increase_size_string_tab = (uint32)string_table_append_data.length; 139 | uint32 increase_size_symtab = (uint32)symbol_table_append_data.length; 140 | uint32 increase_size_all_without_padding = increase_size_symtab + increase_size_string_tab; 141 | 142 | 143 | 144 | uint32 origin_string_table_offset = machOFile.symbolTable.stroff; 145 | uint32 origin_string_table_size = machOFile.symbolTable.strsize; 146 | uint32 origin_symbol_table_offset = machOFile.symbolTable.symoff; 147 | uint32 origin_symbol_table_num = machOFile.symbolTable.nsyms; 148 | 149 | uint32 origin_dysymbol_table_locsymbol_num = machOFile.dynamicSymbolTable.dysymtab.nlocalsym; 150 | 151 | 152 | if (replace_restrict){ 153 | CDLCSegment * restrict_seg = [machOFile segmentWithName:@"__RESTRICT"]; 154 | 155 | struct segment_command *restrict_seg_cmd = (struct segment_command *)((char *)outData.mutableBytes + restrict_seg.commandOffset); 156 | struct section *restrict_section = NULL; 157 | 158 | int cmd_size = (Is32Bit? sizeof(struct segment_command) : sizeof(struct segment_command_64)); 159 | if (restrict_seg.cmdsize > cmd_size) { 160 | restrict_section = (struct section *)((char *)outData.mutableBytes + restrict_seg.commandOffset + cmd_size); 161 | } 162 | 163 | if (restrict_seg && restrict_section) { 164 | fprintf(stderr, "rename segment __RESTRICT in mach-o header.\n"); 165 | strncpy(restrict_seg_cmd -> segname, "__restrict", 16); 166 | strncpy(restrict_section -> segname, "__restrict", 16); 167 | } else { 168 | fprintf(stderr, "No section (__RESTRICT,__restrict) in mach-o header.\n"); 169 | } 170 | 171 | } 172 | 173 | //LC_CODE_SIGNATURE need align 16 byte, so add padding at end of string table. 174 | uint32 string_table_padding = 0; 175 | { 176 | CDLCLinkeditData * codesignature = nil; 177 | for (CDLoadCommand *command in machOFile.loadCommands) { 178 | if (command.cmd == LC_CODE_SIGNATURE){ 179 | codesignature = (CDLCLinkeditData *)command; 180 | } 181 | } 182 | 183 | if (codesignature) { 184 | struct linkedit_data_command *command = (struct linkedit_data_command *)((char *)outData.mutableBytes + codesignature.commandOffset); 185 | uint32_t tmp_offset = command -> dataoff + increase_size_all_without_padding; 186 | uint32_t final_offset = vm_addr_round(tmp_offset, 16); 187 | 188 | string_table_padding = final_offset - tmp_offset; 189 | command -> dataoff = final_offset; 190 | } 191 | } 192 | 193 | 194 | { 195 | CDLCSymbolTable *symtab = [machOFile symbolTable]; 196 | struct symtab_command *symtab_out = (struct symtab_command *)((char *)outData.mutableBytes + symtab.commandOffset); 197 | symtab_out -> nsyms += increase_symbol_num; 198 | symtab_out -> stroff += increase_size_symtab; 199 | symtab_out -> strsize += increase_size_string_tab + string_table_padding; 200 | } 201 | 202 | { 203 | CDLCDynamicSymbolTable *dysymtabCommand = [machOFile dynamicSymbolTable]; 204 | struct dysymtab_command *command = (struct dysymtab_command *)((char *)outData.mutableBytes + dysymtabCommand.commandOffset); 205 | command -> nlocalsym += collector.locSymbolSize; 206 | command -> iextdefsym += collector.locSymbolSize; 207 | command -> nextdefsym += collector.extSymbolSize; 208 | command -> iundefsym += collector.locSymbolSize + collector.extSymbolSize; 209 | command -> indirectsymoff += increase_size_symtab; 210 | } 211 | 212 | 213 | 214 | { 215 | CDLCSegment * linkeditSegment = [machOFile segmentWithName:@"__LINKEDIT"]; 216 | if (Is32Bit) { 217 | struct segment_command *linkedit_segment_command = (struct segment_command *)((char *)outData.mutableBytes + linkeditSegment.commandOffset); 218 | linkedit_segment_command -> filesize += increase_size_all_without_padding + string_table_padding; 219 | linkedit_segment_command -> vmsize = (uint32) vm_addr_round((linkedit_segment_command -> filesize), 0x4000); 220 | 221 | 222 | } else { 223 | 224 | struct segment_command_64 *linkedit_segment_command = (struct segment_command_64 *)((char *)outData.mutableBytes + linkeditSegment.commandOffset); 225 | linkedit_segment_command -> filesize += increase_size_all_without_padding + string_table_padding; 226 | linkedit_segment_command -> vmsize = vm_addr_round((linkedit_segment_command -> filesize), 0x4000); 227 | } 228 | } 229 | 230 | 231 | 232 | 233 | // must first insert string 234 | [outData replaceBytesInRange:NSMakeRange(origin_string_table_offset + origin_string_table_size , 0) withBytes:(const void *)string_table_append_data.bytes length:increase_size_string_tab + string_table_padding]; 235 | 236 | [outData replaceBytesInRange:NSMakeRange(origin_symbol_table_offset + origin_symbol_table_num * NListSize , 0) withBytes:(const void *)symbol_table_append_data.bytes length:increase_size_symtab]; 237 | 238 | NSError * err = nil; 239 | [outData writeToFile:outpath options:NSDataWritingWithoutOverwriting error:&err]; 240 | 241 | if (!err) { 242 | chmod(outpath.UTF8String, 0755); 243 | }else{ 244 | fprintf(stderr,"Write file error : %s", [err localizedDescription].UTF8String); 245 | return; 246 | } 247 | 248 | 249 | fprintf(stderr,"=========== Finish ============\n"); 250 | 251 | } 252 | -------------------------------------------------------------------------------- /source/restore-symbol.pch: -------------------------------------------------------------------------------- 1 | // 2 | // restore-symbol.pch 3 | // restore-symbol 4 | // 5 | // Created by EugeneYang on 16/8/23. 6 | // Copyright © 2016年 Jun. All rights reserved. 7 | // 8 | 9 | #ifndef restore_symbol_pch 10 | #define restore_symbol_pch 11 | 12 | // Include any system framework and library headers here that should be included in all compilation units. 13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 14 | #include 15 | #import "CDExtensions.h" 16 | #define __cmd __PRETTY_FUNCTION__ 17 | #endif /* restore_symbol_pch */ 18 | --------------------------------------------------------------------------------