├── .gitignore ├── Demo ├── SwiftDump ├── result.txt ├── test └── test.swift ├── Doc ├── img_demo_result.jpg └── macho.jpg ├── LICENSE ├── README.md ├── README_zh.md └── SwiftDump ├── SwiftDump.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ └── SwiftDump.xcscheme └── SwiftDump ├── Consts.swift ├── MachO ├── Data+Extensions.swift ├── Load Commands │ ├── RPath.swift │ ├── Section64.swift │ └── Segment.swift ├── LoadCommand.swift ├── MachOFatHeader.swift ├── MachOFile.swift ├── MachOHeader.swift ├── MachOParser.swift └── String+Extensions.swift ├── SDFileLoader.swift ├── SDNominalObj.swift ├── SDObjCClass.swift ├── SDParser.swift ├── SDPointer.swift ├── SDProtocolObj.swift ├── Util ├── ContextDescriptor.swift ├── Ext.swift ├── Log.swift └── RuntimeBridge.swift └── main.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | SwiftDump/SwiftDump.xcodeproj/project.xcworkspace/xcuserdata/* 3 | SwiftDump/SwiftDump.xcodeproj/xcuserdata/* 4 | 5 | 6 | -------------------------------------------------------------------------------- /Demo/SwiftDump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neil-wu/SwiftDump/96d30335a777ee5c3680338ca19552744210ffd3/Demo/SwiftDump -------------------------------------------------------------------------------- /Demo/result.txt: -------------------------------------------------------------------------------- 1 | enum MyEnum { 2 | // <0x52, enum, isUnique, version 0, kindSpecificFlags 0x0> 3 | // Access Function at 0x21c0 4 | case red 5 | case blue 6 | case yellow 7 | } 8 | 9 | struct BaseStruct { 10 | // <0x51, struct, isUnique, version 0, kindSpecificFlags 0x0> 11 | // Access Function at 0x25a0 12 | let bbname: String; 13 | } 14 | 15 | struct MyStruct { 16 | // <0x51, struct, isUnique, version 0, kindSpecificFlags 0x0> 17 | // Access Function at 0x29b0 18 | let sid: Int; 19 | let sname: String; 20 | } 21 | 22 | class BaseClass { 23 | // <0x80000050, class, isUnique, version 0, kindSpecificFlags 0x8000> 24 | // Access Function at 0x29c0 25 | let bcname: String; 26 | } 27 | 28 | class MyClass : BaseClass { 29 | // <0x40000050, class, isUnique, version 0, kindSpecificFlags 0x4000> 30 | // Access Function at 0x2a00 31 | let cid: Int; 32 | let cname: String; 33 | let st: MyStruct?; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Demo/test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neil-wu/SwiftDump/96d30335a777ee5c3680338ca19552744210ffd3/Demo/test -------------------------------------------------------------------------------- /Demo/test.swift: -------------------------------------------------------------------------------- 1 | enum MyEnum { 2 | case red 3 | case blue 4 | case yellow 5 | } 6 | 7 | struct BaseStruct { 8 | var bbname: String = "BaseStruct" 9 | } 10 | 11 | 12 | struct MyStruct { 13 | var sid: Int = 123; 14 | var sname: String = "hello" 15 | } 16 | 17 | 18 | class BaseClass { 19 | var bcname: String = "BaseClass" 20 | } 21 | 22 | 23 | final class MyClass : BaseClass { 24 | 25 | var cid: Int = 456; 26 | var cname: String = "world" 27 | var st: MyStruct? = nil; 28 | } 29 | -------------------------------------------------------------------------------- /Doc/img_demo_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neil-wu/SwiftDump/96d30335a777ee5c3680338ca19552744210ffd3/Doc/img_demo_result.jpg -------------------------------------------------------------------------------- /Doc/macho.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neil-wu/SwiftDump/96d30335a777ee5c3680338ca19552744210ffd3/Doc/macho.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2020 neilwu (https://github.com/neil-wu) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | 25 | 1. Machismo 26 | Apache License 27 | Version 2.0, January 2004 28 | http://www.apache.org/licenses/ 29 | 30 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 31 | 32 | 1. Definitions. 33 | 34 | "License" shall mean the terms and conditions for use, reproduction, 35 | and distribution as defined by Sections 1 through 9 of this document. 36 | 37 | "Licensor" shall mean the copyright owner or entity authorized by 38 | the copyright owner that is granting the License. 39 | 40 | "Legal Entity" shall mean the union of the acting entity and all 41 | other entities that control, are controlled by, or are under common 42 | control with that entity. For the purposes of this definition, 43 | "control" means (i) the power, direct or indirect, to cause the 44 | direction or management of such entity, whether by contract or 45 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 46 | outstanding shares, or (iii) beneficial ownership of such entity. 47 | 48 | "You" (or "Your") shall mean an individual or Legal Entity 49 | exercising permissions granted by this License. 50 | 51 | "Source" form shall mean the preferred form for making modifications, 52 | including but not limited to software source code, documentation 53 | source, and configuration files. 54 | 55 | "Object" form shall mean any form resulting from mechanical 56 | transformation or translation of a Source form, including but 57 | not limited to compiled object code, generated documentation, 58 | and conversions to other media types. 59 | 60 | "Work" shall mean the work of authorship, whether in Source or 61 | Object form, made available under the License, as indicated by a 62 | copyright notice that is included in or attached to the work 63 | (an example is provided in the Appendix below). 64 | 65 | "Derivative Works" shall mean any work, whether in Source or Object 66 | form, that is based on (or derived from) the Work and for which the 67 | editorial revisions, annotations, elaborations, or other modifications 68 | represent, as a whole, an original work of authorship. For the purposes 69 | of this License, Derivative Works shall not include works that remain 70 | separable from, or merely link (or bind by name) to the interfaces of, 71 | the Work and Derivative Works thereof. 72 | 73 | "Contribution" shall mean any work of authorship, including 74 | the original version of the Work and any modifications or additions 75 | to that Work or Derivative Works thereof, that is intentionally 76 | submitted to Licensor for inclusion in the Work by the copyright owner 77 | or by an individual or Legal Entity authorized to submit on behalf of 78 | the copyright owner. For the purposes of this definition, "submitted" 79 | means any form of electronic, verbal, or written communication sent 80 | to the Licensor or its representatives, including but not limited to 81 | communication on electronic mailing lists, source code control systems, 82 | and issue tracking systems that are managed by, or on behalf of, the 83 | Licensor for the purpose of discussing and improving the Work, but 84 | excluding communication that is conspicuously marked or otherwise 85 | designated in writing by the copyright owner as "Not a Contribution." 86 | 87 | "Contributor" shall mean Licensor and any individual or Legal Entity 88 | on behalf of whom a Contribution has been received by Licensor and 89 | subsequently incorporated within the Work. 90 | 91 | 2. Grant of Copyright License. Subject to the terms and conditions of 92 | this License, each Contributor hereby grants to You a perpetual, 93 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 94 | copyright license to reproduce, prepare Derivative Works of, 95 | publicly display, publicly perform, sublicense, and distribute the 96 | Work and such Derivative Works in Source or Object form. 97 | 98 | 3. Grant of Patent License. Subject to the terms and conditions of 99 | this License, each Contributor hereby grants to You a perpetual, 100 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 101 | (except as stated in this section) patent license to make, have made, 102 | use, offer to sell, sell, import, and otherwise transfer the Work, 103 | where such license applies only to those patent claims licensable 104 | by such Contributor that are necessarily infringed by their 105 | Contribution(s) alone or by combination of their Contribution(s) 106 | with the Work to which such Contribution(s) was submitted. If You 107 | institute patent litigation against any entity (including a 108 | cross-claim or counterclaim in a lawsuit) alleging that the Work 109 | or a Contribution incorporated within the Work constitutes direct 110 | or contributory patent infringement, then any patent licenses 111 | granted to You under this License for that Work shall terminate 112 | as of the date such litigation is filed. 113 | 114 | 4. Redistribution. You may reproduce and distribute copies of the 115 | Work or Derivative Works thereof in any medium, with or without 116 | modifications, and in Source or Object form, provided that You 117 | meet the following conditions: 118 | 119 | (a) You must give any other recipients of the Work or 120 | Derivative Works a copy of this License; and 121 | 122 | (b) You must cause any modified files to carry prominent notices 123 | stating that You changed the files; and 124 | 125 | (c) You must retain, in the Source form of any Derivative Works 126 | that You distribute, all copyright, patent, trademark, and 127 | attribution notices from the Source form of the Work, 128 | excluding those notices that do not pertain to any part of 129 | the Derivative Works; and 130 | 131 | (d) If the Work includes a "NOTICE" text file as part of its 132 | distribution, then any Derivative Works that You distribute must 133 | include a readable copy of the attribution notices contained 134 | within such NOTICE file, excluding those notices that do not 135 | pertain to any part of the Derivative Works, in at least one 136 | of the following places: within a NOTICE text file distributed 137 | as part of the Derivative Works; within the Source form or 138 | documentation, if provided along with the Derivative Works; or, 139 | within a display generated by the Derivative Works, if and 140 | wherever such third-party notices normally appear. The contents 141 | of the NOTICE file are for informational purposes only and 142 | do not modify the License. You may add Your own attribution 143 | notices within Derivative Works that You distribute, alongside 144 | or as an addendum to the NOTICE text from the Work, provided 145 | that such additional attribution notices cannot be construed 146 | as modifying the License. 147 | 148 | You may add Your own copyright statement to Your modifications and 149 | may provide additional or different license terms and conditions 150 | for use, reproduction, or distribution of Your modifications, or 151 | for any such Derivative Works as a whole, provided Your use, 152 | reproduction, and distribution of the Work otherwise complies with 153 | the conditions stated in this License. 154 | 155 | 5. Submission of Contributions. Unless You explicitly state otherwise, 156 | any Contribution intentionally submitted for inclusion in the Work 157 | by You to the Licensor shall be under the terms and conditions of 158 | this License, without any additional terms or conditions. 159 | Notwithstanding the above, nothing herein shall supersede or modify 160 | the terms of any separate license agreement you may have executed 161 | with Licensor regarding such Contributions. 162 | 163 | 6. Trademarks. This License does not grant permission to use the trade 164 | names, trademarks, service marks, or product names of the Licensor, 165 | except as required for reasonable and customary use in describing the 166 | origin of the Work and reproducing the content of the NOTICE file. 167 | 168 | 7. Disclaimer of Warranty. Unless required by applicable law or 169 | agreed to in writing, Licensor provides the Work (and each 170 | Contributor provides its Contributions) on an "AS IS" BASIS, 171 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 172 | implied, including, without limitation, any warranties or conditions 173 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 174 | PARTICULAR PURPOSE. You are solely responsible for determining the 175 | appropriateness of using or redistributing the Work and assume any 176 | risks associated with Your exercise of permissions under this License. 177 | 178 | 8. Limitation of Liability. In no event and under no legal theory, 179 | whether in tort (including negligence), contract, or otherwise, 180 | unless required by applicable law (such as deliberate and grossly 181 | negligent acts) or agreed to in writing, shall any Contributor be 182 | liable to You for damages, including any direct, indirect, special, 183 | incidental, or consequential damages of any character arising as a 184 | result of this License or out of the use or inability to use the 185 | Work (including but not limited to damages for loss of goodwill, 186 | work stoppage, computer failure or malfunction, or any and all 187 | other commercial damages or losses), even if such Contributor 188 | has been advised of the possibility of such damages. 189 | 190 | 9. Accepting Warranty or Additional Liability. While redistributing 191 | the Work or Derivative Works thereof, You may choose to offer, 192 | and charge a fee for, acceptance of support, warranty, indemnity, 193 | or other liability obligations and/or rights consistent with this 194 | License. However, in accepting such obligations, You may act only 195 | on Your own behalf and on Your sole responsibility, not on behalf 196 | of any other Contributor, and only if You agree to indemnify, 197 | defend, and hold each Contributor harmless for any liability 198 | incurred by, or claims asserted against, such Contributor by reason 199 | of your accepting any such warranty or additional liability. 200 | 201 | END OF TERMS AND CONDITIONS 202 | 203 | APPENDIX: How to apply the Apache License to your work. 204 | 205 | To apply the Apache License to your work, attach the following 206 | boilerplate notice, with the fields enclosed by brackets "[]" 207 | replaced with your own identifying information. (Don't include 208 | the brackets!) The text should be enclosed in the appropriate 209 | comment syntax for the file format. We also recommend that a 210 | file or class name and description of purpose be included on the 211 | same "printed page" as the copyright notice for easier 212 | identification within third-party archives. 213 | 214 | Copyright [yyyy] [name of copyright owner] 215 | 216 | Licensed under the Apache License, Version 2.0 (the "License"); 217 | you may not use this file except in compliance with the License. 218 | You may obtain a copy of the License at 219 | 220 | http://www.apache.org/licenses/LICENSE-2.0 221 | 222 | Unless required by applicable law or agreed to in writing, software 223 | distributed under the License is distributed on an "AS IS" BASIS, 224 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 225 | See the License for the specific language governing permissions and 226 | limitations under the License. 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | #### SwiftDump 3 | 4 | ##### [中文文档](./README_zh.md) 5 | 6 | SwiftDump is a command-line tool for retriving the Swift Object info from Mach-O file. Similar to [class-dump](https://github.com/nygard/class-dump/), but the difference is that SwiftDump focus on swift 5 objects. For Mach-O files mixed with Objective-C and swift, you can combine class-dump with SwiftDump. 7 | 8 | There is alos a [Frida](https://www.frida.re/) version named [FridaSwiftDump](https://github.com/neil-wu/FridaSwiftDump/). 9 | 10 | You can either use`SwiftDump` for a Mach-O file or `FridaSwiftDump` for a foreground running app. 11 | 12 | If you are curious about the Mach-O format, check the image at the bottom of this article. 13 | 14 | ![demo](./Doc/img_demo_result.jpg) 15 | 16 | #### Usage 17 | 18 | ``` Text 19 | USAGE: SwiftDump [--debug] [--arch ] [--version] 20 | 21 | ARGUMENTS: 22 | MachO File 23 | 24 | OPTIONS: 25 | -d, --debug Show debug log. 26 | -a, --arch Choose architecture from a fat binary (only support x86_64/arm64). 27 | (default: arm64) 28 | -v, --version Version 29 | -h, --help Show help information. 30 | ``` 31 | 32 | * SwiftDump ./TestMachO > result.txt 33 | * SwiftDump -a x86_64 ./TestMachO > result.txt 34 | 35 | #### Features 36 | 37 | * Written entirely in swift, the project is tiny 38 | * Dump swift 5 struct/class/enum/protocol 39 | * Parse enum with payload case 40 | * Support inheritance and protocol 41 | * Since it is written in swift, the mangled names are demangled by swift's runtime function, such as `swift_getTypeByMangledNameInContext` and `swift_demangle_getDemangledName`. 42 | 43 | Thanks to the runtime function, SwiftDump can demangle complex type, such as RxSwift variable. For example, 44 | `RxSwift.Queue<(eventTime: Foundation.Date, event: RxSwift.Event)>` 45 | 46 | #### TODO 47 | 48 | * Parse swift function address 49 | * More 50 | 51 | #### Compile 52 | 53 | 1. Clone the repo 54 | 2. Open SwiftDump.xcodeproj with Xcode 55 | 3. Modify 'Signing & Capabilities' to use your own id 56 | 4. Build & Run 57 | 58 | The default Mach-O file path is `Demo/test`, you can change it in `Xcode - Product - Scheme - Edit Scheme - Arguments` 59 | 60 | (Tested on Xcode Version 11.5 (11E608c), MacOS 10.15.5) 61 | 62 | #### Credit 63 | 64 | * [Machismo](https://github.com/g-Off/Machismo) : Parsing of Mach-O binaries using swift. 65 | * [swift-argument-parser](https://github.com/apple/swift-argument-parser) : Straightforward, type-safe argument parsing for Swift. 66 | * [Swift metadata](https://knight.sc/reverse%20engineering/2019/07/17/swift-metadata.html) : High level description of all the Swift 5 sections that can show up in a Swift binary. 67 | 68 | 69 | #### License 70 | 71 | MIT 72 | 73 | 74 | #### Mach-O File Format 75 | 76 | The following image shows how SwiftDump parse swift types from file `Demo/test`. You can open this file with [MachOView](https://github.com/gdbinit/MachOView). 77 | 78 | ![demo](./Doc/macho.jpg) 79 | 80 | 81 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | 2 | #### SwiftDump 3 | 4 | SwiftDump是从Mach-O文件中获取swift对象定义的命令行工具,类似大家都用过的OC类dump工具[class-dump](https://github.com/nygard/class-dump/),SwiftDump专注于处理swift对象(当前只支持swift 5)。对于采用OC/Swift混编的Mach-O文件,你可以将 class-dump 和 SwiftDump结合起来使用。 5 | 6 | 同时,我在[Frida](https://www.frida.re/)中实现了一个简单版本 [FridaSwiftDump](https://github.com/neil-wu/FridaSwiftDump/)。 7 | 8 | 你可以根据需要选择使用,`SwiftDump`可以解析处理Mach-O文件,而`FridaSwiftDump`可以对一个前台运行的app进行解析。 9 | 10 | 如果你对解析Mach-O的过程感兴趣,请查看该文档最后的配图。 11 | 12 | ![demo](./Doc/img_demo_result.jpg) 13 | 14 | #### 用法 15 | 16 | ``` Text 17 | USAGE: SwiftDump [--debug] [--arch ] [--version] 18 | 19 | ARGUMENTS: 20 | MachO File 21 | 22 | OPTIONS: 23 | -d, --debug Show debug log. 24 | -a, --arch Choose architecture from a fat binary (only support x86_64/arm64). 25 | (default: arm64) 26 | -v, --version Version 27 | -h, --help Show help information. 28 | ``` 29 | 30 | * SwiftDump ./TestMachO > result.txt 31 | * SwiftDump -a x86_64 ./TestMachO > result.txt 32 | 33 | #### 特点 34 | 35 | * 完全使用swift编写,项目小巧 36 | * 支持 dump swift 5 的 struct/class/enum/protocol 37 | * 支持解析 enum with payload case 38 | * 支持解析 swift类继承 和 protocol 39 | * 由于采用swift编写,所以借助于swift的运行时函数来还原修饰符(demangle) 比如,`swift_getTypeByMangledNameInContext` 和 `swift_demangle_getDemangledName` 40 | 41 | 受益于swift运行时函数, SwiftDump可以还原复杂的数据类型, 比如某个使用RxSwift声明的变量类型能达到如下的解析效果: 42 | `RxSwift.Queue<(eventTime: Foundation.Date, event: RxSwift.Event)>` 43 | 44 | #### TODO 45 | 46 | * 考虑添加导出函数地址 47 | * 待定 48 | 49 | #### Compile 50 | 51 | 1. Clone the repo 52 | 2. Open SwiftDump.xcodeproj with Xcode 53 | 3. Modify 'Signing & Capabilities' to use your own id 54 | 4. Build & Run 55 | 56 | 默认输入参数使用目录`Demo/test`的Mach-O文件, 你可以在Xcode里修改输入参数: `Xcode - Product - Scheme - Edit Scheme - Arguments` 57 | 58 | (Xcode Version 11.5 (11E608c), MacOS 10.15.5 测试通过) 59 | 60 | #### 感谢 61 | 62 | * [Machismo](https://github.com/g-Off/Machismo) : 使用swift来读取Mach-O文件 63 | * [swift-argument-parser](https://github.com/apple/swift-argument-parser) : 解析命令行参数 64 | * [Swift metadata](https://knight.sc/reverse%20engineering/2019/07/17/swift-metadata.html) : High level description of all the Swift 5 sections that can show up in a Swift binary. 65 | 66 | 67 | #### License 68 | 69 | MIT 70 | 71 | 72 | #### Mach-O File Format 73 | 74 | 下图展示了 SwiftDump 是如何从测试文件 `Demo/test` 解析 swift 类型的,你可以使用 [MachOView](https://github.com/gdbinit/MachOView) 打开这个测试文件,对照下图查看。 75 | 76 | ![demo](./Doc/macho.jpg) 77 | 78 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6119CF3124A89DCC00CA153D /* SDObjCClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119CF3024A89DCC00CA153D /* SDObjCClass.swift */; }; 11 | 6119CF3424A99D3500CA153D /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 6119CF3324A99D3500CA153D /* ArgumentParser */; }; 12 | 6119D4E724A9C3C300CA153D /* Segment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4D224A9C3C300CA153D /* Segment.swift */; }; 13 | 6119D4EA24A9C3C300CA153D /* MachOFatHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4D524A9C3C300CA153D /* MachOFatHeader.swift */; }; 14 | 6119D4EC24A9C3C300CA153D /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4D824A9C3C300CA153D /* String+Extensions.swift */; }; 15 | 6119D4ED24A9C3C300CA153D /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4D924A9C3C300CA153D /* Data+Extensions.swift */; }; 16 | 6119D4EE24A9C3C300CA153D /* MachOHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4DA24A9C3C300CA153D /* MachOHeader.swift */; }; 17 | 6119D4EF24A9C3C300CA153D /* LoadCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4DB24A9C3C300CA153D /* LoadCommand.swift */; }; 18 | 6119D4F424A9C3C300CA153D /* MachOParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4E224A9C3C300CA153D /* MachOParser.swift */; }; 19 | 6119D4F524A9C3C300CA153D /* MachOFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4E324A9C3C300CA153D /* MachOFile.swift */; }; 20 | 6119D4F724A9C5E900CA153D /* SDFileLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D4F624A9C5E900CA153D /* SDFileLoader.swift */; }; 21 | 6119D50B24A9E24F00CA153D /* Section64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D50A24A9E24F00CA153D /* Section64.swift */; }; 22 | 6119D51824AB271F00CA153D /* SDPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6119D51724AB271F00CA153D /* SDPointer.swift */; }; 23 | 616EC6C3249D9CE700B6D6F1 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6C2249D9CE700B6D6F1 /* main.swift */; }; 24 | 616EC6CA249D9D6000B6D6F1 /* RuntimeBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6C9249D9D6000B6D6F1 /* RuntimeBridge.swift */; }; 25 | 616EC6CC249D9DD000B6D6F1 /* Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6CB249D9DD000B6D6F1 /* Ext.swift */; }; 26 | 616EC6CE249D9FD000B6D6F1 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6CD249D9FD000B6D6F1 /* Log.swift */; }; 27 | 616EC6D6249DAB4400B6D6F1 /* Consts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6D5249DAB4400B6D6F1 /* Consts.swift */; }; 28 | 616EC6D8249DABA000B6D6F1 /* SDParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6D7249DABA000B6D6F1 /* SDParser.swift */; }; 29 | 616EC6DF249E040400B6D6F1 /* SDNominalObj.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6DE249E040400B6D6F1 /* SDNominalObj.swift */; }; 30 | 616EC6E524A04E8600B6D6F1 /* libswiftDemangle.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 616EC6E424A04E8600B6D6F1 /* libswiftDemangle.tbd */; }; 31 | 616EC6E724A2E57700B6D6F1 /* ContextDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6E624A2E57700B6D6F1 /* ContextDescriptor.swift */; }; 32 | 616EC6E924A31F7900B6D6F1 /* SDProtocolObj.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616EC6E824A31F7900B6D6F1 /* SDProtocolObj.swift */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXCopyFilesBuildPhase section */ 36 | 616EC6BD249D9CE700B6D6F1 /* CopyFiles */ = { 37 | isa = PBXCopyFilesBuildPhase; 38 | buildActionMask = 12; 39 | dstPath = ""; 40 | dstSubfolderSpec = 10; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXCopyFilesBuildPhase section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 6119CF3024A89DCC00CA153D /* SDObjCClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDObjCClass.swift; sourceTree = ""; }; 49 | 6119D4D224A9C3C300CA153D /* Segment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Segment.swift; sourceTree = ""; }; 50 | 6119D4D524A9C3C300CA153D /* MachOFatHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachOFatHeader.swift; sourceTree = ""; }; 51 | 6119D4D824A9C3C300CA153D /* String+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; 52 | 6119D4D924A9C3C300CA153D /* Data+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = ""; }; 53 | 6119D4DA24A9C3C300CA153D /* MachOHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachOHeader.swift; sourceTree = ""; }; 54 | 6119D4DB24A9C3C300CA153D /* LoadCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadCommand.swift; sourceTree = ""; }; 55 | 6119D4E224A9C3C300CA153D /* MachOParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachOParser.swift; sourceTree = ""; }; 56 | 6119D4E324A9C3C300CA153D /* MachOFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachOFile.swift; sourceTree = ""; }; 57 | 6119D4F624A9C5E900CA153D /* SDFileLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDFileLoader.swift; sourceTree = ""; }; 58 | 6119D50A24A9E24F00CA153D /* Section64.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section64.swift; sourceTree = ""; }; 59 | 6119D51724AB271F00CA153D /* SDPointer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDPointer.swift; sourceTree = ""; }; 60 | 616EC6BF249D9CE700B6D6F1 /* SwiftDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SwiftDump; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 616EC6C2249D9CE700B6D6F1 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 62 | 616EC6C9249D9D6000B6D6F1 /* RuntimeBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeBridge.swift; sourceTree = ""; }; 63 | 616EC6CB249D9DD000B6D6F1 /* Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ext.swift; sourceTree = ""; }; 64 | 616EC6CD249D9FD000B6D6F1 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; 65 | 616EC6D5249DAB4400B6D6F1 /* Consts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consts.swift; sourceTree = ""; }; 66 | 616EC6D7249DABA000B6D6F1 /* SDParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDParser.swift; sourceTree = ""; }; 67 | 616EC6DE249E040400B6D6F1 /* SDNominalObj.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDNominalObj.swift; sourceTree = ""; }; 68 | 616EC6E424A04E8600B6D6F1 /* libswiftDemangle.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftDemangle.tbd; path = usr/lib/swift/libswiftDemangle.tbd; sourceTree = SDKROOT; }; 69 | 616EC6E624A2E57700B6D6F1 /* ContextDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextDescriptor.swift; sourceTree = ""; }; 70 | 616EC6E824A31F7900B6D6F1 /* SDProtocolObj.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDProtocolObj.swift; sourceTree = ""; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 616EC6BC249D9CE700B6D6F1 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | 6119CF3424A99D3500CA153D /* ArgumentParser in Frameworks */, 79 | 616EC6E524A04E8600B6D6F1 /* libswiftDemangle.tbd in Frameworks */, 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 6119D4CD24A9C3C300CA153D /* MachO */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 6119D4D924A9C3C300CA153D /* Data+Extensions.swift */, 90 | 6119D4CF24A9C3C300CA153D /* Load Commands */, 91 | 6119D4DB24A9C3C300CA153D /* LoadCommand.swift */, 92 | 6119D4D524A9C3C300CA153D /* MachOFatHeader.swift */, 93 | 6119D4E324A9C3C300CA153D /* MachOFile.swift */, 94 | 6119D4DA24A9C3C300CA153D /* MachOHeader.swift */, 95 | 6119D4E224A9C3C300CA153D /* MachOParser.swift */, 96 | 6119D4D824A9C3C300CA153D /* String+Extensions.swift */, 97 | ); 98 | path = MachO; 99 | sourceTree = ""; 100 | }; 101 | 6119D4CF24A9C3C300CA153D /* Load Commands */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 6119D4D224A9C3C300CA153D /* Segment.swift */, 105 | 6119D50A24A9E24F00CA153D /* Section64.swift */, 106 | ); 107 | path = "Load Commands"; 108 | sourceTree = ""; 109 | }; 110 | 616EC6B6249D9CE700B6D6F1 = { 111 | isa = PBXGroup; 112 | children = ( 113 | 616EC6C1249D9CE700B6D6F1 /* SwiftDump */, 114 | 616EC6C0249D9CE700B6D6F1 /* Products */, 115 | 616EC6E324A04E8500B6D6F1 /* Frameworks */, 116 | ); 117 | sourceTree = ""; 118 | }; 119 | 616EC6C0249D9CE700B6D6F1 /* Products */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 616EC6BF249D9CE700B6D6F1 /* SwiftDump */, 123 | ); 124 | name = Products; 125 | sourceTree = ""; 126 | }; 127 | 616EC6C1249D9CE700B6D6F1 /* SwiftDump */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 6119D4CD24A9C3C300CA153D /* MachO */, 131 | 616EC6CF249DA08900B6D6F1 /* Util */, 132 | 616EC6C2249D9CE700B6D6F1 /* main.swift */, 133 | 6119D4F624A9C5E900CA153D /* SDFileLoader.swift */, 134 | 616EC6D7249DABA000B6D6F1 /* SDParser.swift */, 135 | 616EC6D5249DAB4400B6D6F1 /* Consts.swift */, 136 | 616EC6DE249E040400B6D6F1 /* SDNominalObj.swift */, 137 | 616EC6E824A31F7900B6D6F1 /* SDProtocolObj.swift */, 138 | 6119CF3024A89DCC00CA153D /* SDObjCClass.swift */, 139 | 6119D51724AB271F00CA153D /* SDPointer.swift */, 140 | ); 141 | path = SwiftDump; 142 | sourceTree = ""; 143 | }; 144 | 616EC6CF249DA08900B6D6F1 /* Util */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 616EC6C9249D9D6000B6D6F1 /* RuntimeBridge.swift */, 148 | 616EC6E624A2E57700B6D6F1 /* ContextDescriptor.swift */, 149 | 616EC6CB249D9DD000B6D6F1 /* Ext.swift */, 150 | 616EC6CD249D9FD000B6D6F1 /* Log.swift */, 151 | ); 152 | path = Util; 153 | sourceTree = ""; 154 | }; 155 | 616EC6E324A04E8500B6D6F1 /* Frameworks */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 616EC6E424A04E8600B6D6F1 /* libswiftDemangle.tbd */, 159 | ); 160 | name = Frameworks; 161 | sourceTree = ""; 162 | }; 163 | /* End PBXGroup section */ 164 | 165 | /* Begin PBXNativeTarget section */ 166 | 616EC6BE249D9CE700B6D6F1 /* SwiftDump */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = 616EC6C6249D9CE700B6D6F1 /* Build configuration list for PBXNativeTarget "SwiftDump" */; 169 | buildPhases = ( 170 | 616EC6BB249D9CE700B6D6F1 /* Sources */, 171 | 616EC6BC249D9CE700B6D6F1 /* Frameworks */, 172 | 616EC6BD249D9CE700B6D6F1 /* CopyFiles */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | ); 178 | name = SwiftDump; 179 | packageProductDependencies = ( 180 | 6119CF3324A99D3500CA153D /* ArgumentParser */, 181 | ); 182 | productName = SwiftDump; 183 | productReference = 616EC6BF249D9CE700B6D6F1 /* SwiftDump */; 184 | productType = "com.apple.product-type.tool"; 185 | }; 186 | /* End PBXNativeTarget section */ 187 | 188 | /* Begin PBXProject section */ 189 | 616EC6B7249D9CE700B6D6F1 /* Project object */ = { 190 | isa = PBXProject; 191 | attributes = { 192 | LastSwiftUpdateCheck = 1150; 193 | LastUpgradeCheck = 1150; 194 | ORGANIZATIONNAME = nw; 195 | TargetAttributes = { 196 | 616EC6BE249D9CE700B6D6F1 = { 197 | CreatedOnToolsVersion = 11.5; 198 | }; 199 | }; 200 | }; 201 | buildConfigurationList = 616EC6BA249D9CE700B6D6F1 /* Build configuration list for PBXProject "SwiftDump" */; 202 | compatibilityVersion = "Xcode 9.3"; 203 | developmentRegion = en; 204 | hasScannedForEncodings = 0; 205 | knownRegions = ( 206 | en, 207 | Base, 208 | ); 209 | mainGroup = 616EC6B6249D9CE700B6D6F1; 210 | packageReferences = ( 211 | 6119CF3224A99D3500CA153D /* XCRemoteSwiftPackageReference "swift-argument-parser" */, 212 | ); 213 | productRefGroup = 616EC6C0249D9CE700B6D6F1 /* Products */; 214 | projectDirPath = ""; 215 | projectRoot = ""; 216 | targets = ( 217 | 616EC6BE249D9CE700B6D6F1 /* SwiftDump */, 218 | ); 219 | }; 220 | /* End PBXProject section */ 221 | 222 | /* Begin PBXSourcesBuildPhase section */ 223 | 616EC6BB249D9CE700B6D6F1 /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 6119D4EC24A9C3C300CA153D /* String+Extensions.swift in Sources */, 228 | 616EC6C3249D9CE700B6D6F1 /* main.swift in Sources */, 229 | 6119D4E724A9C3C300CA153D /* Segment.swift in Sources */, 230 | 6119CF3124A89DCC00CA153D /* SDObjCClass.swift in Sources */, 231 | 616EC6D6249DAB4400B6D6F1 /* Consts.swift in Sources */, 232 | 6119D4F724A9C5E900CA153D /* SDFileLoader.swift in Sources */, 233 | 616EC6CA249D9D6000B6D6F1 /* RuntimeBridge.swift in Sources */, 234 | 616EC6CE249D9FD000B6D6F1 /* Log.swift in Sources */, 235 | 6119D51824AB271F00CA153D /* SDPointer.swift in Sources */, 236 | 616EC6E724A2E57700B6D6F1 /* ContextDescriptor.swift in Sources */, 237 | 616EC6DF249E040400B6D6F1 /* SDNominalObj.swift in Sources */, 238 | 6119D4EA24A9C3C300CA153D /* MachOFatHeader.swift in Sources */, 239 | 616EC6CC249D9DD000B6D6F1 /* Ext.swift in Sources */, 240 | 6119D4ED24A9C3C300CA153D /* Data+Extensions.swift in Sources */, 241 | 6119D4F424A9C3C300CA153D /* MachOParser.swift in Sources */, 242 | 6119D4EE24A9C3C300CA153D /* MachOHeader.swift in Sources */, 243 | 616EC6D8249DABA000B6D6F1 /* SDParser.swift in Sources */, 244 | 6119D50B24A9E24F00CA153D /* Section64.swift in Sources */, 245 | 6119D4EF24A9C3C300CA153D /* LoadCommand.swift in Sources */, 246 | 616EC6E924A31F7900B6D6F1 /* SDProtocolObj.swift in Sources */, 247 | 6119D4F524A9C3C300CA153D /* MachOFile.swift in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin XCBuildConfiguration section */ 254 | 616EC6C4249D9CE700B6D6F1 /* Debug */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | CLANG_ANALYZER_NONNULL = YES; 259 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_ENABLE_OBJC_WEAK = YES; 265 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 266 | CLANG_WARN_BOOL_CONVERSION = YES; 267 | CLANG_WARN_COMMA = YES; 268 | CLANG_WARN_CONSTANT_CONVERSION = YES; 269 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 271 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 272 | CLANG_WARN_EMPTY_BODY = YES; 273 | CLANG_WARN_ENUM_CONVERSION = YES; 274 | CLANG_WARN_INFINITE_RECURSION = YES; 275 | CLANG_WARN_INT_CONVERSION = YES; 276 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | COPY_PHASE_STRIP = NO; 287 | DEBUG_INFORMATION_FORMAT = dwarf; 288 | ENABLE_STRICT_OBJC_MSGSEND = YES; 289 | ENABLE_TESTABILITY = YES; 290 | GCC_C_LANGUAGE_STANDARD = gnu11; 291 | GCC_DYNAMIC_NO_PIC = NO; 292 | GCC_NO_COMMON_BLOCKS = YES; 293 | GCC_OPTIMIZATION_LEVEL = 0; 294 | GCC_PREPROCESSOR_DEFINITIONS = ( 295 | "DEBUG=1", 296 | "$(inherited)", 297 | ); 298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 299 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 300 | GCC_WARN_UNDECLARED_SELECTOR = YES; 301 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 302 | GCC_WARN_UNUSED_FUNCTION = YES; 303 | GCC_WARN_UNUSED_VARIABLE = YES; 304 | MACOSX_DEPLOYMENT_TARGET = 10.11; 305 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 306 | MTL_FAST_MATH = YES; 307 | ONLY_ACTIVE_ARCH = YES; 308 | SDKROOT = macosx; 309 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 310 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 311 | SWIFT_VERSION = 5.0; 312 | }; 313 | name = Debug; 314 | }; 315 | 616EC6C5249D9CE700B6D6F1 /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_ENABLE_OBJC_WEAK = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 349 | ENABLE_NS_ASSERTIONS = NO; 350 | ENABLE_STRICT_OBJC_MSGSEND = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu11; 352 | GCC_NO_COMMON_BLOCKS = YES; 353 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 354 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 355 | GCC_WARN_UNDECLARED_SELECTOR = YES; 356 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 357 | GCC_WARN_UNUSED_FUNCTION = YES; 358 | GCC_WARN_UNUSED_VARIABLE = YES; 359 | MACOSX_DEPLOYMENT_TARGET = 10.11; 360 | MTL_ENABLE_DEBUG_INFO = NO; 361 | MTL_FAST_MATH = YES; 362 | SDKROOT = macosx; 363 | SWIFT_COMPILATION_MODE = wholemodule; 364 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 365 | SWIFT_VERSION = 5.0; 366 | }; 367 | name = Release; 368 | }; 369 | 616EC6C7249D9CE700B6D6F1 /* Debug */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | CODE_SIGN_STYLE = Automatic; 373 | DEVELOPMENT_TEAM = ""; 374 | ENABLE_HARDENED_RUNTIME = YES; 375 | FRAMEWORK_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/SwiftDump", 378 | ); 379 | LIBRARY_SEARCH_PATHS = ( 380 | "$(inherited)", 381 | "$(SDKROOT)/usr/lib/swift", 382 | ); 383 | MACOSX_DEPLOYMENT_TARGET = 10.13; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | SWIFT_VERSION = 5.0; 386 | }; 387 | name = Debug; 388 | }; 389 | 616EC6C8249D9CE700B6D6F1 /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | CODE_SIGN_STYLE = Automatic; 393 | DEVELOPMENT_TEAM = ""; 394 | ENABLE_HARDENED_RUNTIME = YES; 395 | FRAMEWORK_SEARCH_PATHS = ( 396 | "$(inherited)", 397 | "$(PROJECT_DIR)/SwiftDump", 398 | ); 399 | LIBRARY_SEARCH_PATHS = ( 400 | "$(inherited)", 401 | "$(SDKROOT)/usr/lib/swift", 402 | ); 403 | MACOSX_DEPLOYMENT_TARGET = 10.13; 404 | PRODUCT_NAME = "$(TARGET_NAME)"; 405 | SWIFT_VERSION = 5.0; 406 | }; 407 | name = Release; 408 | }; 409 | /* End XCBuildConfiguration section */ 410 | 411 | /* Begin XCConfigurationList section */ 412 | 616EC6BA249D9CE700B6D6F1 /* Build configuration list for PBXProject "SwiftDump" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 616EC6C4249D9CE700B6D6F1 /* Debug */, 416 | 616EC6C5249D9CE700B6D6F1 /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | 616EC6C6249D9CE700B6D6F1 /* Build configuration list for PBXNativeTarget "SwiftDump" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 616EC6C7249D9CE700B6D6F1 /* Debug */, 425 | 616EC6C8249D9CE700B6D6F1 /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | defaultConfigurationName = Release; 429 | }; 430 | /* End XCConfigurationList section */ 431 | 432 | /* Begin XCRemoteSwiftPackageReference section */ 433 | 6119CF3224A99D3500CA153D /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = { 434 | isa = XCRemoteSwiftPackageReference; 435 | repositoryURL = "https://github.com/apple/swift-argument-parser"; 436 | requirement = { 437 | kind = upToNextMajorVersion; 438 | minimumVersion = 0.2.0; 439 | }; 440 | }; 441 | /* End XCRemoteSwiftPackageReference section */ 442 | 443 | /* Begin XCSwiftPackageProductDependency section */ 444 | 6119CF3324A99D3500CA153D /* ArgumentParser */ = { 445 | isa = XCSwiftPackageProductDependency; 446 | package = 6119CF3224A99D3500CA153D /* XCRemoteSwiftPackageReference "swift-argument-parser" */; 447 | productName = ArgumentParser; 448 | }; 449 | /* End XCSwiftPackageProductDependency section */ 450 | }; 451 | rootObject = 616EC6B7249D9CE700B6D6F1 /* Project object */; 452 | } 453 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-argument-parser", 6 | "repositoryURL": "https://github.com/apple/swift-argument-parser", 7 | "state": { 8 | "branch": null, 9 | "revision": "eb51f949cdd0c9d88abba9ce79d37eb7ea1231d0", 10 | "version": "0.2.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump.xcodeproj/xcshareddata/xcschemes/SwiftDump.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 67 | 68 | 74 | 76 | 82 | 83 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/Consts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Consts.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | enum ESegment: String { 13 | case TEXT = "__TEXT" 14 | case DATA = "__DATA" 15 | case DATA_CONST = "__DATA_CONST" 16 | 17 | } 18 | 19 | 20 | enum ESection: String { 21 | case swift5types = "__swift5_types" 22 | case swift5proto = "__swift5_proto" 23 | case swift5protos = "__swift5_protos" 24 | 25 | case swift5filemd = "__swift5_fieldmd" 26 | case objc_classlist = "__objc_classlist" 27 | 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/MachO/Data+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Geoffrey Foster 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // 17 | // Data+Extensions.swift 18 | // Machismo 19 | // 20 | // Created by Geoffrey Foster on 2018-05-13. 21 | // Copyright © 2018 g-Off.net. All rights reserved. 22 | // 23 | // 24 | // neilwu modify this file for SwiftDump 25 | 26 | import Foundation 27 | 28 | extension Data { 29 | subscript(_ arch: MachOFatArch) -> Data { 30 | return Data(self[arch.offset..<(arch.offset + arch.size)]) 31 | } 32 | 33 | func extract(_ type: T.Type, offset: Int = 0) -> T { 34 | let data = self[offset...size]; 35 | let ret = data.withUnsafeBytes { (ptr:UnsafeRawBufferPointer) -> T in 36 | return ptr.loadUnaligned(as: T.self) 37 | } 38 | return ret; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/MachO/Load Commands/RPath.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RPath.swift 3 | // Machismo 4 | // 5 | // Created by Geoffrey Foster on 2018-05-12. 6 | // Copyright © 2018 g-Off.net. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MachO 11 | 12 | extension LoadCommand { 13 | public struct RPath: LoadCommandType { 14 | let path: String 15 | 16 | init(loadCommand: LoadCommand) { 17 | var command = loadCommand.data.extract(rpath_command.self, offset: loadCommand.offset) 18 | if loadCommand.byteSwapped { 19 | swap_rpath_command(&command, byteSwappedOrder) 20 | } 21 | self.path = String(data: loadCommand.data, offset: loadCommand.offset, commandSize: loadCommand.size, loadCommandString: command.path) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/MachO/Load Commands/Section64.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Section64.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MachO 11 | 12 | public struct Section64 { 13 | private(set) var sectname: String 14 | private(set) var segname: String 15 | 16 | private(set) var info:section_64 17 | 18 | var align: UInt32 { 19 | return UInt32(pow(Double(2), Double(info.align))) 20 | } 21 | var fileOffset: UInt32 { 22 | return info.offset; 23 | } 24 | 25 | var num:Int { 26 | let num: Int = Int(info.size) / Int(align); 27 | return num; 28 | } 29 | 30 | 31 | init(section: section_64) { 32 | segname = String(section.segname); 33 | 34 | var strSect = String(section.sectname); 35 | // max len is 16 36 | if (strSect.count > 16) { 37 | strSect = String(strSect.prefix(16)); 38 | } 39 | sectname = strSect; 40 | self.info = section; 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/MachO/Load Commands/Segment.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Geoffrey Foster 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // 17 | // Segment.swift 18 | // Machismo 19 | // 20 | // Created by Geoffrey Foster on 2018-05-06. 21 | // Copyright © 2018 g-Off.net. All rights reserved. 22 | // 23 | // neilwu modify this file for SwiftDump 24 | 25 | import Foundation 26 | import MachO 27 | 28 | 29 | extension MachOLoadCommand { 30 | public struct Segment: MachOLoadCommandType { 31 | 32 | // public var cmd: UInt32 /* for 64-bit architectures */ /* LC_SEGMENT_64 */ 33 | // public var cmdsize: UInt32 /* includes sizeof section_64 structs */ 34 | // public var segname: (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) /* segment name */ 35 | // public var vmaddr: UInt64 /* memory address of this segment */ 36 | // public var vmsize: UInt64 /* memory size of this segment */ 37 | // public var fileoff: UInt64 /* file offset of this segment */ 38 | // public var filesize: UInt64 /* amount to map from the file */ 39 | // public var maxprot: vm_prot_t /* maximum VM protection */ 40 | // public var initprot: vm_prot_t /* initial VM protection */ 41 | // public var nsects: UInt32 /* number of sections in segment */ 42 | // public var flags: UInt32 /* flags */ 43 | 44 | public let name: String 45 | 46 | private(set) var command64: segment_command_64? = nil; // neilwu added 47 | private(set) var command: segment_command? = nil; // 48 | 49 | private(set) var sections:[Section64] = [] 50 | 51 | init(command: segment_command_64) { 52 | self.name = String(command.segname) 53 | self.command64 = command 54 | } 55 | 56 | init(command: segment_command) { 57 | self.name = String(command.segname) 58 | self.command = command; 59 | } 60 | 61 | init(loadCommand: MachOLoadCommand) { 62 | if loadCommand.command == LC_SEGMENT_64 { 63 | var segmentCommand64:segment_command_64 = loadCommand.data.extract(segment_command_64.self, offset: loadCommand.offset) 64 | if loadCommand.byteSwapped { 65 | swap_segment_command_64(&segmentCommand64, byteSwappedOrder) 66 | } 67 | self.init(command: segmentCommand64) 68 | 69 | if (segmentCommand64.nsects <= 0) { 70 | return; 71 | } 72 | let sectionOffset = loadCommand.offset + 0x48; // 0x48=sizeof(segment_command_64) 73 | 74 | for i in 0.. MachOLoadCommandType? { 52 | switch Int(command) { 53 | case Int(LC_SEGMENT), Int(LC_SEGMENT_64): 54 | return Segment(loadCommand: self) 55 | default: 56 | return nil 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/MachO/MachOFatHeader.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Geoffrey Foster 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // 17 | // FatHeader.swift 18 | // Machismo 19 | // 20 | // Created by Geoffrey Foster on 2018-05-13. 21 | // Copyright © 2018 g-Off.net. All rights reserved. 22 | // 23 | 24 | import Foundation 25 | import MachO.fat 26 | 27 | public struct MachOFatArch { 28 | public var cputype: cpu_type_t /* cpu specifier (int) */ 29 | public var cpusubtype: cpu_subtype_t /* machine specifier (int) */ 30 | public var offset: UInt64 /* file offset to this object file */ 31 | public var size: UInt64 /* size of this object file */ 32 | public var align: UInt32 /* alignment as a power of 2 */ 33 | 34 | init(arch: fat_arch_64) { 35 | self.cputype = arch.cputype 36 | self.cpusubtype = arch.cpusubtype 37 | self.offset = arch.offset 38 | self.size = arch.size 39 | self.align = arch.align 40 | } 41 | 42 | init(arch: fat_arch) { 43 | self.cputype = arch.cputype 44 | self.cpusubtype = arch.cpusubtype 45 | self.offset = UInt64(arch.offset) 46 | self.size = UInt64(arch.size) 47 | self.align = arch.align 48 | } 49 | } 50 | 51 | public struct MachOFatHeader { 52 | 53 | public let architectures: [MachOFatArch]; 54 | 55 | init?(data: Data) { 56 | let magic = data.extract(UInt32.self) 57 | guard [FAT_MAGIC, FAT_MAGIC_64, FAT_CIGAM, FAT_CIGAM_64].contains(magic) else { return nil } 58 | 59 | var header = data.extract(fat_header.self) 60 | let is64Bit = [FAT_MAGIC_64, FAT_CIGAM_64].contains(magic) 61 | let byteSwapped = [FAT_CIGAM, FAT_CIGAM_64].contains(magic) 62 | if [FAT_CIGAM, FAT_CIGAM_64].contains(magic) { 63 | swap_fat_header(&header, byteSwappedOrder) 64 | } 65 | var offset = MemoryLayout.size(ofValue: header) 66 | var architectures: [MachOFatArch] = [] 67 | if is64Bit { 68 | for _ in 0.. MachAttributes { 87 | let magic = data.extract(UInt32.self) 88 | let is64Bit = magic == MH_MAGIC_64 || magic == MH_CIGAM_64 89 | let isByteSwapped = magic == MH_CIGAM || magic == MH_CIGAM_64 90 | return MachAttributes(is64Bit: is64Bit, isByteSwapped: isByteSwapped) 91 | } 92 | 93 | private static func header(from data: Data, attributes: MachAttributes) -> MachOHeader { 94 | if attributes.is64Bit { 95 | let header = data.extract(mach_header_64.self) 96 | return MachOHeader(header: header) 97 | } else { 98 | let header = data.extract(mach_header.self) 99 | return MachOHeader(header: header) 100 | } 101 | } 102 | 103 | private static func segmentCommands(from data: Data, header: MachOHeader, attributes: MachAttributes) -> [MachOLoadCommandType] { 104 | var segmentCommands: [MachOLoadCommandType] = [] 105 | var offset = header.size 106 | for _ in 0.. String in 32 | return pointer.withMemoryRebound(to: UInt8.self, capacity: rawCStringSize, { 33 | return String(cString: $0) 34 | }) 35 | } 36 | self.init(string) 37 | } 38 | 39 | /* 40 | * A variable length string in a load command is represented by an lc_str 41 | * union. The strings are stored just after the load command structure and 42 | * the offset is from the start of the load command structure. The size 43 | * of the string is reflected in the cmdsize field of the load command. 44 | * Once again any padded bytes to bring the cmdsize field to a multiple 45 | * of 4 bytes must be zero. 46 | */ 47 | init(data: Data, offset: Int, commandSize: Int, loadCommandString: lc_str) { 48 | let loadCommandStringOffset = Int(loadCommandString.offset) 49 | let stringOffset = offset + loadCommandStringOffset 50 | let length = commandSize - loadCommandStringOffset 51 | self = String(data: data[stringOffset..<(stringOffset + length)], encoding: .utf8)!.trimmingCharacters(in: .controlCharacters) 52 | } 53 | 54 | init(loadCommand: MachOLoadCommand, string: lc_str) { 55 | let stringOffset = loadCommand.offset + Int(string.offset) 56 | self = String(data: loadCommand.data[stringOffset..<(loadCommand.offset + loadCommand.size)], encoding: .utf8)!.trimmingCharacters(in: .controlCharacters) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/SDFileLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileLoaderNew.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SDFileLoader { 12 | private let filePath: String; 13 | 14 | private(set) var machoFile: MachOFile? = nil; 15 | 16 | init(file: String) { 17 | self.filePath = file; 18 | } 19 | 20 | func load(cpu: MachOCpuType) -> Bool { 21 | 22 | Log("load file from \(self.filePath)") 23 | let fileURL = URL(fileURLWithPath: self.filePath); 24 | do { 25 | let fileObj = try MachOFile(url: fileURL, cpu: cpu); 26 | self.machoFile = fileObj; 27 | Log("load file success") 28 | return true; 29 | } catch { 30 | LogError("load fail, error \(error.localizedDescription)"); 31 | } 32 | 33 | return false; 34 | } 35 | 36 | func getSegment(of seg: ESegment) -> MachOLoadCommand.Segment? { 37 | //return self.macho?.segments(withName: seg.rawValue).first?.value 38 | guard let machoFile = self.machoFile else { 39 | return nil; 40 | } 41 | for cmd in machoFile.commands { 42 | //seg.rawValue 43 | if let segment = cmd as? MachOLoadCommand.Segment { 44 | if (seg.rawValue == segment.name) { 45 | return segment; 46 | } 47 | } 48 | } 49 | return nil; 50 | } 51 | 52 | func getSection(of section: ESection, seg: ESegment) -> Section64? { 53 | guard let segObj:MachOLoadCommand.Segment = self.getSegment(of: seg) else { 54 | return nil; 55 | } 56 | let ret = segObj.sections.first { (sect:Section64) -> Bool in 57 | return sect.sectname == section.rawValue; 58 | } 59 | return ret; 60 | } 61 | 62 | func readU32(_ archPtr: SDPointer) -> UInt32 { 63 | return self.machoFile?.dataSlice.readU32(offset: Int(archPtr.address)) ?? 0; 64 | } 65 | 66 | func readU64(_ archPtr: SDPointer) -> UInt64 { 67 | return self.machoFile?.dataSlice.readU64(offset: Int(archPtr.address)) ?? 0; 68 | } 69 | 70 | func readS32(_ archPtr: SDPointer) -> Int32 { 71 | return self.machoFile?.dataSlice.readS32(offset: Int(archPtr.address)) ?? 0; 72 | } 73 | 74 | func readMove(_ ptr: SDPointer) -> SDPointer { 75 | let val = readU32(ptr); 76 | return ptr.add(Int64(val)); 77 | } 78 | 79 | func readStr(_ ptr: SDPointer) -> String? { 80 | return self.machoFile?.dataSlice.readCString(from: Int(ptr.address)) 81 | } 82 | 83 | } 84 | 85 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/SDNominalObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NominalObj.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | final class SDNominalObjField { 13 | var name: String = ""; 14 | var type: String = ""; 15 | 16 | var namePtr: SDPointer = SDPointer(addr: 0) 17 | var typePtr: SDPointer = SDPointer(addr: 0) 18 | } 19 | 20 | final class SDNominalObj { 21 | 22 | var typeName: String = ""; // type name 23 | var contextDescriptorFlag: SDContextDescriptorFlags = SDContextDescriptorFlags(0); // default 24 | var fields: [SDNominalObjField] = []; 25 | 26 | var mangledTypeName: String = ""; // if someone else define this type as property, you can use this to retrive the name 27 | var nominalOffset: Int64 = 0; // Context Descriptor offset 28 | var accessorOffset: UInt64 = 0; // Access Function address 29 | 30 | var protocols:[String] = []; 31 | var superClassName: String = ""; 32 | 33 | var dumpDefine: String { 34 | let intent: String = " "; 35 | var str: String = ""; 36 | let kind = contextDescriptorFlag.kind; 37 | str += "\(kind) " + typeName; 38 | if (!superClassName.isEmpty) { 39 | str += " : " + superClassName; 40 | } 41 | if (protocols.count > 0) { 42 | let superStr: String = protocols.joined(separator: ",") 43 | let tmp: String = superClassName.isEmpty ? " : " : ""; 44 | str += tmp + superStr; 45 | } 46 | str += " {\n"; 47 | 48 | str += intent + "// \(contextDescriptorFlag)\n"; 49 | if (accessorOffset > 0) { 50 | str += intent + "// Access Function at \(accessorOffset.hex) \n"; 51 | } 52 | 53 | for field in fields { 54 | var fs: String = intent; 55 | if kind == .Enum { 56 | if (field.type.isEmpty) { 57 | fs += "case \(field.name)\n"; // without payload 58 | } else { 59 | let tmp = field.type.hasPrefix("(") ? field.type : "(" + field.type + ")"; 60 | fs += "case \(field.name)\(tmp)\n"; // enum with payload 61 | } 62 | 63 | } else { 64 | fs += "let \(field.name): \(field.type);\n"; 65 | } 66 | str += fs; 67 | } 68 | 69 | str += "}\n"; 70 | 71 | return str; 72 | } 73 | 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/SDObjCClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftObj.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct SDObjCClassROData { 12 | var flags: UInt32 = 0; 13 | var instanceStart: UInt32 = 0; 14 | var instanceSize: UInt32 = 0; 15 | var unknowField: UInt32 = 0; // 16 | var instanceVarLayout: UInt64 = 0; 17 | var nameAddr: UInt64 = 0; 18 | var weakInstanceVarLayout: UInt64 = 0; 19 | } 20 | struct SDObjCClass { 21 | var isaAddress: UInt64 = 0; // pointer 22 | var superclassAddress: UInt64 = 0; // pointer 23 | var cache: UInt64 = 0; // pointer 24 | var mask:UInt32 = 0; 25 | var occupied: UInt32 = 0; 26 | var dataAddr: UInt64 = 0; 27 | } 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/SDParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SDParser { 12 | private(set) var protocolObjs:[SDProtocolObj] = []; 13 | private(set) var cacheProtocolAddressMap:[UInt64: String] = [:]; 14 | 15 | private(set) var nominalObjs:[SDNominalObj] = []; 16 | private(set) var cacheNominalOffsetMap:[Int64: String] = [:]; // used for name demangle 17 | 18 | private(set) var nominalProtoMap:[String: [String] ] = [:]; 19 | private(set) var classNameInheritanceMap: [String : String] = [:]; // [className : SuperClassName] 20 | 21 | private var loader: SDFileLoader? = nil; 22 | 23 | init(with loader: SDFileLoader) { 24 | self.loader = loader 25 | } 26 | 27 | // mangledName -> TypeName 28 | private var mangledNameMap:[String : String] = ["0x02f36d": "Int32", 29 | "0x02cd6d": "Int16", "0x027b6e": "UInt16", 30 | "0x022b6c": "UInt32", 31 | "0x02b98502": "Int64", "0x02418a02" : "UInt64", 32 | "0x02958802": "CGFloat"]; 33 | 34 | func parseSwiftProto() { 35 | guard let loader = self.loader else { 36 | return; 37 | } 38 | let sectionType = ESection.swift5proto; 39 | Log("start parse section \(sectionType.rawValue)") 40 | guard let typeSect:Section64 = loader.getSection(of: sectionType, seg: ESegment.TEXT) else { 41 | Log("did not find section \(sectionType.rawValue)") 42 | return; 43 | } 44 | 45 | // This section contains an array of 32-bit signed integers. Each integer is a relative offset that points to a protocol conformance descriptor in the __TEXT.__const section. 46 | if (4 != typeSect.align) { 47 | Log("error! section \(sectionType.rawValue) is not 4 bytes align. align \(typeSect.align)") 48 | return; 49 | } 50 | /* 51 | type ProtocolConformanceDescriptor struct { 52 | ProtocolDescriptor int32 53 | NominalTypeDescriptor int32 54 | ProtocolWitnessTable int32 55 | ConformanceFlags uint32 56 | }*/ 57 | 58 | let num: Int = typeSect.num; 59 | let fileOffset: Int64 = Int64(typeSect.fileOffset); 60 | 61 | for i in 0.. \(protoName), nominalTypeDescriptorPtr \( nominalTypeDescriptorVal.hex ) => \(nominalName), protocolWitnessTablePtr \(protocolWitnessTablePtr.desc), conformanceFlags \(conformanceFlags.hex)"); 129 | 130 | Log("\(i) \(pcdPtr.desc) proto=\(protoName), nominal=\(nominalName)"); 131 | 132 | } 133 | } 134 | 135 | func parseSwiftProtos() { 136 | guard let loader = self.loader else { 137 | return; 138 | } 139 | let sectionType = ESection.swift5protos; 140 | Log("start parse section \(sectionType.rawValue)"); 141 | 142 | guard let typeSect:Section64 = loader.getSection(of: sectionType, seg: ESegment.TEXT) else { 143 | Log("did not find section \(sectionType.rawValue)") 144 | return; 145 | } 146 | // This section contains an array of 32-bit signed integers. Each integer is a relative offset that points to a protocol descriptor in the __TEXT.__const section. 147 | if (4 != typeSect.align) { 148 | Log("error! section \(sectionType.rawValue) is not 4 bytes align. align \(typeSect.align)") 149 | return; 150 | } 151 | 152 | let num: Int = typeSect.num; 153 | let fileOffset: Int64 = Int64(typeSect.fileOffset); 154 | 155 | Log("section \(sectionType.rawValue) \(fileOffset.hex)") 156 | /* 157 | type ProtocolDescriptor struct { 158 | Flags uint32 159 | Parent int32 160 | Name int32 161 | NumRequirementsInSignature uint32 162 | NumRequirements uint32 163 | AssociatedTypeNames int32 164 | [The generic requirements that form the requirement signature] 165 | [The protocol requirements of the protocol] 166 | }*/ 167 | for i in 0.. 0) { 305 | mangledNameMap[obj.mangledTypeName] = obj.typeName; 306 | } 307 | } 308 | } 309 | 310 | private func resolveSuperClassName(_ nominalPtr: SDPointer) -> String { 311 | //nominalPtr 312 | let ptr = nominalPtr.add(4 * 5) 313 | let superClassTypeVal = self.loader?.readS32(ptr) ?? 0; 314 | if (superClassTypeVal == 0) { 315 | return ""; 316 | } 317 | 318 | var retName: String = ""; 319 | 320 | let superClassRefPtr = ptr.add( Int64(superClassTypeVal) ); 321 | if let superRefStr = self.loader?.readStr(superClassRefPtr), !superRefStr.isEmpty { 322 | if superRefStr.hasPrefix("0x") { 323 | retName = self.mangledNameMap[superRefStr] ?? superRefStr; 324 | } else { 325 | retName = superRefStr; // resolve later 326 | } 327 | } 328 | return retName; 329 | } 330 | 331 | private func getISAClassName(of obj:SDObjCClass) -> String { 332 | if (obj.isaAddress == 0) { 333 | return ""; 334 | } 335 | 336 | let dataSlice: Data? = self.loader?.machoFile?.dataSlice; 337 | 338 | guard let metaObj:SDObjCClass = dataSlice?.extract(SDObjCClass.self, offset: Int(obj.isaAddress & 0xFFFFFFFF)) else { 339 | return ""; 340 | } 341 | if (metaObj.dataAddr == 0) { 342 | return ""; 343 | } 344 | // find class name string 345 | guard let dataObj:SDObjCClassROData = dataSlice?.extract(SDObjCClassROData.self, offset: Int(metaObj.dataAddr & 0xFFFFFFFF)) else { 346 | return ""; 347 | } 348 | 349 | let name: String = self.loader?.readStr(SDPointer(addr: dataObj.nameAddr & 0xFFFFFFFF)) ?? ""; 350 | //print(" metaname:", dataObj.nameAddr.hex, name) 351 | return name; 352 | } 353 | 354 | private func getSuperClassName(of obj:SDObjCClass) -> String { 355 | if (obj.superclassAddress == 0) { 356 | return ""; 357 | } 358 | let dataSlice: Data? = self.loader?.machoFile?.dataSlice; 359 | 360 | guard let superClassObj:SDObjCClass = dataSlice?.extract(SDObjCClass.self, offset: Int(obj.superclassAddress & 0xFFFFFFFF)) else { 361 | return ""; 362 | } 363 | 364 | if (superClassObj.isaAddress == 0) { 365 | // find class name string 366 | if let dataObj:SDObjCClassROData = dataSlice?.extract(SDObjCClassROData.self, offset: Int(superClassObj.dataAddr & 0xFFFFFFFF)) { 367 | // 368 | let name: String = self.loader?.readStr(SDPointer(addr: dataObj.nameAddr & 0xFFFFFFFF)) ?? ""; 369 | //print(" super dataObj:", dataObj.nameAddr.hex, name) 370 | return name; 371 | } 372 | } else { 373 | return getISAClassName(of: superClassObj); 374 | } 375 | 376 | return ""; 377 | } 378 | private func demangleClassName(_ name: String) -> String { 379 | var tmp: String = runtimeGetDemangledName(name); 380 | tmp = removeSwiftModulePrefix(tmp) 381 | return tmp; 382 | } 383 | 384 | func parseSwiftOCClass() { 385 | guard let loader = self.loader else { 386 | return; 387 | } 388 | Log("start parse section \(ESection.objc_classlist.rawValue)") 389 | let typeSect:Section64 390 | if let tmp:Section64 = loader.getSection(of: ESection.objc_classlist, seg: ESegment.DATA) { 391 | typeSect = tmp; 392 | Log("use seg \(ESegment.DATA.rawValue)"); 393 | } else if let tmp:Section64 = loader.getSection(of: ESection.objc_classlist, seg: ESegment.DATA_CONST) { 394 | typeSect = tmp; 395 | Log("use seg \(ESegment.DATA_CONST.rawValue)"); 396 | } else { 397 | LogWarn("didn't find section \(ESection.objc_classlist.rawValue)") 398 | return; 399 | } 400 | 401 | let fileOffset: UInt64 = UInt64(typeSect.fileOffset); 402 | 403 | var metaClassNameMap:[UInt64: String] = [:]; // isaAddress : name 404 | 405 | // align is 8 406 | for i in 0.. 0) { 418 | metaClassNameMap[obj.isaAddress] = metaClassName; 419 | } else { 420 | continue; 421 | } 422 | 423 | let superClassName: String = demangleClassName(self.getSuperClassName(of: obj) ); 424 | if (superClassName.count > 0) { 425 | Log("\(i). \(metaClassName) : \(superClassName)") 426 | self.classNameInheritanceMap[metaClassName] = superClassName; 427 | } else { 428 | Log("\(i). \(metaClassName)") 429 | } 430 | } 431 | 432 | } 433 | 434 | 435 | func dumpAll() { 436 | 437 | for obj in self.protocolObjs { 438 | let protoName: String = obj.name; 439 | if let arr:[String] = self.nominalProtoMap[protoName] { 440 | obj.superProtocols = arr; 441 | } 442 | print(obj.dumpDefine) 443 | } 444 | 445 | for obj in nominalObjs { 446 | 447 | if let arr:[String] = self.nominalProtoMap[obj.typeName] { 448 | obj.protocols = arr; 449 | } 450 | 451 | var resoleSuperFromOC:Bool = obj.superClassName.isEmpty; 452 | if (obj.superClassName.hasPrefix("0x")) { 453 | if let tmp = self.mangledNameMap[obj.superClassName], !tmp.isEmpty { 454 | obj.superClassName = tmp; 455 | resoleSuperFromOC = false; 456 | } 457 | } else { 458 | let tmp = runtimeGetDemangledName("$s" + obj.superClassName); 459 | if (!tmp.hasPrefix("$s") && tmp != obj.superClassName) { 460 | obj.superClassName = tmp; 461 | } 462 | } 463 | if (resoleSuperFromOC) { 464 | obj.superClassName = self.classNameInheritanceMap[obj.typeName] ?? obj.superClassName; 465 | } 466 | 467 | 468 | for field in obj.fields { 469 | let ft: String = field.type; 470 | if (ft.hasPrefix("0x")) { 471 | if let fixName = mangledNameMap[ft] { 472 | field.type = fixName; 473 | } else { 474 | // 475 | field.type = fixMangledName(ft, startPtr: field.typePtr) 476 | } 477 | 478 | } else if (ft != "String") { 479 | let checkName: String = "$s" + ft; 480 | let tmp: String = runtimeGetDemangledName(checkName) 481 | if (tmp != checkName ) { 482 | field.type = tmp; 483 | } 484 | } 485 | } 486 | print(obj.dumpDefine) 487 | } 488 | } 489 | 490 | func makeDemangledTypeName(_ type: String, header: String) -> String { 491 | 492 | let isArray:Bool = header.contains("Say") || header.contains("SDy"); 493 | let suffix: String = isArray ? "G" : ""; 494 | let fixName = "So\(type.count)\(type)C" + suffix; 495 | return fixName; 496 | } 497 | 498 | func fixMangledName(_ name: String, startPtr: SDPointer) -> String { 499 | // symbolic-references 500 | let hexName: String = name.removingPrefix("0x") 501 | let dataArray: [UInt8] = hexName.hexBytes 502 | //print(dataArray.map{ String(format: "0x%x", $0) }) 503 | 504 | var mangledName: String = ""; 505 | var i: Int = 0; 506 | 507 | while i < dataArray.count { 508 | let val = dataArray[i]; 509 | if (val == 0x01) { 510 | //find 511 | let fromIdx:Int = i + 1; // ignore 0x01 512 | let toIdx:Int = i + 5; // 4 bytes 513 | if (toIdx > dataArray.count) { 514 | mangledName = mangledName + String(format: "%c", val); 515 | i = i + 1; 516 | continue; 517 | } 518 | let offsetArray:[UInt8] = Array(dataArray[fromIdx..= dataArray.count) { 522 | mangledName = mangledName + result; // use original result 523 | } else { 524 | let fixName = makeDemangledTypeName(result, header: "") 525 | mangledName = mangledName + fixName; 526 | } 527 | 528 | i = i + 5; 529 | } else if (val == 0x02) { 530 | //indirectly 531 | let fromIdx:Int = i + 1; // ignore 0x02 532 | let toIdx:Int = ((i + 4) > dataArray.count) ? ( i + (dataArray.count - i) ) : (i + 4); // 4 bytes 533 | 534 | let offsetArray:[UInt8] = Array(dataArray[fromIdx..= dataArray.count) { 538 | mangledName = mangledName + result; 539 | } else { 540 | let fixName = makeDemangledTypeName(result, header: mangledName) 541 | mangledName = mangledName + fixName 542 | } 543 | i = toIdx + 1; 544 | } else { 545 | //check next 546 | mangledName = mangledName + String(format: "%c", val); 547 | i = i + 1; 548 | } 549 | } 550 | 551 | let result: String = getTypeFromMangledName(mangledName) 552 | if (result == mangledName) { 553 | let tmp: String = runtimeGetDemangledName("$s" + mangledName) 554 | if (tmp != ("$s" + mangledName)) { 555 | return tmp; 556 | } 557 | } 558 | return result; 559 | } 560 | 561 | func resoleSymbolicRefDirectly(_ hexArray: [UInt8], ptr: SDPointer) -> String { 562 | // {any-generic-type, protocol, opaque-type-decl-name} ::= '\x01' .{4} // Reference points directly to context descriptor 563 | //print("resoleSymbolicRef", hexArray, ptr.desc) 564 | let origHex: String = "0x01" + hexArray.hex; 565 | let tmp = hexArray.reversed().hex; 566 | 567 | guard let address = Int64(tmp, radix: 16) else { 568 | return origHex; 569 | } 570 | 571 | let nominalArchPtr: SDPointer = ptr.add(address).fix(); 572 | //print("ptr", tmpPtr.desc) 573 | let nominalArchOffset: Int64 = Int64(nominalArchPtr.address); 574 | return self.cacheNominalOffsetMap[nominalArchOffset] ?? origHex; // use hex as default value 575 | } 576 | 577 | func resoleSymbolicRefIndirectly(_ hexArray: [UInt8], ptr: SDPointer) -> String { 578 | // {any-generic-type, protocol, opaque-type-decl-name} ::= '\x02' .{4} // Reference points indirectly to context descriptor 579 | let origHex: String = "0x02" + hexArray.hex; 580 | let tmp = hexArray.reversed().hex; 581 | 582 | guard let address = Int64(tmp, radix: 16) else { 583 | return origHex; 584 | } 585 | let addrPtr = ptr.add(address).fix() 586 | 587 | if let loader = self.loader { 588 | // read the value from 'addrPtr' as address offset 589 | let val:UInt32 = loader.readU32(addrPtr); 590 | 591 | let nominalArchOffset: Int64 = Int64(val); 592 | return self.cacheNominalOffsetMap[nominalArchOffset] ?? origHex; 593 | } 594 | 595 | return origHex; // use hex as default value 596 | } 597 | } 598 | 599 | 600 | 601 | func dumpStruct(loader: SDFileLoader, nominalPtr: SDPointer, fieldDescriptorPtr: SDPointer, to: SDNominalObj) { 602 | //struct 603 | //let numFields:UInt32 = loader.readU32(nominalPtr.add(4 * 5)); 604 | //let fieldOffsetVectorOffset:UInt32 = loader.readU32(nominalPtr.add(4 * 6)); 605 | //print(" numFields \(numFields), fieldOffsetVectorOffset \(fieldOffsetVectorOffset)") 606 | dumpFieldDescriptor(loader: loader, fieldDescriptorPtr: fieldDescriptorPtr, to: to) 607 | } 608 | 609 | 610 | func dumpFieldDescriptor(loader: SDFileLoader, fieldDescriptorPtr: SDPointer, to: SDNominalObj) { 611 | //swift5_filedmd, FieldDescriptor 612 | 613 | let numFields = loader.readU32(fieldDescriptorPtr.add( 4 + 4 + 2 + 2) ); 614 | if (0 == numFields) { 615 | return; 616 | } 617 | if (numFields >= 1000) { 618 | //TODO: sometimes it may be a invalid value 619 | Log("[dumpFieldDescriptor] \(numFields) too many fields of \(to.typeName), ignore format"); 620 | return; 621 | } 622 | 623 | let fieldStart:SDPointer = fieldDescriptorPtr.add(4 + 4 + 2 + 2 + 4); 624 | for i in 0.. 100) { 631 | continue 632 | } 633 | 634 | let fieldNamePtr = loader.readMove(fieldAddr.add(8)).fix(); 635 | let fieldName = loader.readStr(fieldNamePtr); 636 | 637 | if let field = fieldName, (field.count <= 0 || field.count > 100) { 638 | continue 639 | } 640 | 641 | if let type = typeName, let field = fieldName { 642 | 643 | let realType = getTypeFromMangledName(type); 644 | let fieldObj = SDNominalObjField(); 645 | fieldObj.name = field; // name: field, type: realType 646 | fieldObj.type = realType; 647 | fieldObj.namePtr = fieldNamePtr; 648 | fieldObj.typePtr = typeNamePtr; 649 | to.fields.append(fieldObj); 650 | 651 | //print(" \(field) : \(realType), \(fieldNamePtr.desc) : \(typeNamePtr.desc)") 652 | } 653 | } 654 | } 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/SDPointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SDPointer.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct SDPointer { 12 | private(set) var address: UInt64 13 | 14 | init(addr: UInt64) { 15 | self.address = addr; 16 | } 17 | 18 | func add(_ offset: Int64) -> SDPointer { 19 | var address: UInt64 = 0 20 | if (offset < 0) { 21 | address = self.address - UInt64(abs(offset) ); 22 | } else { 23 | address = self.address + UInt64(offset); 24 | } 25 | return SDPointer(addr: address); 26 | } 27 | 28 | func fix() -> SDPointer { 29 | return SDPointer(addr: self.address & 0xFFFFFFFF); 30 | } 31 | 32 | var desc: String { 33 | return self.address.hex; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/SDProtocolObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProtocolObj.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SDProtocolObj { 12 | var flags: UInt32 = 0; 13 | var name: String = ""; 14 | var numRequirementsInSignature: UInt32 = 0; 15 | var numRequirements: UInt32 = 0; 16 | var associatedTypeNames: String = ""; // joined by " " 17 | 18 | var superProtocols:[String] = []; 19 | 20 | var dumpDefine: String { 21 | let intent: String = " " 22 | var str: String = "protocol \(name)"; 23 | if (superProtocols.count > 0) { 24 | let superStr: String = superProtocols.joined(separator: ",") 25 | str += " : " + superStr; 26 | } 27 | str += " {\n"; 28 | str += intent + "//flags \(flags.hex), numRequirements \(numRequirements)" + "\n" 29 | for astype in associatedTypeNames.split(separator: " ") { 30 | str += intent + "associatedtype " + String(astype) + "\n" 31 | } 32 | 33 | str += "}\n" 34 | return str; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/Util/ContextDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | // https://github.com/apple/swift/blob/a021e6ca020e667ce4bc8ee174e2de1cc0d9be73/include/swift/ABI/MetadataValues.h#L1183 13 | enum SDContextDescriptorKind: UInt8, CustomStringConvertible { 14 | /// This context descriptor represents a module. 15 | case Module = 0 16 | 17 | /// This context descriptor represents an extension. 18 | case Extension = 1 19 | 20 | /// This context descriptor represents an anonymous possibly-generic context 21 | /// such as a function body. 22 | case Anonymous = 2 23 | 24 | /// This context descriptor represents a protocol context. 25 | case SwiftProtocol = 3 26 | 27 | /// This context descriptor represents an opaque type alias. 28 | case OpaqueType = 4 29 | 30 | /// First kind that represents a type of any sort. 31 | //case Type_First = 16 32 | 33 | /// This context descriptor represents a class. 34 | case Class = 16 // Type_First 35 | 36 | /// This context descriptor represents a struct. 37 | case Struct = 17 // Type_First + 1 38 | 39 | /// This context descriptor represents an enum. 40 | case Enum = 18 // Type_First + 2 41 | 42 | /// Last kind that represents a type of any sort. 43 | case Type_Last = 31 44 | 45 | case Unknow = 0xFF // It's not in swift source, this value only used for dump 46 | 47 | var description: String { 48 | switch self { 49 | case .Module: return "module"; 50 | case .Extension: return "extension"; 51 | case .Anonymous: return "anonymous"; 52 | case .SwiftProtocol: return "protocol"; 53 | case .OpaqueType: return "OpaqueType"; 54 | case .Class: return "class"; 55 | case .Struct: return "struct"; 56 | case .Enum: return "enum"; 57 | case .Type_Last: return "Type_Last"; 58 | case .Unknow: return "unknow"; 59 | } 60 | } 61 | } 62 | 63 | // https://github.com/apple/swift/blob/a021e6ca020e667ce4bc8ee174e2de1cc0d9be73/include/swift/ABI/MetadataValues.h#L1217 64 | struct SDContextDescriptorFlags:CustomStringConvertible { 65 | let value: UInt32 66 | init(_ value: UInt32) { 67 | self.value = value; 68 | } 69 | 70 | /// The kind of context this descriptor describes. 71 | var kind: SDContextDescriptorKind { 72 | if let kind = SDContextDescriptorKind(rawValue: UInt8( value & 0x1F ) ) { 73 | return kind; 74 | } 75 | return SDContextDescriptorKind.Unknow; 76 | } 77 | 78 | /// Whether the context being described is generic. 79 | var isGeneric: Bool { 80 | return (value & 0x80) != 0; 81 | } 82 | 83 | /// Whether this is a unique record describing the referenced context. 84 | var isUnique: Bool { 85 | return (value & 0x40) != 0; 86 | } 87 | 88 | /// The format version of the descriptor. Higher version numbers may have 89 | /// additional fields that aren't present in older versions. 90 | var version: UInt8 { 91 | return UInt8((value >> 8) & 0xFF); 92 | } 93 | 94 | /// The most significant two bytes of the flags word, which can have 95 | /// kind-specific meaning. 96 | var kindSpecificFlags: UInt16 { 97 | return UInt16((value >> 16) & 0xFFFF); 98 | } 99 | 100 | var description: String { 101 | let kindDesc: String = kind.description; 102 | let kindSpecificFlagsStr: String = String(format: "0x%x", kindSpecificFlags); 103 | 104 | var desc: String = "<\(value.hex), \(kindDesc),"; 105 | if isGeneric { 106 | desc += " isGeneric," 107 | } 108 | if isUnique { 109 | desc += " isUnique," 110 | } else { 111 | desc += " NotUnique," 112 | } 113 | 114 | desc += " version \(version), kindSpecificFlags \(kindSpecificFlagsStr)>"; 115 | return desc; 116 | } 117 | } 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/Util/Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ext.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Array where Element == UInt8 { 12 | var hex: String { 13 | let tmp = self.reduce("") { (result, val:UInt8) -> String in 14 | return result + String(format: "%02x", val); 15 | } 16 | return tmp; 17 | } 18 | } 19 | 20 | extension String { 21 | func isAsciiStr() -> Bool { 22 | return self.range(of: ".*[^A-Za-z0-9_$ ].*", options: .regularExpression) == nil; 23 | } 24 | 25 | var hexData: Data { .init(hexa) } 26 | var hexBytes: [UInt8] { .init(hexa) } 27 | private var hexa: UnfoldSequence { 28 | sequence(state: startIndex) { startIndex in 29 | guard startIndex < self.endIndex else { return nil } 30 | let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex 31 | defer { startIndex = endIndex } 32 | return UInt8(self[startIndex.. UnsafePointer? { 37 | guard let data = self.data(using: String.Encoding.utf8) else { return nil } 38 | 39 | let buffer = UnsafeMutablePointer.allocate(capacity: data.count) 40 | let stream = OutputStream(toBuffer: buffer, capacity: data.count) 41 | 42 | stream.open() 43 | data.withUnsafeBytes { (bp:UnsafeRawBufferPointer) in 44 | if let sp:UnsafePointer = bp.baseAddress?.bindMemory(to: UInt8.self, capacity: MemoryLayout.stride) { 45 | stream.write(sp, maxLength: data.count) 46 | } 47 | } 48 | 49 | stream.close() 50 | 51 | return UnsafePointer(buffer) 52 | } 53 | func toCharPointer() -> UnsafePointer { 54 | let strPtr:UnsafePointer = self.withCString { (ptr:UnsafePointer) -> UnsafePointer in 55 | return ptr; 56 | } 57 | return strPtr; 58 | } 59 | 60 | 61 | public func removingPrefix(_ prefix: String) -> String { 62 | guard hasPrefix(prefix) else { return self } 63 | return String(dropFirst(prefix.count)) 64 | } 65 | public func removingSuffix(_ suffix: String) -> String { 66 | guard hasSuffix(suffix) else { return self } 67 | return String(dropLast(suffix.count)) 68 | } 69 | } 70 | 71 | 72 | 73 | extension Int64 { 74 | var hex: String { 75 | return String(format: "0x%llx", self); 76 | } 77 | } 78 | 79 | extension UInt64 { 80 | var hex: String { 81 | return String(format: "0x%llx", self); 82 | } 83 | } 84 | 85 | 86 | extension Int { 87 | var hex: String { 88 | return String(format: "0x%llx", self); 89 | } 90 | } 91 | extension Int32 { 92 | var hex: String { 93 | return String(format: "0x%llx", self); 94 | } 95 | } 96 | extension UInt32 { 97 | var hex: String { 98 | return String(format: "0x%llx", self); 99 | } 100 | } 101 | 102 | extension Data { 103 | 104 | func readS8(offset: Int) -> Int8 { 105 | return readValue(offset) ?? 0 106 | } 107 | func readU8(offset: Int) -> UInt8 { 108 | return readValue(offset) ?? 0 109 | } 110 | 111 | func readS16(offset: Int) -> Int16 { 112 | return readValue(offset) ?? 0 113 | } 114 | 115 | func readS32(offset: Int) -> Int32 { 116 | return readValue(offset) ?? 0 117 | } 118 | 119 | func readU32(offset: Int) -> UInt32 { 120 | return readValue(offset) ?? 0 121 | } 122 | func readU64(offset: Int) -> UInt64 { 123 | return readValue(offset) ?? 0 124 | } 125 | 126 | 127 | func readValue(_ offset: Int) -> Type? { 128 | let val:Type? = self.withUnsafeBytes { (ptr:UnsafeRawBufferPointer) -> Type? in 129 | return ptr.baseAddress?.advanced(by: offset).loadUnaligned(as: Type.self); 130 | } 131 | return val; 132 | } 133 | 134 | func readCString(from: Int) -> String? { 135 | if (from >= self.count) { 136 | return nil; 137 | } 138 | var address: Int = (from); 139 | var result:[UInt8] = []; 140 | while true { 141 | let val: UInt8 = self[address]; 142 | if (val == 0) { 143 | break; 144 | } 145 | address += 1; 146 | result.append(val); 147 | } 148 | 149 | if let str = String(bytes: result, encoding: String.Encoding.ascii) { 150 | if (str.isAsciiStr()) { 151 | return str; 152 | } 153 | } 154 | 155 | if (result.count > 10000) { 156 | return nil 157 | } 158 | 159 | let tmp = result.reduce("0x") { (result, val:UInt8) -> String in 160 | return result + String(format: "%02x", val); 161 | } 162 | return tmp; 163 | } 164 | } 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/Util/Log.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Log.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum LogLevel: Int { 12 | case debug = 1 13 | //case info = 2 14 | case warn = 3 15 | case error = 4 16 | } 17 | 18 | fileprivate var loglevel: LogLevel = .warn; 19 | 20 | func enableDebugLog() { 21 | loglevel = .debug; 22 | } 23 | 24 | func Log(_ msg: String, level:LogLevel = .debug, file: String = #file, method: String = #function, line: Int = #line) { 25 | 26 | if (level.rawValue < loglevel.rawValue ) { 27 | return; 28 | } 29 | 30 | var filename = file.components(separatedBy: "/").last ?? "unknowfile" 31 | filename = filename.components(separatedBy: ".").first ?? filename 32 | let methodPrefix: String = method.components(separatedBy: "(").first ?? method; 33 | 34 | let str: String = "[\(filename) \(methodPrefix)] \(msg)"; 35 | print(str) 36 | } 37 | 38 | func LogError(_ msg: String, file: String = #file, method: String = #function, line: Int = #line) { 39 | let prefix: String = "[error]" 40 | Log(prefix + msg, level: .error, file: file, method: method, line: line) 41 | } 42 | 43 | func LogWarn(_ msg: String, file: String = #file, method: String = #function, line: Int = #line) { 44 | let prefix: String = "[warn]" 45 | Log(prefix + msg, level: .warn, file: file, method: method, line: line) 46 | } 47 | 48 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/Util/RuntimeBridge.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeBridge.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @_silgen_name("swift_getTypeByMangledNameInContext") 12 | public func _getTypeByMangledNameInContext(_ name: UnsafePointer, 13 | _ nameLength: Int, 14 | genericContext: UnsafeRawPointer?, 15 | genericArguments: UnsafeRawPointer?) -> Any.Type? 16 | 17 | 18 | // size_t swift_demangle_getDemangledName(const char *MangledName, char *OutputBuffer,size_t Length) 19 | @_silgen_name("swift_demangle_getDemangledName") 20 | public func _getDemangledName(_ name:UnsafePointer?, output:UnsafeMutablePointer?, len:Int) -> Int; 21 | 22 | 23 | 24 | // ex. So8UIButtonCSg -> UIButton? 25 | // if demangle fail, will return the origin string 26 | // Only demangle str start with So/$So/_$so/_T 27 | func canDemangleFromRuntime(_ instr: String) -> Bool { 28 | return instr.hasPrefix("So") || instr.hasPrefix("$So") || instr.hasPrefix("_$So") || instr.hasPrefix("_T") 29 | } 30 | func runtimeGetDemangledName(_ instr: String) -> String { 31 | var str: String = instr; 32 | if (instr.hasPrefix("$s")) { 33 | str = instr; 34 | } else if (instr.hasPrefix("So")) { 35 | str = "$s" + instr; 36 | } else if (instr.hasPrefix("_T")) { 37 | // 38 | } else { 39 | return instr; 40 | } 41 | 42 | let strPtr:UnsafePointer = str.withCString { (ptr:UnsafePointer) -> UnsafePointer in 43 | return ptr; 44 | } 45 | 46 | let bufLen: Int = 128; // may be 128 is big enough 47 | var buf:[Int8] = Array(repeating: 0, count: bufLen); 48 | let retLen = _getDemangledName(strPtr, output: &buf, len: bufLen) 49 | 50 | if retLen > 0 && retLen < bufLen { 51 | let resultBuf:[UInt8] = buf[0.. String { 62 | if (canDemangleFromRuntime(str)) { 63 | return runtimeGetDemangledName(str); 64 | } 65 | //check is ascii string 66 | if (!str.isAsciiStr()) { 67 | return str; 68 | } 69 | guard let ptr = str.toPointer() else { 70 | return str; 71 | } 72 | 73 | var useCnt:Int = str.count 74 | if (str.hasSuffix("_pG")) { 75 | useCnt = useCnt - 3 76 | } 77 | 78 | guard let typeRet: Any.Type = _getTypeByMangledNameInContext(ptr, useCnt, genericContext: nil, genericArguments: nil) else { 79 | return str; 80 | } 81 | 82 | let tstr: String = String(describing: typeRet) 83 | //print("\(str) -> \(tstr)") 84 | return fixOptionalTypeName(tstr); 85 | } 86 | 87 | // Optional => Any.Type? 88 | // Optional => Int? 89 | func fixOptionalTypeName(_ typeName: String) -> String { 90 | let prefix: String = "Optional"; 91 | if (!typeName.hasPrefix(prefix)) { 92 | return typeName; 93 | } 94 | var name: String = typeName.removingPrefix(prefix); 95 | name = name.removingPrefix("<") 96 | name = name.removingSuffix(">") 97 | if (name.contains(" ")) { 98 | return "(" + name + ")?" 99 | } 100 | return name + "?"; 101 | } 102 | 103 | func removeSwiftModulePrefix(_ typeName: String) -> String { 104 | if let idx = typeName.firstIndex(of: ".") { 105 | let useIdx = typeName.index(after: idx) 106 | return String(typeName.suffix(from: useIdx )); 107 | } 108 | 109 | return typeName; 110 | } 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /SwiftDump/SwiftDump/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // SwiftDump 4 | // 5 | // Created by neilwu on 2020/6/26. 6 | // Copyright © 2020 nw. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ArgumentParser 11 | 12 | // https://github.com/apple/swift-argument-parser 13 | // https://github.com/apple/swift/blob/master/docs/ABI/Mangling.rst#symbolic-references 14 | 15 | func runMain(file: String, cpu: MachOCpuType) { 16 | let loader = SDFileLoader(file: file); 17 | let isSuccess: Bool = loader.load(cpu: cpu); 18 | if (!isSuccess) { 19 | LogError("fail to load file") 20 | return; 21 | } 22 | let parser = SDParser(with: loader); 23 | parser.parseSwiftProtos(); // find all Protocol 24 | parser.parseSwiftType(); // find all Type 25 | parser.parseSwiftProto(); // parse after Protocol & Type 26 | parser.parseSwiftOCClass(); 27 | 28 | parser.dumpAll(); 29 | } 30 | 31 | fileprivate let SDVersion: String = "1.0"; 32 | fileprivate let SDBuildTime: String = "2020-06-26"; 33 | 34 | struct SwiftDump: ParsableCommand { 35 | 36 | @Flag(name: .shortAndLong, help: "Show debug log.") 37 | var debug: Bool = false; 38 | 39 | @Option(name: .shortAndLong, help: "Choose architecture from a fat binary (only support x86_64/arm64).") 40 | var arch: String = "arm64" 41 | 42 | @Flag(name: .shortAndLong, help: "Version") 43 | var version: Bool = false; 44 | 45 | @Argument(help: "MachO File") 46 | var file: String = ""; 47 | 48 | 49 | mutating func run() throws { 50 | if (version) { 51 | print("SwiftDump v\(SDVersion) \(SDBuildTime). Created by neilwu."); 52 | if (file.isEmpty) { 53 | return; 54 | } 55 | print("\n"); 56 | } 57 | 58 | let isFileExist = FileManager.default.fileExists(atPath: file) 59 | if (!isFileExist) { 60 | print("input file [\(file)] does not exist") 61 | return; 62 | } 63 | //print("[run] debug ", debug) 64 | if (debug) { 65 | enableDebugLog(); 66 | } 67 | #if DEBUG 68 | //enableDebugLog(); 69 | #endif 70 | guard let archVal = self.getArchVal() else { 71 | print("Fail to find architecture [\(self.arch)] in [\(file)], SwiftDump only support x86_64/arm64"); 72 | return; 73 | } 74 | 75 | runMain(file: file, cpu: archVal); 76 | } 77 | 78 | func getArchVal() -> MachOCpuType? { 79 | if let val = MachOCpuType(rawValue: self.arch) { 80 | return val; 81 | } 82 | return nil; 83 | } 84 | 85 | 86 | } 87 | 88 | SwiftDump.main() 89 | 90 | --------------------------------------------------------------------------------