├── .github ├── FUNDING.yml └── workflows │ ├── Build.yml │ └── Release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .swiftformat ├── .swiftlint.yml ├── Brewfile ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── CodeSignParser │ ├── ASN1Decoder │ │ ├── ASN1Decoder.swift │ │ ├── ASN1DistinguishedNames.swift │ │ ├── ASN1Encoder.swift │ │ ├── ASN1Identifier.swift │ │ ├── ASN1Object.swift │ │ ├── OID.swift │ │ ├── PKCS7.swift │ │ ├── PKCS7_AppleReceipt.swift │ │ ├── PKCS7_Signature.swift │ │ ├── X509Certificate.swift │ │ ├── X509Extension.swift │ │ ├── X509ExtensionAltName.swift │ │ ├── X509ExtensionClasses.swift │ │ └── X509PublicKey.swift │ └── CodeSignature │ │ ├── CodeSignature.swift │ │ └── TypeDefine │ │ ├── External │ │ ├── CodeDirectory.swift │ │ └── HashType.swift │ │ └── Internal │ │ └── Blob.swift ├── MachCore │ ├── Extensions │ │ ├── BinaryInteger.Hex.swift │ │ ├── Data.CType.swift │ │ ├── Data.Magic.swift │ │ ├── Range.Initialize.swift │ │ ├── Sequence.unique.swift │ │ ├── String.Ascii.swift │ │ ├── String.capitalizedOnFirstLetter.swift │ │ ├── UnsafeRawPointer.CString.swift │ │ ├── UnsafeRawPointer.CType.swift │ │ └── UnsafeRawPointer.LEB128.swift │ └── Operator │ │ └── Operator.swift ├── MachLog │ └── Logger.swift ├── MachOCLI │ ├── CLI │ │ ├── Commands │ │ │ ├── Info │ │ │ │ └── Info.swift │ │ │ ├── NullifySymbolTable │ │ │ │ └── NullifySymbolTable.swift │ │ │ ├── ReplaceString │ │ │ │ └── ReplaceString.swift │ │ │ └── StripFilePath │ │ │ │ └── StripFilePath.swift │ │ └── MachOCLI.swift │ └── main.swift ├── MachOEditor │ ├── Editor │ │ └── Editor.swift │ ├── Error │ │ └── Error.swift │ ├── Extensions │ │ └── Data.Replace.swift │ ├── MachOTypes │ │ ├── Fat │ │ │ ├── MutableFat.Architecture.swift │ │ │ └── MutableFat.swift │ │ ├── Image │ │ │ ├── MutableImage.Content.swift │ │ │ └── MutableImage.swift │ │ └── Mach │ │ │ └── MutableMach.swift │ └── RepleaceStyle │ │ └── ReplaceStyle.swift ├── MachOObjcParser │ ├── Mach │ │ ├── Mach.Data.swift │ │ ├── Mach.ObjC.swift │ │ ├── Mach.VM.swift │ │ └── Section │ │ │ ├── Name │ │ │ └── Section.Name.swift │ │ │ ├── Section.__DATA__objc_catlist.swift │ │ │ ├── Section.__DATA__objc_classlist.swift │ │ │ ├── Section.__DATA__objc_protolist.swift │ │ │ ├── Section.__TEXT__objc_classname.swift │ │ │ ├── Section.__TEXT__objc_methname.swift │ │ │ └── Section.__TEXT__objc_methtype.swift │ ├── Objc │ │ ├── ArchitectureDependent.swift │ │ ├── ImageObjClass.swift │ │ ├── ImageObjClassRO.swift │ │ ├── ObjC.swift │ │ └── Symbols │ │ │ ├── FromMach.swift │ │ │ ├── Implements │ │ │ ├── ObjcCategoryImpl.swift │ │ │ ├── ObjcClassImpl.swift │ │ │ ├── ObjcIvarImpl.swift │ │ │ ├── ObjcMethodImpl.swift │ │ │ ├── ObjcPropertyImpl.swift │ │ │ └── ObjcProtocolImpl.swift │ │ │ ├── ObjcArchitecture.swift │ │ │ ├── ObjcArchitecture32.swift │ │ │ ├── ObjcArchitecture64.swift │ │ │ ├── ObjcCategory.swift │ │ │ ├── ObjcClass.swift │ │ │ ├── ObjcImage.swift │ │ │ ├── ObjcIvar.swift │ │ │ ├── ObjcMethod.swift │ │ │ ├── ObjcProperty.swift │ │ │ └── ObjcProtocol.swift │ └── ValueInData │ │ ├── ContainedInData.swift │ │ ├── Data.ContainedInData.CString.swift │ │ ├── MangledObjcClassNameInData.swift │ │ ├── PlainStringInData.swift │ │ └── Section.ContainedInDataType.swift ├── MachOParser │ ├── Define │ │ ├── BitWide.swift │ │ ├── ByteOrder.swift │ │ └── Option.swift │ ├── Extensions │ │ └── MachO.LoadCommand.Name.swift │ └── MachOTypes │ │ ├── CPU │ │ ├── CPUSubType.swift │ │ └── CPUType.swift │ │ ├── DyldInfo │ │ └── DyldInfo.swift │ │ ├── Fat │ │ ├── Fat.Architecture._32_.swift │ │ ├── Fat.Architecture._64_.swift │ │ ├── Fat.Architecture.swift │ │ ├── Fat.Error.swift │ │ ├── Fat.Header.swift │ │ └── Fat.swift │ │ ├── Headers │ │ ├── SectionHeader.swift │ │ └── SectionHeader64.swift │ │ ├── Image │ │ ├── Image.Content.swift │ │ ├── Image.Error.swift │ │ └── Image.swift │ │ ├── ImportStack │ │ ├── Arrary.Import.swift │ │ └── Import.swift │ │ ├── LoadCommand │ │ ├── BuildVersionLC.swift │ │ ├── CodeSignatureLC.swift │ │ ├── DataInCodeLC.swift │ │ ├── DyldChainedFixupsLC.swift │ │ ├── DyldEnvironmentLC.swift │ │ ├── DyldExportsTrieLC.swift │ │ ├── DyldInfoLC.swift │ │ ├── DyldInfoOnlyLC.swift │ │ ├── DylibCodeSignDrsLC.swift │ │ ├── DynamicSymbolTableLC.swift │ │ ├── EncryptionInfo64LC.swift │ │ ├── EncryptionInfoLC.swift │ │ ├── FixedVMFileLC.swift │ │ ├── FunctionStartsLC.swift │ │ ├── IDDylibLC.swift │ │ ├── IDDylinkerLC.swift │ │ ├── LazyLoadDylibLC.swift │ │ ├── LinkerOptimizationHintLC.swift │ │ ├── LinkerOptionLC.swift │ │ ├── LoadCommand.swift │ │ ├── LoadDylibLC.swift │ │ ├── LoadDylinkerLC.swift │ │ ├── LoadUpwardDylibLC.swift │ │ ├── LoadWeakDylibLC.swift │ │ ├── MainLC.swift │ │ ├── NoteLC.swift │ │ ├── PrebindChecksumLC.swift │ │ ├── ReexportedDylibLC.swift │ │ ├── Routines64LC.swift │ │ ├── RoutinesLC.swift │ │ ├── RunPathLC.swift │ │ ├── Segment64LC.swift │ │ ├── SegmentLC.swift │ │ ├── SegmentSplitInfoLC.swift │ │ ├── SourceVersionLC.swift │ │ ├── SubClientLC.swift │ │ ├── SubFrameworkLC.swift │ │ ├── SubLibraryLC.swift │ │ ├── SubUmbrellaLC.swift │ │ ├── SymbolTableLC.swift │ │ ├── ThreadLC.swift │ │ ├── TwoLevelHintsLC.swift │ │ ├── UUIDLC.swift │ │ ├── UnixThreadLC.swift │ │ ├── VersionMinIphoneosLC.swift │ │ ├── VersionMinMacosxLC.swift │ │ ├── VersionMinTvosLC.swift │ │ └── VersionMinWatchosLC.swift │ │ ├── Mach │ │ ├── Functions │ │ │ ├── Mach.LoadCommand.swift │ │ │ └── Mach.Section.swift │ │ ├── Mach.swift │ │ └── SubTypes │ │ │ ├── Mach.Error.swift │ │ │ ├── Mach.Header.FileType.swift │ │ │ ├── Mach.Header.Flag.swift │ │ │ ├── Mach.Header._32_.swift │ │ │ ├── Mach.Header._64_.swift │ │ │ └── Mach.Header.swift │ │ ├── ProcessMach │ │ ├── Functions │ │ │ └── ProcessMach.LoadCommand.swift │ │ ├── ProcessMach.swift │ │ └── SubTypes │ │ │ ├── ProcessMach.Error.swift │ │ │ └── ProcessMach.Header.swift │ │ ├── Section │ │ ├── Other │ │ │ └── Section.__RODATA__cstring.swift │ │ ├── Section.swift │ │ ├── SectionContent.swift │ │ ├── SectionName.swift │ │ └── __Text │ │ │ └── __cstring │ │ │ └── Section.__TEXT__cstring.swift │ │ ├── Segment │ │ ├── Segment.swift │ │ └── SegmentName.swift │ │ ├── Symbols │ │ ├── StringTable.Symbol.swift │ │ ├── StringTable.swift │ │ └── SymbolTable.swift │ │ ├── Trie │ │ └── Trie.swift │ │ └── Version │ │ ├── SourceVersion.swift │ │ └── Version.swift ├── MachOSwiftParser │ ├── Mach │ │ └── Section │ │ │ └── Section.Name.swift │ ├── SwiftDemangler.swift │ ├── SwiftMeta │ │ ├── Protocol │ │ │ ├── Description │ │ │ │ └── SwiftMeta.ProtocolDescriptor.swift │ │ │ └── SwiftMeta.ProtocolType.swift │ │ ├── SwiftMeta.swift │ │ └── Type │ │ │ ├── Description │ │ │ ├── SwiftMeta.ClassDescriptor.swift │ │ │ ├── SwiftMeta.ContextDescriptorFlags.swift │ │ │ ├── SwiftMeta.ContextDescriptorKind.swift │ │ │ ├── SwiftMeta.EnumDescriptor.swift │ │ │ ├── SwiftMeta.FieldDescriptor.swift │ │ │ ├── SwiftMeta.FieldRecord.swift │ │ │ ├── SwiftMeta.NominalDescriptor.swift │ │ │ └── SwiftMeta.StructDescriptor.swift │ │ │ ├── SwiftMeta.ClassType.swift │ │ │ └── SwiftMeta.Field.swift │ └── SwiftParser.swift └── ObjCObfuscation │ ├── Mangling │ └── SymbolMangling.swift │ └── Symbol │ ├── Mach.CPUID.swift │ ├── ObfuscationSymbols.swift │ ├── ObjCSymbols.swift │ └── SymbolManglingMap.swift └── Tests ├── MachOEditorTests └── MachOEditorTests.swift └── MachOParserTests └── MachOParserTests.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [L1MeN9Yu] 4 | -------------------------------------------------------------------------------- /.github/workflows/Build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build_on_mac: 13 | 14 | runs-on: macos-12 15 | 16 | steps: 17 | - uses: maxim-lobanov/setup-xcode@v1 18 | with: 19 | xcode-version: latest-stable 20 | - uses: actions/checkout@v2 21 | with: 22 | submodules: recursive 23 | - name: Build 24 | run: swift build -v 25 | - name: Run tests 26 | run: swift test --enable-code-coverage -v 27 | - name: Swift Coverage Report 28 | run: xcrun llvm-cov export -format="lcov" .build/debug/MachObjectPackageTests.xctest/Contents/MacOS/MachObjectPackageTests -instr-profile .build/debug/codecov/default.profdata > .build/debug/codecov/info.lcov 29 | - name: Upload coverage to Codecov 30 | uses: codecov/codecov-action@v1 31 | 32 | # build_on_linux: 33 | # 34 | # runs-on: ubuntu-latest 35 | # container: 36 | # image: swift:5.5.3-focal 37 | # steps: 38 | # - uses: actions/checkout@v2 39 | # with: 40 | # submodules: recursive 41 | # - name: Build 42 | # run: swift build -v 43 | # - name: Run tests 44 | # run: swift test -v 45 | -------------------------------------------------------------------------------- /.github/workflows/Release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*.*.*" 7 | 8 | jobs: 9 | release: 10 | runs-on: macos-12 11 | 12 | steps: 13 | - uses: maxim-lobanov/setup-xcode@v1 14 | with: 15 | xcode-version: latest-stable 16 | - uses: actions/checkout@v2 17 | with: 18 | submodules: recursive 19 | - name: Build 20 | run: swift build -c release MachOCLI 21 | - name: Release 22 | uses: softprops/action-gh-release@v1 23 | with: 24 | files: | 25 | .build/release/MachOCLI 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | Brewfile.lock.json 7 | .idea 8 | Brewfile.lock.json 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.3.0 6 | hooks: 7 | - id: trailing-whitespace 8 | - id: end-of-file-fixer 9 | - id: check-yaml 10 | - id: check-added-large-files 11 | - id: detect-private-key 12 | - id: check-merge-conflict 13 | - repo: https://github.com/hodovani/pre-commit-swift 14 | rev: 0551a937b9f98a839fd98d2c3e6ce0b6c0a1e093 15 | hooks: 16 | - id: swift-lint 17 | - id: swift-format 18 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --indent 4 2 | --indentcase false 3 | --trimwhitespace always 4 | --empty tuple 5 | --nospaceoperators ..<,... 6 | --ifdef noindent 7 | --stripunusedargs closure-only 8 | --disable andOperator 9 | --swiftversion 5.3 10 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_comma 3 | - identifier_name 4 | - void_return 5 | - operator_whitespace 6 | - nesting 7 | - cyclomatic_complexity 8 | - multiple_closures_with_trailing_closure 9 | - type_name 10 | - todo 11 | - large_tuple 12 | 13 | opt_in_rules: 14 | - indentation_width 15 | 16 | line_length: 220 17 | 18 | indentation_width: 4 19 | 20 | function_body_length: 21 | - 50 22 | 23 | inclusive_language: 24 | override_allowed_terms: 25 | - blacklist 26 | - whitelist 27 | 28 | included: 29 | - Sources 30 | - Tests 31 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew "pre-commit" 2 | brew "swiftformat" 3 | brew "swiftlint" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 L1MeN9Yu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Senna", 6 | "repositoryURL": "https://github.com/L1MeN9Yu/Senna.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "0d769b074013682cf78bd0c643c2953dac5b8bf8", 10 | "version": "2.5.0" 11 | } 12 | }, 13 | { 14 | "package": "swift-argument-parser", 15 | "repositoryURL": "https://github.com/apple/swift-argument-parser", 16 | "state": { 17 | "branch": null, 18 | "revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca", 19 | "version": "0.5.0" 20 | } 21 | }, 22 | { 23 | "package": "swift-log", 24 | "repositoryURL": "https://github.com/apple/swift-log.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "6fe203dc33195667ce1759bf0182975e4653ba1c", 28 | "version": "1.4.4" 29 | } 30 | } 31 | ] 32 | }, 33 | "version": 1 34 | } 35 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "MachObject", 8 | products: [ 9 | .library(name: "MachOParser", targets: ["MachOParser"]), 10 | .library(name: "CodeSignParser", targets: ["CodeSignParser"]), 11 | .executable(name: "MachOCLI", targets: ["MachOCLI"]), 12 | ], 13 | dependencies: [ 14 | .package(url: "https://github.com/apple/swift-argument-parser", from: "0.5.0"), 15 | .package(url: "https://github.com/apple/swift-log.git", from: "1.4.4"), 16 | .package(url: "https://github.com/L1MeN9Yu/Senna.git", from: "2.5.0"), 17 | ], 18 | targets: [ 19 | .target(name: "MachCore"), 20 | .target(name: "MachLog", dependencies: [ 21 | .product(name: "Logging", package: "swift-log"), 22 | .product(name: "Senna", package: "Senna"), 23 | ]), 24 | .target(name: "CodeSignParser", dependencies: [ 25 | .target(name: "MachCore"), 26 | ]), 27 | .target(name: "MachOParser", dependencies: [ 28 | .target(name: "MachCore"), 29 | .target(name: "CodeSignParser"), 30 | ]), 31 | .target(name: "MachOObjcParser", dependencies: [ 32 | .target(name: "MachCore"), 33 | .target(name: "MachOParser"), 34 | ]), 35 | .target(name: "ObjCObfuscation", dependencies: [ 36 | .target(name: "MachCore"), 37 | .target(name: "MachOParser"), 38 | ]), 39 | .target(name: "MachOSwiftParser", dependencies: [ 40 | .target(name: "MachCore"), 41 | .target(name: "MachOParser"), 42 | ], linkerSettings: [ 43 | LinkerSetting.linkedLibrary("swiftDemangle"), 44 | ]), 45 | .target(name: "MachOEditor", dependencies: [ 46 | .target(name: "MachCore"), 47 | .target(name: "MachOParser"), 48 | .target(name: "MachOSwiftParser"), 49 | .target(name: "MachOObjcParser"), 50 | .target(name: "ObjCObfuscation"), 51 | .target(name: "MachLog"), 52 | ]), 53 | .target(name: "MachOCLI", dependencies: [ 54 | .target(name: "MachOParser"), 55 | .target(name: "MachOEditor"), 56 | .product(name: "ArgumentParser", package: "swift-argument-parser"), 57 | ]), 58 | .testTarget(name: "MachOEditorTests", dependencies: ["MachOEditor"]), 59 | .testTarget(name: "MachOParserTests", dependencies: ["MachOParser"]), 60 | ] 61 | ) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MachObject 2 | 3 | [WIP] A Macho file parser and editor project. 4 | 5 | ## Status 6 | 7 | [![Build](https://github.com/L1MeN9Yu/MachObject/actions/workflows/Build.yml/badge.svg)](https://github.com/L1MeN9Yu/MachObject/actions/workflows/Build.yml) 8 | [![codecov](https://codecov.io/gh/L1MeN9Yu/MachObject/branch/main/graph/badge.svg?token=35ZWOL070P)](https://codecov.io/gh/L1MeN9Yu/MachObject) 9 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/ASN1Encoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Encoder.swift 3 | // ASN1Decoder 4 | // 5 | // Copyright © 2020 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public enum ASN1DEREncoder { 28 | public static func encodeSequence(content: Data) -> Data { 29 | var encoded = Data() 30 | encoded.append(ASN1Identifier.constructedTag | ASN1Identifier.TagNumber.sequence.rawValue) 31 | encoded.append(contentLength(of: content.count)) 32 | encoded.append(content) 33 | return encoded 34 | } 35 | 36 | private static func contentLength(of size: Int) -> Data { 37 | if size >= 128 { 38 | var lenBytes = byteArray(from: size) 39 | while lenBytes.first == 0 { lenBytes.removeFirst() } 40 | let len: UInt8 = 0x80 | UInt8(lenBytes.count) 41 | return Data([len] + lenBytes) 42 | } else { 43 | return Data([UInt8(size)]) 44 | } 45 | } 46 | 47 | private static func byteArray(from value: T) -> [UInt8] where T: FixedWidthInteger { 48 | withUnsafeBytes(of: value.bigEndian, Array.init) 49 | } 50 | } 51 | 52 | public extension Data { 53 | var derEncodedSequence: Data { 54 | ASN1DEREncoder.encodeSequence(content: self) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/ASN1Identifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Identifier.swift 3 | // 4 | // Copyright © 2017 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class ASN1Identifier: CustomStringConvertible { 27 | public enum Class: UInt8 { 28 | case universal = 0x00 29 | case application = 0x40 30 | case contextSpecific = 0x80 31 | case `private` = 0xC0 32 | } 33 | 34 | public enum TagNumber: UInt8 { 35 | case endOfContent = 0x00 36 | case boolean = 0x01 37 | case integer = 0x02 38 | case bitString = 0x03 39 | case octetString = 0x04 40 | case null = 0x05 41 | case objectIdentifier = 0x06 42 | case objectDescriptor = 0x07 43 | case external = 0x08 44 | case read = 0x09 45 | case enumerated = 0x0A 46 | case embeddedPdv = 0x0B 47 | case utf8String = 0x0C 48 | case relativeOid = 0x0D 49 | case sequence = 0x10 50 | case set = 0x11 51 | case numericString = 0x12 52 | case printableString = 0x13 53 | case t61String = 0x14 54 | case videotexString = 0x15 55 | case ia5String = 0x16 56 | case utcTime = 0x17 57 | case generalizedTime = 0x18 58 | case graphicString = 0x19 59 | case visibleString = 0x1A 60 | case generalString = 0x1B 61 | case universalString = 0x1C 62 | case characterString = 0x1D 63 | case bmpString = 0x1E 64 | } 65 | 66 | public static let constructedTag: UInt8 = 0x20 67 | 68 | var rawValue: UInt8 69 | 70 | init(rawValue: UInt8) { 71 | self.rawValue = rawValue 72 | } 73 | 74 | public func typeClass() -> Class { 75 | for tc in [Class.application, Class.contextSpecific, Class.private] where (rawValue & tc.rawValue) == tc.rawValue { 76 | return tc 77 | } 78 | return .universal 79 | } 80 | 81 | public func isPrimitive() -> Bool { 82 | (rawValue & ASN1Identifier.constructedTag) == 0 83 | } 84 | 85 | public func isConstructed() -> Bool { 86 | (rawValue & ASN1Identifier.constructedTag) != 0 87 | } 88 | 89 | public func tagNumber() -> TagNumber { 90 | TagNumber(rawValue: rawValue & 0x1F) ?? .endOfContent 91 | } 92 | 93 | public var description: String { 94 | if typeClass() == .universal { 95 | return String(describing: tagNumber()) 96 | } else { 97 | return "\(typeClass())(\(tagNumber().rawValue))" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/ASN1Object.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Object.swift 3 | // 4 | // Copyright © 2017 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class ASN1Object: CustomStringConvertible { 27 | /// This property contains the DER encoded object 28 | public var rawValue: Data? 29 | 30 | /// This property contains the decoded Swift object whenever is possible 31 | public var value: Any? 32 | 33 | public var identifier: ASN1Identifier? 34 | 35 | var sub: [ASN1Object]? 36 | 37 | public internal(set) weak var parent: ASN1Object? 38 | 39 | public func sub(_ index: Int) -> ASN1Object? { 40 | if let sub = sub, index >= 0, index < sub.count { 41 | return sub[index] 42 | } 43 | return nil 44 | } 45 | 46 | public func subCount() -> Int { 47 | sub?.count ?? 0 48 | } 49 | 50 | public func findOid(_ oid: OID) -> ASN1Object? { 51 | findOid(oid.rawValue) 52 | } 53 | 54 | public func findOid(_ oid: String) -> ASN1Object? { 55 | for child in sub ?? [] { 56 | if child.identifier?.tagNumber() == .objectIdentifier { 57 | if child.value as? String == oid { 58 | return child 59 | } 60 | } else { 61 | if let result = child.findOid(oid) { 62 | return result 63 | } 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | public var description: String { 70 | printAsn1() 71 | } 72 | 73 | public var asString: String? { 74 | if let string = value as? String { 75 | return string 76 | } 77 | 78 | for item in sub ?? [] { 79 | if let string = item.asString { 80 | return string 81 | } 82 | } 83 | 84 | return nil 85 | } 86 | 87 | fileprivate func printAsn1(insets: String = "") -> String { 88 | var output = insets 89 | output.append(identifier?.description.uppercased() ?? "") 90 | output.append(value != nil ? ": \(value!)" : "") 91 | if identifier?.typeClass() == .universal, identifier?.tagNumber() == .objectIdentifier { 92 | if let oidName = OID.description(of: value as? String ?? "") { 93 | output.append(" (\(oidName))") 94 | } 95 | } 96 | output.append(sub != nil && sub!.count > 0 ? " {" : "") 97 | output.append("\n") 98 | for item in sub ?? [] { 99 | output.append(item.printAsn1(insets: insets + " ")) 100 | } 101 | output.append(sub != nil && sub!.count > 0 ? insets + "}\n" : "") 102 | return output 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/OID.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OID.swift 3 | // ASN1Decoder 4 | // 5 | // Created by Filippo Maguolo on 01/12/2019. 6 | // Copyright © 2019 Filippo Maguolo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum OID: String { 12 | case etsiQcsCompliance = "0.4.0.1862.1.1" 13 | case etsiQcsRetentionPeriod = "0.4.0.1862.1.3" 14 | case etsiQcsQcSSCD = "0.4.0.1862.1.4" 15 | case dsa = "1.2.840.10040.4.1" 16 | case ecPublicKey = "1.2.840.10045.2.1" 17 | case prime256v1 = "1.2.840.10045.3.1.7" 18 | case ecdsaWithSHA256 = "1.2.840.10045.4.3.2" 19 | case ecdsaWithSHA512 = "1.2.840.10045.4.3.4" 20 | case rsaEncryption = "1.2.840.113549.1.1.1" 21 | case sha256WithRSAEncryption = "1.2.840.113549.1.1.11" 22 | case md5WithRSAEncryption = "1.2.840.113549.1.1.4" 23 | case sha1WithRSAEncryption = "1.2.840.113549.1.1.5" 24 | 25 | // Digest algorithms 26 | case sha1 = "1.3.14.3.2.26" 27 | case pkcsSha256 = "1.3.6.1.4.1.22554.1.2.1" 28 | case sha2Family = "1.3.6.1.4.1.22554.1.2" 29 | case sha3_244 = "2.16.840.1.101.3.4.2.7" 30 | case sha3_256 = "2.16.840.1.101.3.4.2.8" 31 | case sha3_384 = "2.16.840.1.101.3.4.2.9" 32 | case md5 = "0.2.262.1.10.1.3.2" 33 | 34 | case pkcs7data = "1.2.840.113549.1.7.1" 35 | case pkcs7signedData = "1.2.840.113549.1.7.2" 36 | case pkcs7envelopedData = "1.2.840.113549.1.7.3" 37 | case emailAddress = "1.2.840.113549.1.9.1" 38 | case signingCertificateV2 = "1.2.840.113549.1.9.16.2.47" 39 | case contentType = "1.2.840.113549.1.9.3" 40 | case messageDigest = "1.2.840.113549.1.9.4" 41 | case signingTime = "1.2.840.113549.1.9.5" 42 | case certificateExtension = "1.3.6.1.4.1.11129.2.4.2" 43 | case jurisdictionLocalityName = "1.3.6.1.4.1.311.60.2.1.1" 44 | case jurisdictionStateOrProvinceName = "1.3.6.1.4.1.311.60.2.1.2" 45 | case jurisdictionCountryName = "1.3.6.1.4.1.311.60.2.1.3" 46 | case authorityInfoAccess = "1.3.6.1.5.5.7.1.1" 47 | case qcStatements = "1.3.6.1.5.5.7.1.3" 48 | case cps = "1.3.6.1.5.5.7.2.1" 49 | case unotice = "1.3.6.1.5.5.7.2.2" 50 | case serverAuth = "1.3.6.1.5.5.7.3.1" 51 | case clientAuth = "1.3.6.1.5.5.7.3.2" 52 | case ocsp = "1.3.6.1.5.5.7.48.1" 53 | case caIssuers = "1.3.6.1.5.5.7.48.2" 54 | case dateOfBirth = "1.3.6.1.5.5.7.9.1" 55 | case sha256 = "2.16.840.1.101.3.4.2.1" 56 | case VeriSignEVpolicy = "2.16.840.1.113733.1.7.23.6" 57 | case extendedValidation = "2.23.140.1.1" 58 | case organizationValidated = "2.23.140.1.2.2" 59 | case subjectKeyIdentifier = "2.5.29.14" 60 | case keyUsage = "2.5.29.15" 61 | case subjectAltName = "2.5.29.17" 62 | case issuerAltName = "2.5.29.18" 63 | case basicConstraints = "2.5.29.19" 64 | case cRLDistributionPoints = "2.5.29.31" 65 | case certificatePolicies = "2.5.29.32" 66 | case authorityKeyIdentifier = "2.5.29.35" 67 | case extKeyUsage = "2.5.29.37" 68 | case subjectDirectoryAttributes = "2.5.29.9" 69 | 70 | // X.500 attributes 71 | case commonName = "2.5.4.3" 72 | case surname = "2.5.4.4" 73 | case serialNumber = "2.5.4.5" 74 | case countryName = "2.5.4.6" 75 | case localityName = "2.5.4.7" 76 | case stateOrProvinceName = "2.5.4.8" 77 | case streetAddress = "2.5.4.9" 78 | case organizationName = "2.5.4.10" 79 | case organizationalUnitName = "2.5.4.11" 80 | case businessCategory = "2.5.4.15" 81 | case postalCode = "2.5.4.17" 82 | case givenName = "2.5.4.42" 83 | case dnQualifier = "2.5.4.46" 84 | 85 | case domainComponent = "0.9.2342.19200300.100.1.25" 86 | 87 | case userId = "0.9.2342.19200300.100.1.1" 88 | 89 | static func description(of value: String) -> String? { 90 | guard let oid = OID(rawValue: value) else { 91 | return nil 92 | } 93 | return "\(oid)" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/PKCS7.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PKCS7.swift 3 | // 4 | // Copyright © 2017 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class PKCS7 { 27 | public let mainBlock: ASN1Object 28 | 29 | public init(data: Data) throws { 30 | let asn1 = try ASN1DERDecoder.decode(data: data) 31 | 32 | guard let firstBlock = asn1.first, 33 | let mainBlock = firstBlock.sub(1)?.sub(0) 34 | else { 35 | throw PKCS7Error.parseError 36 | } 37 | 38 | self.mainBlock = mainBlock 39 | 40 | guard firstBlock.sub(0)?.value as? String == OID.pkcs7signedData.rawValue else { 41 | throw PKCS7Error.notSupported 42 | } 43 | } 44 | 45 | public var digestAlgorithm: String? { 46 | if let block = mainBlock.sub(1) { 47 | return firstLeafValue(block: block) as? String 48 | } 49 | return nil 50 | } 51 | 52 | public var digestAlgorithmName: String? { 53 | OID.description(of: digestAlgorithm ?? "") ?? digestAlgorithm 54 | } 55 | 56 | public var certificate: X509Certificate? { 57 | mainBlock.sub(3)?.sub?.first.map { try? X509Certificate(asn1: $0) } ?? nil 58 | } 59 | 60 | public var certificates: [X509Certificate] { 61 | mainBlock.sub(3)?.sub?.compactMap { try? X509Certificate(asn1: $0) } ?? [] 62 | } 63 | 64 | public var data: Data? { 65 | if let block = mainBlock.findOid(.pkcs7data) { 66 | if let dataBlock = block.parent?.sub?.last { 67 | var out = Data() 68 | if let value = dataBlock.value as? Data { 69 | out.append(value) 70 | } else if dataBlock.value is String, let rawValue = dataBlock.rawValue { 71 | out.append(rawValue) 72 | } else { 73 | for sub in dataBlock.sub ?? [] { 74 | if let value = sub.value as? Data { 75 | out.append(value) 76 | } else if sub.value is String, let rawValue = sub.rawValue { 77 | out.append(rawValue) 78 | } else { 79 | for sub2 in sub.sub ?? [] { 80 | if let value = sub2.rawValue { 81 | out.append(value) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | return out.count > 0 ? out : nil 88 | } 89 | } 90 | return nil 91 | } 92 | } 93 | 94 | enum PKCS7Error: Error { 95 | case notSupported 96 | case parseError 97 | } 98 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/X509Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509Extension.swift 3 | // 4 | // Copyright © 2019 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class X509Extension { 27 | let block: ASN1Object 28 | 29 | required init(block: ASN1Object) { 30 | self.block = block 31 | } 32 | 33 | public var oid: String? { 34 | block.sub(0)?.value as? String 35 | } 36 | 37 | public var name: String? { 38 | OID.description(of: oid ?? "") 39 | } 40 | 41 | public var isCritical: Bool { 42 | if block.sub?.count ?? 0 > 2 { 43 | return block.sub(1)?.value as? Bool ?? false 44 | } 45 | return false 46 | } 47 | 48 | public var value: Any? { 49 | if let valueBlock = block.sub?.last { 50 | return firstLeafValue(block: valueBlock) 51 | } 52 | return nil 53 | } 54 | 55 | var valueAsBlock: ASN1Object? { 56 | block.sub?.last 57 | } 58 | 59 | var valueAsStrings: [String] { 60 | var result: [String] = [] 61 | for item in block.sub?.last?.sub?.last?.sub ?? [] { 62 | if let name = item.value as? String { 63 | result.append(name) 64 | } 65 | } 66 | return result 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/X509ExtensionAltName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509ExtensionAltName.swift 3 | // 4 | // Copyright © 2020 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | extension X509Extension { 27 | // Used for SubjectAltName and IssuerAltName 28 | // Every name can be one of these subtype: 29 | // - otherName [0] INSTANCE OF OTHER-NAME, 30 | // - rfc822Name [1] IA5String, 31 | // - dNSName [2] IA5String, 32 | // - x400Address [3] ORAddress, 33 | // - directoryName [4] Name, 34 | // - ediPartyName [5] EDIPartyName, 35 | // - uniformResourceIdentifier [6] IA5String, 36 | // - IPAddress [7] OCTET STRING, 37 | // - registeredID [8] OBJECT IDENTIFIER 38 | // 39 | // Result does not support: x400Address and ediPartyName 40 | // 41 | var alternativeNameAsStrings: [String] { 42 | var result: [String] = [] 43 | for item in block.sub?.last?.sub?.last?.sub ?? [] { 44 | guard let name = generalName(of: item) else { 45 | continue 46 | } 47 | result.append(name) 48 | } 49 | return result 50 | } 51 | 52 | func generalName(of item: ASN1Object) -> String? { 53 | guard let nameType = item.identifier?.tagNumber().rawValue else { 54 | return nil 55 | } 56 | switch nameType { 57 | case 0: 58 | if let name = item.sub?.last?.sub?.last?.value as? String { 59 | return name 60 | } 61 | case 1, 2, 6: 62 | if let name = item.value as? String { 63 | return name 64 | } 65 | case 4: 66 | if let sequence = item.sub(0) { 67 | return ASN1DistinguishedNameFormatter.string(from: sequence) 68 | } 69 | case 7: 70 | if let ip = item.value as? Data { 71 | return ip.map { "\($0)" }.joined(separator: ".") 72 | } 73 | case 8: 74 | if let value = item.value as? String, var data = value.data(using: .utf8) { 75 | let oid = ASN1DERDecoder.decodeOid(contentData: &data) 76 | return oid 77 | } 78 | default: 79 | return nil 80 | } 81 | return nil 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/ASN1Decoder/X509PublicKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509PublicKey.swift 3 | // 4 | // Copyright © 2019 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class X509PublicKey { 27 | let pkBlock: ASN1Object 28 | 29 | init(pkBlock: ASN1Object) { 30 | self.pkBlock = pkBlock 31 | } 32 | 33 | public var algOid: String? { 34 | pkBlock.sub(0)?.sub(0)?.value as? String 35 | } 36 | 37 | public var algName: String? { 38 | OID.description(of: algOid ?? "") 39 | } 40 | 41 | public var algParams: String? { 42 | pkBlock.sub(0)?.sub(1)?.value as? String 43 | } 44 | 45 | public var derEncodedKey: Data? { 46 | pkBlock.rawValue?.derEncodedSequence 47 | } 48 | 49 | public var key: Data? { 50 | guard 51 | let algOid = algOid, 52 | let oid = OID(rawValue: algOid), 53 | let keyData = pkBlock.sub(1)?.value as? Data 54 | else { 55 | return nil 56 | } 57 | 58 | switch oid { 59 | case .ecPublicKey: 60 | return keyData 61 | 62 | case .rsaEncryption: 63 | guard let publicKeyAsn1Objects = (try? ASN1DERDecoder.decode(data: keyData)) else { 64 | return nil 65 | } 66 | guard let publicKeyModulus = publicKeyAsn1Objects.first?.sub(0)?.value as? Data else { 67 | return nil 68 | } 69 | return publicKeyModulus 70 | 71 | default: 72 | return nil 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/CodeSignature/TypeDefine/External/CodeDirectory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/11/18. 3 | // 4 | 5 | import Foundation 6 | 7 | public struct CodeDirectory { 8 | public let version: String 9 | public let ident: String 10 | public let team: String? 11 | public let hashType: HashType? 12 | 13 | public init(version: UInt32, ident: String, team: String?, hashType: UInt8) { 14 | self.version = String(version, radix: 16) 15 | self.ident = ident 16 | self.team = team 17 | self.hashType = HashType(rawValue: hashType) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/CodeSignature/TypeDefine/External/HashType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/11/18. 3 | // 4 | 5 | import Foundation 6 | 7 | public enum HashType: UInt8 { 8 | case sha1 = 1 9 | case sha256 = 2 10 | case sha256Truncated = 3 11 | case sha384 = 4 12 | } 13 | -------------------------------------------------------------------------------- /Sources/CodeSignParser/CodeSignature/TypeDefine/Internal/Blob.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/11/18. 3 | // 4 | 5 | struct BlobIndex { 6 | let type: UInt32 7 | let offset: UInt32 8 | } 9 | 10 | struct Blob { 11 | let magic: UInt32 12 | let length: UInt32 13 | } 14 | 15 | struct SuperBlob { 16 | let blob: Blob 17 | let count: UInt32 18 | } 19 | 20 | enum Slot: UInt32 { 21 | case codeDirectory = 0x00000 22 | case info = 0x00001 23 | case requirements = 0x00002 24 | case resourcesDirectory = 0x00003 25 | case application = 0x00004 26 | case entitlements = 0x00005 27 | case alternate = 0x01000 28 | case signature = 0x10000 // CMS Signature 29 | } 30 | 31 | struct CodeDirectoryBlob { 32 | let magic: UInt32 /* magic number (CSMAGIC_CODEDIRECTORY) */ 33 | let length: UInt32 /* total length of CodeDirectory blob */ 34 | let version: UInt32 /* compatibility version */ 35 | let flags: UInt32 /* setup and mode flags */ 36 | let hashOffset: UInt32 /* offset of hash slot element at index zero */ 37 | let identOffset: UInt32 /* offset of identifier string */ 38 | let nSpecialSlots: UInt32 /* number of special hash slots */ 39 | let nCodeSlots: UInt32 /* number of ordinary (code) hash slots */ 40 | let codeLimit: UInt32 /* limit to main image signature range */ 41 | let hashSize: UInt8 /* size of each hash in bytes */ 42 | let hashType: UInt8 /* type of hash (cdHashType* constants) */ 43 | let spare1: UInt8 /* unused (must be zero) */ 44 | let pageSize: UInt8 /* log2(page size in bytes); 0 => infinite */ 45 | let spare2: UInt32 /* unused (must be zero) */ 46 | // char end_earliest[0]; 47 | 48 | /* Version 0x20100 */ 49 | let scatterOffset: UInt32 /* offset of optional scatter vector */ 50 | // char end_withScatter[0]; 51 | 52 | /* Version 0x20200 */ 53 | let teamOffset: UInt32 /* offset of optional team identifier */ 54 | // char end_withTeam[0]; 55 | 56 | /* Version 0x20300 */ 57 | let spare3: UInt32 /* unused (must be zero) */ 58 | let codeLimit64: UInt64 /* limit to main image signature range, 64 bits */ 59 | // char end_withCodeLimit64[0]; 60 | 61 | /* Version 0x20400 */ 62 | let execSegBase: UInt64 /* offset of executable segment */ 63 | let execSegLimit: UInt64 /* limit of executable segment */ 64 | let execSegFlags: UInt64 /* executable segment flags */ 65 | // char end_withExecSeg[0]; 66 | 67 | /* followed by dynamic content as located by offset fields above */ 68 | } 69 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/BinaryInteger.Hex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/10/19. 3 | // 4 | 5 | extension BinaryInteger { 6 | var hex: String { 7 | "0x" + String(self, radix: 16) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/Data.CType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/23. 3 | // 4 | 5 | import struct Foundation.Data 6 | 7 | public extension Data { 8 | func get(atOffset offset: Int) -> T { 9 | withUnsafeBytes { $0.baseAddress!.advanced(by: offset).get() } 10 | } 11 | 12 | func get(atOffset offset: Int, count: Int) -> [T] { 13 | withUnsafeBytes { $0.baseAddress!.advanced(by: offset).get(count: count) } 14 | } 15 | 16 | func get(fromRange range: Range) -> [T] { 17 | get(atOffset: range.startIndex, count: range.count / MemoryLayout.stride) 18 | } 19 | 20 | func get(atOffset offset: Int, fallbackConvert: Bool = false) -> String { 21 | withUnsafeBytes { 22 | switch fallbackConvert { 23 | case false: 24 | return $0.bindMemory(to: UInt8.self).baseAddress!.advanced(by: offset) |> String.init(cString:) 25 | case true: 26 | var address: Int = offset 27 | var result: [UInt8] = [] 28 | while true { 29 | let val: UInt8 = self[address] 30 | if val == 0 { break } 31 | address += 1 32 | result.append(val) 33 | } 34 | 35 | if let str = String(bytes: result, encoding: String.Encoding.ascii) { 36 | if str.isASCII { return str } 37 | } 38 | 39 | return result.reduce("0x") { (result, val: UInt8) -> String in result + String(format: "%02x", val) } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/Data.Magic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/23. 3 | // 4 | 5 | import struct Foundation.Data 6 | 7 | public extension Data { 8 | typealias SizeType = UInt32 9 | var magic: SizeType? { 10 | guard count >= MemoryLayout.size else { return nil } 11 | return withUnsafeBytes { $0.load(as: SizeType.self) } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/Range.Initialize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | public extension Range where Bound: BinaryInteger { 6 | init(offset: Bound, count: Bound) { 7 | self = offset..<(offset + count) 8 | } 9 | 10 | var intRange: Range { Int(lowerBound).. { Set(self) } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/String.Ascii.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | 7 | public extension String { 8 | var isASCII: Bool { 9 | unicodeScalars.allSatisfy { (element: UnicodeScalarView.Element) -> Bool in element.isASCII } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/String.capitalizedOnFirstLetter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | 7 | public extension String { 8 | var capitalizedOnFirstLetter: String { 9 | prefix(1).capitalized + dropFirst() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/UnsafeRawPointer.CString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/9/29. 3 | // 4 | 5 | import struct Foundation.Data 6 | 7 | public extension UnsafeRawPointer { 8 | mutating func readStringBytes() -> [UInt8] { 9 | let basePtr = self 10 | while load(as: UInt8.self) != 0 { self = advanced(by: 1) } 11 | // skip terminal 0 12 | defer { self = advanced(by: 1) } 13 | return [UInt8](UnsafeRawBufferPointer(start: basePtr, count: basePtr.distance(to: self))) 14 | } 15 | } 16 | 17 | public extension UnsafeRawPointer { 18 | func getString() -> String { 19 | var pointer = self 20 | let bytes = pointer.readStringBytes() 21 | switch String(data: Data(bytes), encoding: .utf8) { 22 | case .none: 23 | return bytes.reduce("0x") { (result, val: UInt8) -> String in result + String(format: "%02x", val) } 24 | case let .some(value): 25 | return value 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/UnsafeRawPointer.CType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/23. 3 | // 4 | 5 | import Foundation 6 | 7 | public extension UnsafeRawPointer { 8 | func get() -> T { 9 | bindMemory(to: T.self, capacity: 1).pointee 10 | } 11 | 12 | func get(count: Int) -> [T] { 13 | bindMemory(to: T.self, capacity: count) 14 | |> { t in UnsafeBufferPointer(start: t, count: count) } 15 | |> [T].init 16 | } 17 | } 18 | 19 | public extension UnsafeRawPointer { 20 | mutating func read() -> T { 21 | defer { 22 | self = advanced(by: MemoryLayout.stride) 23 | } 24 | return get() 25 | } 26 | 27 | mutating func read(count: Int) -> [T] { 28 | defer { 29 | self = advanced(by: MemoryLayout.stride * count) 30 | } 31 | return get(count: count) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachCore/Extensions/UnsafeRawPointer.LEB128.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/9/29. 3 | // 4 | 5 | public extension UnsafeRawPointer { 6 | mutating func readSleb128() -> Int64 { 7 | // continuation bit is 1 in default leb128 implementation 8 | readLeb128(continuationBitState: true) 9 | } 10 | 11 | mutating func readNibSleb128() -> Int64 { 12 | // Nib file format uses uleb-like coding, but uses 0 as a continuation bit 13 | readLeb128(continuationBitState: false) 14 | } 15 | 16 | mutating func readUleb128() -> UInt64 { 17 | // continuation bit is 1 in default leb128 implementation 18 | readLeb128(continuationBitState: true) 19 | } 20 | 21 | mutating func readNibUleb128() -> UInt64 { 22 | // Nib file format uses uleb-like coding, but uses 0 as a continuation bit 23 | readLeb128(continuationBitState: false) 24 | } 25 | 26 | private mutating func readLeb128(continuationBitState: Bool) -> T { 27 | var accumulator: T = 0 28 | var group: UInt8 29 | var shift = 0 30 | let maxShift = (MemoryLayout.size * 8) - 1 31 | repeat { 32 | if shift > maxShift { 33 | fatalError("sleb128 too long to be represented as \(T.self)") 34 | } 35 | group = load(as: UInt8.self) 36 | accumulator |= T(group & 0x7F) << shift 37 | shift += 7 38 | self = advanced(by: 1) 39 | } while (group & 0x80 != 0) == continuationBitState 40 | 41 | if T.isSigned { 42 | let isNegative = group >> 6 & 0x01 != 0 43 | if isNegative { 44 | accumulator |= ~T(0) << min(shift, maxShift) // 1-bit padding 45 | } else { 46 | accumulator &= T.max // clear sign bit (possible 1-bits overflow) 47 | } 48 | } 49 | 50 | return accumulator 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/MachCore/Operator/Operator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/23. 3 | // 4 | 5 | precedencegroup Forward { 6 | associativity: left 7 | } 8 | 9 | infix operator |>: Forward 10 | 11 | func |> (value: T, function: (T) -> U) -> U { function(value) } 12 | -------------------------------------------------------------------------------- /Sources/MachLog/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/30. 3 | // 4 | 5 | import Foundation 6 | import Logging 7 | import Senna 8 | 9 | public let logger = Logger(label: "Logger") { 10 | Handler(name: $0, sink: StandardSink.out(), formation: Formation.standard, logLevel: .trace) 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOCLI/CLI/Commands/Info/Info.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/3. 3 | // 4 | 5 | import ArgumentParser 6 | import Foundation 7 | import MachOObjcParser 8 | import MachOParser 9 | 10 | struct Info: ParsableCommand { 11 | static let configuration = CommandConfiguration(abstract: "Show Macho File Information") 12 | 13 | @Argument(help: "mach file path") 14 | var macho: String 15 | 16 | mutating func run() throws { 17 | let fileURL = URL(fileURLWithPath: macho) 18 | let image = try Image.load(url: fileURL) 19 | let content = image.content 20 | switch content { 21 | case let .fat(fat): 22 | fat.architectures.forEach { 23 | show(mach: $0.mach) 24 | } 25 | case let .mach(mach): 26 | show(mach: mach) 27 | } 28 | } 29 | } 30 | 31 | private extension Info { 32 | func show(mach: Mach) { 33 | header(mach: mach) 34 | loadCommands(mach: mach) 35 | codeSignature(mach: mach) 36 | } 37 | } 38 | 39 | private extension Info { 40 | func header(mach: Mach) { 41 | print("header ====>>") 42 | print("cpu type : \(mach.header.cpuType)") 43 | print("file type : \(mach.header.fileType)") 44 | print("command count : \(mach.header.commandCount)") 45 | print("command size : \(mach.header.commandSize)") 46 | print("flags : \(mach.header.flags)") 47 | } 48 | 49 | func loadCommands(mach: Mach) { 50 | print("load commands ====>>") 51 | mach.allLoadCommands.forEach { 52 | print("\($0)") 53 | } 54 | } 55 | 56 | func codeSignature(mach: Mach) { 57 | guard let codeSignature = mach.codeSignature else { return } 58 | print("code signature ====>>") 59 | print("\(codeSignature)") 60 | } 61 | } 62 | 63 | private extension Info { 64 | func showObjCClassList(mach: Mach) { 65 | let objcClassList = mach.objcClasses 66 | objcClassList.forEach { (objcClass: ObjcClass) in 67 | print("\(objcClass)") 68 | } 69 | } 70 | 71 | func showImportStack(mach: Mach) { 72 | mach.importStack.forEach { 73 | print("\($0.symbolString)") 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sources/MachOCLI/CLI/Commands/NullifySymbolTable/NullifySymbolTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/11/27. 3 | // 4 | 5 | import ArgumentParser 6 | import Foundation 7 | import MachOEditor 8 | 9 | struct NullifySymbolTable: ParsableCommand { 10 | @Argument(help: ArgumentHelp(stringLiteral: "macho file path")) 11 | var macho: String 12 | 13 | @Argument(help: ArgumentHelp(stringLiteral: "output file path")) 14 | var outputFile: String 15 | 16 | func run() throws { 17 | let data = try Editor.eraseSymbolTable(macho: URL(fileURLWithPath: macho)) 18 | try data.write(to: URL(fileURLWithPath: outputFile), options: .atomic) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOCLI/CLI/Commands/ReplaceString/ReplaceString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/3. 3 | // 4 | 5 | import ArgumentParser 6 | import Foundation 7 | import MachOEditor 8 | import MachOParser 9 | 10 | struct ReplaceString: ParsableCommand { 11 | static let configuration = CommandConfiguration(abstract: "Replace Strings in Macho File CString ") 12 | 13 | @Argument(help: ArgumentHelp(stringLiteral: "macho file path")) 14 | var macho: String 15 | 16 | @Argument(help: ArgumentHelp(stringLiteral: "the string need to be replaced")) 17 | var keyword: String 18 | 19 | @Argument(help: ArgumentHelp(stringLiteral: "the replacement string")) 20 | var replacement: String 21 | 22 | @Argument(help: ArgumentHelp(stringLiteral: "output file path")) 23 | var outputFile: String 24 | 25 | @Flag(help: "Replace Whole String") 26 | var whole: Bool = false 27 | 28 | mutating func run() throws { 29 | let replaceStyle: ReplaceStyle = whole ? .wholeString : .onlyKeyword 30 | let data = try Editor.replace( 31 | keyword: keyword, replacement: replacement, 32 | replaceStyle: replaceStyle, macho: URL(fileURLWithPath: macho) 33 | ) 34 | try data.write(to: URL(fileURLWithPath: outputFile), options: .atomic) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/MachOCLI/CLI/Commands/StripFilePath/StripFilePath.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/3. 3 | // 4 | 5 | import ArgumentParser 6 | import Foundation 7 | import MachOEditor 8 | 9 | struct StripFilePath: ParsableCommand { 10 | static let configuration = CommandConfiguration( 11 | abstract: "Strip File Path Strings in Macho File CString " 12 | ) 13 | 14 | @Argument(help: ArgumentHelp(stringLiteral: "macho file path")) 15 | var macho: String 16 | 17 | @Argument(help: ArgumentHelp(stringLiteral: "file path prefix")) 18 | var prefix: String 19 | 20 | @Option(name: [.short, .long], help: ArgumentHelp(stringLiteral: "replacement string,default is an empty string")) 21 | var replacement: String = "" 22 | 23 | @Argument(help: ArgumentHelp(stringLiteral: "output file path")) 24 | var outputFile: String 25 | 26 | mutating func run() throws { 27 | let data = try Editor.erase(filePath: [prefix], replacement: replacement, macho: URL(fileURLWithPath: macho)) 28 | try data.write(to: URL(fileURLWithPath: outputFile), options: .atomic) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOCLI/CLI/MachOCLI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/3. 3 | // 4 | 5 | import ArgumentParser 6 | import Foundation 7 | 8 | struct MachOCLI: ParsableCommand { 9 | static let configuration = CommandConfiguration( 10 | abstract: "MachO CLI", 11 | discussion: "The MachO Tool", 12 | version: "0.0.1", 13 | shouldDisplay: false, 14 | subcommands: [ 15 | Info.self, 16 | StripFilePath.self, 17 | ReplaceString.self, 18 | NullifySymbolTable.self, 19 | ], 20 | defaultSubcommand: Info.self 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOCLI/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/3. 3 | // 4 | 5 | MachOCLI.main() 6 | -------------------------------------------------------------------------------- /Sources/MachOEditor/Editor/Editor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/30. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public struct Editor { private init() {} } 9 | 10 | public extension Editor { 11 | static func erase(filePath prefixes: [String], replacement: String = "", macho url: URL) throws -> Data { 12 | let image = try Image.load(url: url) 13 | var mutableImage = MutableImage(image: image) 14 | try mutableImage.update { (mach: inout MutableMach) throws in 15 | try mach.erase(filePaths: prefixes, replacement: replacement) 16 | } 17 | return mutableImage.data 18 | } 19 | } 20 | 21 | public extension Editor { 22 | static func replace(keyword: String, replacement: String, replaceStyle: ReplaceStyle, macho url: URL) throws -> Data { 23 | let image = try Image.load(url: url) 24 | var mutableImage = MutableImage(image: image) 25 | try mutableImage.update { (mach: inout MutableMach) throws in 26 | try mach.replace(keyword: keyword, replacement: replacement, replaceStyle: replaceStyle) 27 | } 28 | return mutableImage.data 29 | } 30 | } 31 | 32 | public extension Editor { 33 | static func eraseSymbolTable(macho url: URL) throws -> Data { 34 | let image = try Image.load(url: url) 35 | var mutableImage = MutableImage(image: image) 36 | try mutableImage.update { (mach: inout MutableMach) in 37 | try mach.eraseSymbolTable() 38 | } 39 | return mutableImage.data 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOEditor/Error/Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | enum Error: Swift.Error { 9 | case replaceStringLength 10 | case sectionNotFound(name: SegmentName) 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOEditor/Extensions/Data.Replace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | import Foundation 6 | 7 | extension Data { 8 | mutating func nullify(range: Range) { 9 | let nullReplacement = Data(repeating: 0, count: range.count) 10 | replaceSubrange(range, with: nullReplacement) 11 | } 12 | } 13 | 14 | extension Data { 15 | mutating func replaceString(range: Range, filter: (String) -> Bool = { _ in true }, mapping: (String) -> String) { 16 | func stringRanges(in range: Range) -> [String: [Range]] { 17 | let enumeratedBytes: ArraySlice<(offset: Int, element: UInt8)> = Array(enumerated())[range] 18 | let chunksOfEnumeratedBytes = enumeratedBytes.split { _, data in data == 0 } 19 | let stringWithRangePairs: [(String, Range)] = chunksOfEnumeratedBytes.compactMap { chunk in 20 | let chunkBytes = chunk.map { _, data in data } 21 | guard let string = String(bytes: chunkBytes, encoding: .utf8) else { return nil } 22 | let chunkArray = Array(chunk) 23 | let range = (chunkArray.first!.offset..<(chunkArray.last!.offset + 1)) 24 | return (string, range) 25 | } 26 | return Dictionary(grouping: stringWithRangePairs) { string, _ in string } 27 | .mapValues { $0.map { _, range in range } } 28 | } 29 | 30 | let rangesPerString = stringRanges(in: range) 31 | rangesPerString.filter { filter($0.key) } 32 | .forEach { (originalString: String, ranges: [Range]) in 33 | let mappedString = mapping(originalString) 34 | precondition(originalString.utf8.count >= mappedString.utf8.count) 35 | guard let mappedData = mappedString.data(using: .utf8) else { return } 36 | ranges.forEach { replaceWithPadding(range: $0, data: mappedData) } 37 | } 38 | } 39 | } 40 | 41 | extension Data { 42 | mutating func replaceWithPadding(range: Range, data: Data) { 43 | precondition(range.count >= data.count) 44 | let targetDataWithPadding = data + Array(repeating: UInt8(0), count: range.count - data.count) 45 | assert(range.count == targetDataWithPadding.count) 46 | replaceSubrange(range, with: targetDataWithPadding) 47 | } 48 | 49 | mutating func replaceWithPadding(range: Range, string: String) { 50 | guard let data = string.data(using: .utf8) else { fatalError() } 51 | replaceWithPadding(range: range, data: data) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/MachOEditor/MachOTypes/Fat/MutableFat.Architecture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | extension MutableFat { 9 | struct Architecture { 10 | private(set) var offset: UInt64 11 | private(set) var mach: MutableMach 12 | 13 | init(architecture: Fat.Architecture) { 14 | offset = architecture.offset 15 | mach = MutableMach(mach: architecture.mach) 16 | } 17 | } 18 | } 19 | 20 | extension MutableFat.Architecture { 21 | mutating func update(action: (inout MutableMach) throws -> ()) throws { 22 | var mutableMach = mach 23 | try action(&mutableMach) 24 | mach = mutableMach 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOEditor/MachOTypes/Fat/MutableFat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct MutableFat { 9 | var data: Data 10 | var architectures: [Architecture] 11 | 12 | init(fat: Fat) { 13 | data = fat.data 14 | architectures = fat.architectures.map { architecture -> Architecture in 15 | Architecture(architecture: architecture) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/MachOEditor/MachOTypes/Image/MutableImage.Content.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | extension MutableImage { 9 | enum Content { 10 | case fat(MutableFat) 11 | case mach(MutableMach) 12 | 13 | init(content: Image.Content) { 14 | switch content { 15 | case let .fat(fat): 16 | self = .fat(MutableFat(fat: fat)) 17 | case let .mach(mach): 18 | self = .mach(MutableMach(mach: mach)) 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOEditor/MachOTypes/Image/MutableImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct MutableImage { 9 | let url: URL 10 | private(set) var content: Content 11 | 12 | init(image: Image) { 13 | url = image.url 14 | content = Content(content: image.content) 15 | } 16 | } 17 | 18 | extension MutableImage { 19 | mutating func update(action: (inout MutableMach) throws -> ()) throws { 20 | switch content { 21 | case let .fat(fat): 22 | var mutableFat = fat 23 | mutableFat.architectures = try fat.architectures.map { arch in 24 | var mutableArch = arch 25 | try mutableArch.update(action: action) 26 | return mutableArch 27 | } 28 | for arch in mutableFat.architectures { 29 | let archRangeInFat = Range(offset: Int(arch.offset), count: arch.mach.data.count) 30 | mutableFat.data.replaceSubrange(archRangeInFat, with: arch.mach.data) 31 | } 32 | content = .fat(mutableFat) 33 | case let .mach(mach): 34 | var mutableMach = mach 35 | try action(&mutableMach) 36 | content = .mach(mutableMach) 37 | } 38 | } 39 | } 40 | 41 | // MARK: - Save 42 | 43 | extension MutableImage { 44 | var data: Data { 45 | switch content { 46 | case let .fat(fat): 47 | return fat.data 48 | case let .mach(mach): 49 | return mach.data 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/MachOEditor/MachOTypes/Mach/MutableMach.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | import Foundation 6 | @_implementationOnly import MachCore 7 | @_implementationOnly import MachLog 8 | import MachOObjcParser 9 | import MachOParser 10 | 11 | struct MutableMach { 12 | private(set) var data: Data 13 | 14 | init(mach: Mach) { data = mach.data } 15 | } 16 | 17 | // MARK: - Erase 18 | 19 | extension MutableMach { 20 | mutating func erase(filePaths prefixes: [String], replacement: String) throws { 21 | guard !prefixes.isEmpty else { return } 22 | let mach = try Mach(data: data) 23 | guard let section = mach.section(of: SegmentName.__TEXT, name: SectionName.__cstring) else { 24 | throw Error.sectionNotFound(name: SegmentName.__TEXT) 25 | } 26 | data.replaceString(range: section.range.intRange, filter: { (string: String) -> Bool in 27 | string.utf8.count >= replacement.utf8.count && 28 | prefixes.contains(where: { prefix in string.starts(with: prefix) }) 29 | }, mapping: { _ in replacement }) 30 | } 31 | 32 | mutating func eraseSymbolTable() throws { 33 | let mach = try Mach(data: data) 34 | guard let symtab: SymbolTableLC = mach.loadCommand() else { return } 35 | let range = Range(offset: UInt64(symtab.stringTableOffset), count: UInt64(symtab.stringTableSize)) 36 | data.nullify(range: range.intRange) 37 | } 38 | } 39 | 40 | extension MutableMach { 41 | mutating func replace(keyword: String, replacement: String, replaceStyle: ReplaceStyle) throws { 42 | guard !keyword.isEmpty else { return } 43 | guard keyword.utf8.count >= replacement.utf8.count else { throw Error.replaceStringLength } 44 | let mach = try Mach(data: data) 45 | guard let section = mach.section(of: SegmentName.__TEXT, name: SectionName.__cstring) else { 46 | throw Error.sectionNotFound(name: SegmentName.__TEXT) 47 | } 48 | let filter: (String) -> Bool = { string in 49 | string.contains(keyword) 50 | } 51 | let mapping: (String) -> String = { originalString in 52 | switch replaceStyle { 53 | case .onlyKeyword: 54 | return originalString.replacingOccurrences(of: keyword, with: replacement) 55 | case .wholeString: 56 | return replacement 57 | } 58 | } 59 | data.replaceString( 60 | range: section.range.intRange, 61 | filter: filter, 62 | mapping: mapping 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/MachOEditor/RepleaceStyle/ReplaceStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | 7 | public enum ReplaceStyle { 8 | case onlyKeyword 9 | case wholeString 10 | } 11 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Mach.Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | extension Mach { 9 | func get(atFileOffset offset: Int) -> T { 10 | data.get(atOffset: offset) 11 | } 12 | 13 | func get(fromVmOffset offset: I) -> T { 14 | let objectFileAddress = fileOffset(fromVmOffset: offset) 15 | return get(atFileOffset: objectFileAddress) 16 | } 17 | 18 | func getCString(fromVmOffset offset: I) -> R where R: ContainedInData, R.ValueType == String { 19 | data.getCString(atOffset: fileOffset(fromVmOffset: offset)) 20 | } 21 | 22 | func get_entsize_list_tt(fromVmOffset offset: I, flags: UInt32 = 0) -> [Element] { 23 | guard offset != 0 else { return [] } 24 | let listFileOffset = fileOffset(fromVmOffset: offset) 25 | let list: ObjC.entsize_list_tt = get(atFileOffset: listFileOffset) 26 | let elementSize = list.entsizeAndFlags & ~flags 27 | 28 | let expectedSize = MemoryLayout.size 29 | guard elementSize == expectedSize else { 30 | fatalError( 31 | """ 32 | List of \(Element.self) defines entsize=\(elementSize), while \(expectedSize) expected. 33 | Looks like the binary is malformed. 34 | """ 35 | ) 36 | } 37 | 38 | let elements: [Element] = (0...size + Int(idx * elementSize)) 40 | } 41 | return elements 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Mach.ObjC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | private extension Mach { 9 | private var as32: Mach32 { Mach32(mach: self) } 10 | 11 | private var as64: Mach64 { Mach64(mach: self) } 12 | 13 | private var asArchitecture: ObjcImage { 14 | switch data.magic { 15 | case MH_MAGIC_64: 16 | return as64 17 | case MH_MAGIC: 18 | return as32 19 | default: 20 | fatalError("Unsupported mach binary magic \(String(data.magic ?? 0, radix: 0x10, uppercase: true))") 21 | } 22 | } 23 | 24 | private var objcClasslistSection: Section? { 25 | section(of: SegmentName.__DATA, name: SectionName.__objc_classlist) 26 | } 27 | 28 | private var objcCatlistSection: Section? { 29 | section(of: SegmentName.__DATA, name: SectionName.__objc_catlist) 30 | } 31 | 32 | private var objcProtocollistSection: Section? { 33 | section(of: SegmentName.__DATA, name: SectionName.__objc_protolist) 34 | } 35 | } 36 | 37 | public extension Mach { 38 | var objcClasses: [ObjcClass] { 39 | asArchitecture.getObjectsFromList(section: objcClasslistSection, creator: asArchitecture.create(offset:)) 40 | } 41 | 42 | var objcCategories: [ObjcCategory] { 43 | asArchitecture.getObjectsFromList(section: objcCatlistSection, creator: asArchitecture.create(offset:)) 44 | } 45 | 46 | var objcProtocols: [ObjcProtocol] { 47 | asArchitecture.getObjectsFromList(section: objcProtocollistSection, creator: asArchitecture.create(offset:)) 48 | } 49 | } 50 | 51 | public extension Mach { 52 | var classNamesInData: [MangledObjcClassNameInData] { 53 | (objcClasses.map(\.name) + objcProtocols.map(\.name)).filter { !$0.isSwift } + pureObjcCategoryNames 54 | } 55 | 56 | var classNames: [String] { 57 | classNamesInData.map(\.value) 58 | } 59 | 60 | private var pureObjcCategoryNames: [MangledObjcClassNameInData] { 61 | objcCategories.map(\.name).filter(isPureObjCCategory(_:)) 62 | } 63 | 64 | func isPureObjCCategory(_ name: MangledObjcClassNameInData) -> Bool { 65 | section(of: .__TEXT, name: .__objc_classname)?.contains(data: name) ?? false 66 | } 67 | } 68 | 69 | private class Mach32: MachArchitecture, ObjcArchitecture32 {} 70 | 71 | private class Mach64: MachArchitecture, ObjcArchitecture64 {} 72 | 73 | private class MachArchitecture: ObjcImage { 74 | private let mach: Mach 75 | 76 | init(mach: Mach) { 77 | self.mach = mach 78 | } 79 | 80 | private func getObjectsOffsetsFromList(section list: Section?) -> [Int] { 81 | guard let objcList = list else { return [] } 82 | 83 | let objectVMAddresses: [PointerType] = mach.data.get(fromRange: objcList.range.intRange) 84 | let objectFileAddresses: [Int] = objectVMAddresses.map(mach.fileOffset(fromVmOffset:)) 85 | return objectFileAddresses 86 | } 87 | 88 | func getObjectsFromList(section list: Section?, creator: (Int) -> Element) -> [Element] { 89 | getObjectsOffsetsFromList(section: list).map { creator($0) } 90 | } 91 | 92 | func create(offset: Int) -> ObjcClass { 93 | ObjcClassImpl(mach: mach, offset: offset) 94 | } 95 | 96 | func create(offset: Int) -> ObjcCategory { 97 | ObjcCategoryImpl(mach: mach, offset: offset) 98 | } 99 | 100 | func create(offset: Int) -> ObjcProtocol { 101 | ObjcProtocolImpl(mach: mach, offset: offset) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Mach.VM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | extension Mach { 9 | func fileOffset(fromVmOffset vmOffset: I) -> Int { 10 | var mutableSelf = self 11 | guard let segment = mutableSelf.segments.first(where: { $0.vmRange.contains(UInt64(vmOffset)) }) else { 12 | fatalError("vmOffset \(vmOffset) does not exist in the image") 13 | } 14 | 15 | let fileOffset = Int(segment.fileRange.lowerBound + (UInt64(vmOffset) - segment.vmRange.lowerBound)) 16 | return fileOffset 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Name/Section.Name.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension SectionName { 9 | static let __objc_methname = Self(rawValue: "__objc_methname") 10 | static let __objc_methtype = Self(rawValue: "__objc_methtype") 11 | static let __objc_classname = Self(rawValue: "__objc_classname") 12 | static let __objc_classlist = Self(rawValue: "__objc_classlist") 13 | static let __objc_protolist = Self(rawValue: "__objc_protolist") 14 | static let __objc_catlist = Self(rawValue: "__objc_catlist") 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Section.__DATA__objc_catlist.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | struct __DATA__objc_catlist: SectionContent { 10 | public typealias Value = Data 11 | public let value: Value 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange) 15 | } 16 | 17 | public static let segmentName: SegmentName = .__DATA 18 | public static let sectionName: SectionName = .__objc_catlist 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Section.__DATA__objc_classlist.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | struct __DATA__objc_classlist: SectionContent { 10 | public typealias Value = Data 11 | public let value: Value 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange) 15 | } 16 | 17 | public private(set) static var segmentName: SegmentName = .__DATA 18 | public private(set) static var sectionName: SectionName = .__objc_classlist 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Section.__DATA__objc_protolist.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | struct __DATA__objc_protolist: SectionContent { 10 | public typealias Value = Data 11 | public let value: Value 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange) 15 | } 16 | 17 | public static let segmentName: SegmentName = .__DATA 18 | public static let sectionName: SectionName = .__objc_protolist 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Section.__TEXT__objc_classname.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | struct __TEXT__objc_classname: SectionContent { 10 | public typealias Value = Data 11 | public let value: Value 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange) 15 | } 16 | 17 | public static let segmentName: SegmentName = .__TEXT 18 | public static let sectionName: SectionName = .__objc_classname 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Section.__TEXT__objc_methname.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | struct __TEXT__objc_methname: SectionContent { 10 | public typealias Value = Data 11 | public let value: Value 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange) 15 | } 16 | 17 | public static let segmentName: SegmentName = .__TEXT 18 | public static let sectionName: SectionName = .__objc_methname 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Mach/Section/Section.__TEXT__objc_methtype.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | struct __TEXT__objc_methtype: SectionContent { 10 | public typealias Value = Data 11 | public let value: Value 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange) 15 | } 16 | 17 | public static let segmentName: SegmentName = .__TEXT 18 | public static let sectionName: SectionName = .__objc_methtype 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/ArchitectureDependent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | /// Objc metadata structures with different 32-bit and 64-bit versions. 8 | public protocol ArchitectureDependent { 9 | /// Type of pointer for given architecture 10 | associatedtype PointerType: UnsignedInteger 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/ImageObjClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ImageObjClass: ArchitectureDependent { 8 | associatedtype RODataType: ImageObjClassRO where RODataType.PointerType == PointerType 9 | /// Pointer to class_ro calculated from data stored in image 10 | var data: PointerType { get } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/ImageObjClassRO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ImageObjClassRO: ArchitectureDependent { 8 | var ivarLayout: PointerType { get } 9 | var name: PointerType { get } 10 | var baseMethodList: PointerType { get } 11 | var baseProtocols: PointerType { get } 12 | var ivars: PointerType { get } 13 | var weakIvarLayout: PointerType { get } 14 | var baseProperties: PointerType { get } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/FromMach.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | protocol FromMach { 9 | init(mach: Mach, offset: Int) 10 | /// Structure describing raw metadata structure in image 11 | associatedtype Raw 12 | 13 | var mach: Mach { get } 14 | var offset: Int { get } 15 | } 16 | 17 | extension FromMach { 18 | var raw: Raw { 19 | mach.data.get(atOffset: offset) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/Implements/ObjcCategoryImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObjcCategoryImpl: FromMach { 9 | typealias Raw = ObjC.category_t 10 | let mach: Mach 11 | let offset: Int 12 | 13 | init(mach: Mach, offset: Int) { 14 | self.mach = mach 15 | self.offset = offset 16 | } 17 | } 18 | 19 | extension ObjcCategoryImpl: ObjcCategory { 20 | var name: MangledObjcClassNameInData { mach.getCString(fromVmOffset: raw.name) } 21 | 22 | var cls: ObjcClass? { 23 | guard raw.cls != 0 else { return nil } 24 | let offset = mach.fileOffset(fromVmOffset: raw.cls) 25 | return ObjcClassImpl(mach: mach, offset: offset) 26 | } 27 | 28 | var methods: [ObjcMethod] { 29 | // methods_list is entsize_list_tt with flags=0x3 30 | let list: [ObjcMethodImpl] = mach.get_entsize_list_tt(fromVmOffset: raw.instanceMethods, flags: 0x03) 31 | return list 32 | } 33 | 34 | var properties: [ObjcProperty] { 35 | // property_list is entsize_list_tt with flags=0 36 | let list: [ObjcPropertyImpl] = mach.get_entsize_list_tt(fromVmOffset: raw.instanceProperties) 37 | return list 38 | } 39 | } 40 | 41 | extension ObjcCategoryImpl: CustomStringConvertible { 42 | var description: String { "Category \(cls?.name.value ?? "")+\(name)\n methods: \(methods)\n properties: \(properties)" } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/Implements/ObjcClassImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObjcClassImpl: FromMach { 9 | typealias Raw = Arch.ClassData 10 | let mach: Mach 11 | let offset: Int 12 | 13 | init(mach: Mach, offset: Int) { 14 | self.mach = mach 15 | self.offset = offset 16 | } 17 | } 18 | 19 | extension ObjcClassImpl: ObjcClass { 20 | var ivarLayout: PlainStringInData? { 21 | guard class_ro.ivarLayout != 0 else { 22 | return nil 23 | } 24 | return mach.getCString(fromVmOffset: class_ro.ivarLayout) 25 | } 26 | 27 | var name: MangledObjcClassNameInData { 28 | mach.getCString(fromVmOffset: class_ro.name) 29 | } 30 | 31 | var methods: [ObjcMethod] { 32 | // methods_list is entsize_list_tt with flags=0x3 33 | let list: [ObjcMethodImpl] = mach.get_entsize_list_tt(fromVmOffset: class_ro.baseMethodList, flags: 0x03) 34 | return list 35 | } 36 | 37 | var ivars: [ObjcIvar] { 38 | // ivar_list is entsize_list_tt with flags=0 39 | let list: [ObjcIvarImpl] = mach.get_entsize_list_tt(fromVmOffset: class_ro.ivars) 40 | return list 41 | } 42 | 43 | var properties: [ObjcProperty] { 44 | // property_list is entsize_list_tt with flags=0 45 | let list: [ObjcPropertyImpl] = mach.get_entsize_list_tt(fromVmOffset: class_ro.baseProperties) 46 | return list 47 | } 48 | } 49 | 50 | private extension ObjcClassImpl { 51 | private var class_ro: Arch.ClassData.RODataType { mach.get(fromVmOffset: raw.data) } 52 | } 53 | 54 | extension ObjcClassImpl: CustomStringConvertible { 55 | var description: String { "Class \(name)\n methods: \(methods)\n properties: \(properties)\n ivars: \(ivars)" } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/Implements/ObjcIvarImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObjcIvarImpl: FromMach { 9 | typealias Raw = ObjC.ivar_t 10 | 11 | let mach: Mach 12 | let offset: Int 13 | 14 | init(mach: Mach, offset: Int) { 15 | self.mach = mach 16 | self.offset = offset 17 | } 18 | } 19 | 20 | extension ObjcIvarImpl: ObjcIvar { 21 | var name: PlainStringInData { mach.getCString(fromVmOffset: raw.name) } 22 | 23 | var type: PlainStringInData { mach.getCString(fromVmOffset: raw.type) } 24 | } 25 | 26 | extension ObjcIvarImpl: CustomStringConvertible { 27 | var description: String { "\(name): \(type)" } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/Implements/ObjcMethodImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObjcMethodImpl: FromMach { 9 | typealias Raw = ObjC.method_t 10 | 11 | let mach: Mach 12 | let offset: Int 13 | 14 | init(mach: Mach, offset: Int) { 15 | self.mach = mach 16 | self.offset = offset 17 | } 18 | } 19 | 20 | extension ObjcMethodImpl: ObjcMethod { 21 | var name: PlainStringInData { mach.getCString(fromVmOffset: raw.name) } 22 | var methType: PlainStringInData { mach.getCString(fromVmOffset: raw.types) } 23 | } 24 | 25 | extension ObjcMethodImpl: CustomStringConvertible { 26 | var description: String { "\(name): \(methType)" } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/Implements/ObjcPropertyImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObjcPropertyImpl: FromMach { 9 | typealias Raw = ObjC.property_t 10 | 11 | let mach: Mach 12 | let offset: Int 13 | 14 | init(mach: Mach, offset: Int) { 15 | self.mach = mach 16 | self.offset = offset 17 | } 18 | } 19 | 20 | extension ObjcPropertyImpl: ObjcProperty { 21 | var name: PlainStringInData { mach.getCString(fromVmOffset: raw.name) } 22 | } 23 | 24 | extension ObjcPropertyImpl: CustomStringConvertible { 25 | var attributes: PlainStringInData { mach.getCString(fromVmOffset: raw.attributes) } 26 | 27 | var description: String { "\(name): \(attributes)" } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/Implements/ObjcProtocolImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObjcProtocolImpl: FromMach { 9 | typealias Raw = ObjC.protocol_t 10 | let mach: Mach 11 | let offset: Int 12 | 13 | init(mach: Mach, offset: Int) { 14 | self.mach = mach 15 | self.offset = offset 16 | } 17 | } 18 | 19 | extension ObjcProtocolImpl: ObjcProtocol { 20 | var name: MangledObjcClassNameInData { mach.getCString(fromVmOffset: raw.name) } 21 | 22 | var methods: [ObjcMethod] { 23 | // methods_list is entsize_list_tt with flags=0x3 24 | let list: [ObjcMethodImpl] = mach.get_entsize_list_tt(fromVmOffset: raw.instanceMethods, flags: 0x03) 25 | return list 26 | } 27 | 28 | var properties: [ObjcProperty] { 29 | // property_list is entsize_list_tt with flags=0 30 | let list: [ObjcPropertyImpl] = mach.get_entsize_list_tt(fromVmOffset: raw.instanceProperties) 31 | return list 32 | } 33 | } 34 | 35 | extension ObjcProtocolImpl: CustomStringConvertible { 36 | var description: String { "Protocol \(name)\n methods: \(methods)\n properties: \(properties)" } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcArchitecture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | protocol ObjcArchitecture { 8 | // class_ro_t has padding that is present only in 32-bit version so we have to choose appropriate struct. 9 | // For other objc metadata generic 32/64-bit structs are enough. 10 | associatedtype ClassData: ImageObjClass 11 | 12 | typealias PointerType = ClassData.PointerType 13 | } 14 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcArchitecture32.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | protocol ObjcArchitecture32: ObjcArchitecture where ClassData == ObjC.class_32 {} 8 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcArchitecture64.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | protocol ObjcArchitecture64: ObjcArchitecture where ClassData == ObjC.class_64 {} 8 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcCategory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ObjcCategory { 8 | var name: MangledObjcClassNameInData { get } 9 | /// Class to which this category is related 10 | var cls: ObjcClass? { get } 11 | var methods: [ObjcMethod] { get } 12 | var properties: [ObjcProperty] { get } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ObjcClass { 8 | var ivarLayout: PlainStringInData? { get } 9 | var name: MangledObjcClassNameInData { get } 10 | var ivars: [ObjcIvar] { get } 11 | var methods: [ObjcMethod] { get } 12 | var properties: [ObjcProperty] { get } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | /// Architecture-erased protocol for 32/64 ObjC images 9 | protocol ObjcImage { 10 | func getObjectsFromList(section list: Section?, creator: (Int) -> Element) -> [Element] 11 | func create(offset: Int) -> ObjcClass 12 | func create(offset: Int) -> ObjcCategory 13 | func create(offset: Int) -> ObjcProtocol 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcIvar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ObjcIvar { 8 | var name: PlainStringInData { get } 9 | var type: PlainStringInData { get } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ObjcMethod { 8 | var name: PlainStringInData { get } 9 | var methType: PlainStringInData { get } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcProperty.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ObjcProperty { 8 | var name: PlainStringInData { get } 9 | var attributes: PlainStringInData { get } 10 | } 11 | 12 | public extension ObjcProperty { 13 | var attributeValues: [String] { attributes.value.split(separator: ",").map { String($0) } } 14 | 15 | /// Property type string. 16 | /// Format is described in https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5 17 | /// but it is outdated. 18 | /// However it seems that property types are encoded in the same way as in methtypes, ie. they are surrounded by quotation marks, brackets, etc. 19 | var typeAttribute: String { 20 | guard let typeAttr = attributeValues.first, typeAttr.starts(with: "T") else { 21 | fatalError("Type attribute missing or in unexpected format for property \(name.value)") 22 | } 23 | return typeAttr 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/Objc/Symbols/ObjcProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol ObjcProtocol { 8 | var name: MangledObjcClassNameInData { get } 9 | var methods: [ObjcMethod] { get } 10 | var properties: [ObjcProperty] { get } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/ValueInData/ContainedInData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | /// Value that is loaded from Mach-O image and is stored at a specific location in it 8 | public protocol ContainedInData { 9 | associatedtype ValueType 10 | init(value: ValueType, range: Range) 11 | var value: ValueType { get } 12 | var range: Range { get } 13 | } 14 | 15 | public extension CustomStringConvertible where Self: ContainedInData { 16 | var description: String { "'\(value)'[\(range)]" } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/ValueInData/Data.ContainedInData.CString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachCore 7 | 8 | public extension Data { 9 | func getCString(atOffset offset: Int) -> T where T: ContainedInData, T.ValueType == String { 10 | let value = get(atOffset: offset) 11 | let range = offset.. 11 | 12 | public init(value: ValueType, range: Range) { 13 | precondition(value.utf8.count == range.count) 14 | 15 | self.value = value 16 | self.range = range 17 | } 18 | } 19 | 20 | public extension MangledObjcClassNameInData { 21 | var isSwift: Bool { value.starts(with: "_Tt") } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/ValueInData/PlainStringInData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | 7 | public struct PlainStringInData: ContainedInData { 8 | public typealias ValueType = String 9 | 10 | public let value: ValueType 11 | public let range: Range 12 | 13 | public init(value: ValueType, range: Range) { 14 | precondition(value.utf8.count == range.count) 15 | 16 | self.value = value 17 | self.range = range 18 | } 19 | } 20 | 21 | public extension PlainStringInData { 22 | static let empty = PlainStringInData(value: "", range: 0..<0) 23 | } 24 | -------------------------------------------------------------------------------- /Sources/MachOObjcParser/ValueInData/Section.ContainedInDataType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/25. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension Section { 9 | func contains(data: ContainedInDataType) -> Bool { 10 | range.intRange.overlaps(data.range) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/MachOParser/Define/BitWide.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2022/10/5. 3 | // 4 | 5 | public enum BitWide { 6 | case _32 7 | case _64 8 | } 9 | -------------------------------------------------------------------------------- /Sources/MachOParser/Define/ByteOrder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/24. 3 | // 4 | 5 | import Foundation 6 | 7 | public enum ByteOrder { 8 | case little 9 | case big 10 | } 11 | 12 | public extension ByteOrder { 13 | static let current: Self = { 14 | switch 0x123456.bigEndian == 0x123456 { 15 | case true: 16 | return big 17 | case false: 18 | return little 19 | } 20 | }() 21 | } 22 | -------------------------------------------------------------------------------- /Sources/MachOParser/Define/Option.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/29. 3 | // 4 | 5 | public protocol Option: Hashable, CaseIterable { 6 | var value: Int64 { get } 7 | } 8 | 9 | extension Set: RawRepresentable where Element: Option { 10 | public typealias RawValue = Int64 11 | public var rawValue: RawValue { 12 | var rawValue: Int64 = 0 13 | for element in Element.allCases { 14 | if contains(element) { 15 | rawValue |= element.value 16 | } 17 | } 18 | 19 | return rawValue 20 | } 21 | 22 | public init(rawValue: RawValue) { 23 | var result = [Element]() 24 | for element in Element.allCases { 25 | if rawValue & element.value == element.value { 26 | result.append(element) 27 | } 28 | } 29 | self = Set(result) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOParser/Extensions/MachO.LoadCommand.Name.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public extension segment_command_64 { var name: String { String(tuple16: segname) } } 9 | 10 | public extension segment_command { var name: String { String(tuple16: segname) } } 11 | 12 | public extension section_64 { var name: String { String(tuple16: sectname) } } 13 | 14 | public extension section { var name: String { String(tuple16: sectname) } } 15 | 16 | public extension String { 17 | typealias Tuple16 = (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) 18 | 19 | init(tuple16: Tuple16) { 20 | let count = 16 21 | var table = [Int8](repeating: 0, count: count + 1) 22 | withUnsafePointer(to: tuple16) { ptr in 23 | ptr.withMemoryRebound(to: Int8.self, capacity: count) { ptr in 24 | for i in 0.. 9 | let bind: Range 10 | let weakBind: Range 11 | let lazyBind: Range 12 | let exportRange: Range 13 | } 14 | 15 | public extension DyldInfo { 16 | init(dyldInfoOnlyLC: DyldInfoOnlyLC) { 17 | rebase = Range(offset: UInt64(dyldInfoOnlyLC.rebaseOffset), count: UInt64(dyldInfoOnlyLC.rebaseSize)) 18 | bind = Range(offset: UInt64(dyldInfoOnlyLC.bindOffset), count: UInt64(dyldInfoOnlyLC.bindSize)) 19 | lazyBind = Range(offset: UInt64(dyldInfoOnlyLC.weakBindOffset), count: UInt64(dyldInfoOnlyLC.weakBindSize)) 20 | weakBind = Range(offset: UInt64(dyldInfoOnlyLC.lazyBindOffset), count: UInt64(dyldInfoOnlyLC.lazyBindSize)) 21 | exportRange = Range(offset: UInt64(dyldInfoOnlyLC.exportOffset), count: UInt64(dyldInfoOnlyLC.exportSize)) 22 | } 23 | 24 | init(dyldInfoLC: DyldInfoLC) { 25 | rebase = Range(offset: UInt64(dyldInfoLC.rebaseOffset), count: UInt64(dyldInfoLC.rebaseSize)) 26 | bind = Range(offset: UInt64(dyldInfoLC.bindOffset), count: UInt64(dyldInfoLC.bindSize)) 27 | lazyBind = Range(offset: UInt64(dyldInfoLC.weakBindOffset), count: UInt64(dyldInfoLC.weakBindSize)) 28 | weakBind = Range(offset: UInt64(dyldInfoLC.lazyBindOffset), count: UInt64(dyldInfoLC.lazyBindSize)) 29 | exportRange = Range(offset: UInt64(dyldInfoLC.exportOffset), count: UInt64(dyldInfoLC.exportSize)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Fat/Fat.Architecture._32_.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2022/10/7. 3 | // 4 | 5 | import Foundation 6 | @_implementationOnly import MachCore 7 | import MachO 8 | 9 | public extension Fat.Architecture { 10 | struct _32_ { 11 | public let cpuType: CPUType 12 | public let cpuSubType: CPUSubType 13 | public let offset: UInt32 14 | public let size: UInt32 15 | public let align: UInt32 16 | public let mach: Mach 17 | 18 | init(data: Data, arch: fat_arch, byteOrder: ByteOrder) throws { 19 | switch byteOrder { 20 | case .little: 21 | cpuType = CPUType(cpuType: arch.cputype.bigEndian) 22 | cpuSubType = CPUSubType(cpu_subtype: arch.cpusubtype.bigEndian) 23 | offset = arch.offset.bigEndian 24 | size = arch.size.bigEndian 25 | align = arch.align.bigEndian 26 | case .big: 27 | cpuType = CPUType(cpuType: arch.cputype) 28 | cpuSubType = CPUSubType(cpu_subtype: arch.cpusubtype) 29 | offset = arch.offset 30 | size = arch.size 31 | align = arch.align 32 | } 33 | 34 | let range: Range = Int(offset).. = Int(offset)...size 28 | } 29 | 30 | static var rawStride: Int { 31 | MemoryLayout.stride 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Fat/Fat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/23. 3 | // 4 | 5 | import Foundation 6 | @_implementationOnly import MachCore 7 | import MachO 8 | 9 | public struct Fat { 10 | public let data: Data 11 | public let header: Header 12 | public let architectures: [Architecture] 13 | private let byteOrder: ByteOrder 14 | private let bitWide: BitWide 15 | 16 | init(data: Data) throws { 17 | let magic = data.magic 18 | switch magic { 19 | case FAT_MAGIC: 20 | byteOrder = .big 21 | bitWide = ._32 22 | case FAT_CIGAM: 23 | byteOrder = .little 24 | bitWide = ._32 25 | case FAT_MAGIC_64: 26 | byteOrder = .big 27 | bitWide = ._64 28 | case FAT_CIGAM_64: 29 | byteOrder = .little 30 | bitWide = ._64 31 | default: 32 | throw Error.magic(magic) 33 | } 34 | self.data = data 35 | 36 | let fat_header: fat_header = data.get(atOffset: 0) 37 | header = Header(fat_header, byteOrder: byteOrder) 38 | 39 | switch bitWide { 40 | case ._32: 41 | let fatArchitectures: [fat_arch] = data.get(atOffset: Header.rawSize, count: Int(header.architectureCount)) 42 | architectures = try fatArchitectures.map { [byteOrder] arch in 43 | try Architecture._32(Architecture._32_(data: data, arch: arch, byteOrder: byteOrder)) 44 | } 45 | case ._64: 46 | let fatArchitectures: [fat_arch_64] = data.get(atOffset: Header.rawSize, count: Int(header.architectureCount)) 47 | architectures = try fatArchitectures.map { [byteOrder] arch in 48 | try Architecture._64(Architecture._64_(data: data, arch: arch, byteOrder: byteOrder)) 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Headers/SectionHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SectionHeader { 9 | public let sectionName: String /* for 32-bit architectures */ /* name of this section */ 10 | public let segmentName: String /* segment this section goes in */ 11 | public let address: UInt32 /* memory address of this section */ 12 | public let size: UInt32 /* size in bytes of this section */ 13 | public let offset: UInt32 /* file offset of this section */ 14 | public let align: UInt32 /* section alignment (power of 2) */ 15 | public let relocationOffset: UInt32 /* file offset of relocation entries */ 16 | public let relocationCount: UInt32 /* number of relocation entries */ 17 | public let flags: UInt32 /* flags (section type and attributes)*/ 18 | public let reserved1: UInt32 /* reserved (for offset or index) */ 19 | public let reserved2: UInt32 /* reserved (for count or sizeof) */ 20 | 21 | init(section: section) { 22 | sectionName = String(tuple16: section.sectname) 23 | segmentName = String(tuple16: section.segname) 24 | address = section.addr 25 | size = section.size 26 | offset = section.offset 27 | align = section.align 28 | relocationOffset = section.reloff 29 | relocationCount = section.nreloc 30 | flags = section.flags 31 | reserved1 = section.reserved1 32 | reserved2 = section.reserved2 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Headers/SectionHeader64.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SectionHeader64 { 9 | public let sectionName: String /* for 64-bit architectures */ /* name of this section */ 10 | public let segmentName: String /* segment this section goes in */ 11 | public let address: UInt64 /* memory address of this section */ 12 | public let size: UInt64 /* size in bytes of this section */ 13 | public let offset: UInt32 /* file offset of this section */ 14 | public let align: UInt32 /* section alignment (power of 2) */ 15 | public let relocationOffset: UInt32 /* file offset of relocation entries */ 16 | public let relocationCount: UInt32 /* number of relocation entries */ 17 | public let flags: UInt32 /* flags (section type and attributes)*/ 18 | public let reserved1: UInt32 /* reserved (for offset or index) */ 19 | public let reserved2: UInt32 /* reserved (for count or sizeof) */ 20 | public let reserved3: UInt32 /* reserved */ 21 | 22 | init(section: section_64) { 23 | sectionName = String(tuple16: section.sectname) 24 | segmentName = String(tuple16: section.segname) 25 | address = section.addr 26 | size = section.size 27 | offset = section.offset 28 | align = section.align 29 | relocationOffset = section.reloff 30 | relocationCount = section.nreloc 31 | flags = section.flags 32 | reserved1 = section.reserved1 33 | reserved2 = section.reserved2 34 | reserved3 = section.reserved3 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Image/Image.Content.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | public extension Image { 6 | enum Content { 7 | case fat(Fat) 8 | case mach(Mach) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Image/Image.Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import struct Foundation.URL 6 | 7 | public extension Image { 8 | enum Error: Swift.Error { 9 | case unsupported(url: URL, magic: UInt32?) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Image/Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/23. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | // MARK: - Define 9 | 10 | public struct Image { 11 | public let url: URL 12 | public let content: Content 13 | 14 | public init(url: URL) throws { 15 | self.url = url 16 | let data = try Data(contentsOf: url, options: .mappedIfSafe) 17 | let magic = data.magic 18 | switch magic { 19 | case FAT_MAGIC, FAT_CIGAM, FAT_MAGIC_64, FAT_CIGAM_64: 20 | content = try .fat(Fat(data: data)) 21 | case MH_MAGIC, MH_CIGAM, MH_MAGIC_64, MH_CIGAM_64: 22 | content = try .mach(Mach(data: data)) 23 | default: 24 | throw Error.unsupported(url: url, magic: magic) 25 | } 26 | } 27 | } 28 | 29 | // MARK: - Initialize 30 | 31 | public extension Image { 32 | static func load(url: URL) throws -> Image { 33 | try Image(url: url) 34 | } 35 | } 36 | 37 | // MARK: - Public 38 | 39 | public extension Image { 40 | func eachMach(block: (Mach) -> ()) { 41 | switch content { 42 | case let .fat(fat): 43 | fat.architectures.forEach { block($0.mach) } 44 | case let .mach(mach): 45 | block(mach) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/ImportStack/Import.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | 7 | public typealias ImportStack = [Import] 8 | 9 | public struct Import { 10 | public private(set) var dylibOrdinal: Int 11 | public let symbol: [UInt8] 12 | public let symbolRange: Range 13 | public let weak: Bool // should be skipped when dylib is missing 14 | } 15 | 16 | public extension Import { 17 | var symbolString: String { 18 | guard let symbolName = String(bytes: symbol, encoding: .utf8) else { fatalError() } 19 | return symbolName 20 | } 21 | } 22 | 23 | extension Import { 24 | mutating func update(dylibOrdinal: Int) { self.dylibOrdinal = dylibOrdinal } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/CodeSignatureLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct CodeSignatureLC: LoadCommand { 9 | public static let id = UInt32(LC_CODE_SIGNATURE) 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension CodeSignatureLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DataInCodeLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DataInCodeLC: LoadCommand { 9 | public static let id = UInt32(LC_DATA_IN_CODE) 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension DataInCodeLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DyldChainedFixupsLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DyldChainedFixupsLC: LoadCommand { 9 | public static let id: UInt32 = LC_DYLD_CHAINED_FIXUPS 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension DyldChainedFixupsLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DyldEnvironmentLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | @_implementationOnly import MachCore 7 | import MachO 8 | 9 | public struct DyldEnvironmentLC: LoadCommand { 10 | public static let id = UInt32(LC_DYLD_ENVIRONMENT) 11 | 12 | public let name: String 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: dylinker_command = machData.get(atOffset: offset) 16 | name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.name) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: dylinker_command = pointer.get() 21 | name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.name) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DyldExportsTrieLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DyldExportsTrieLC: LoadCommand { 9 | public static let id: UInt32 = LC_DYLD_EXPORTS_TRIE 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension DyldExportsTrieLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DyldInfoLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DyldInfoLC: LoadCommand { 9 | public static let id = UInt32(LC_DYLD_INFO) 10 | 11 | public let rebaseOffset: UInt32 12 | public let rebaseSize: UInt32 13 | public let bindOffset: UInt32 14 | public let bindSize: UInt32 15 | public let weakBindOffset: UInt32 16 | public let weakBindSize: UInt32 17 | public let lazyBindOffset: UInt32 18 | public let lazyBindSize: UInt32 19 | public let exportOffset: UInt32 20 | public let exportSize: UInt32 21 | 22 | public init(machData: Data, offset: Int) { 23 | let command: dyld_info_command = machData.get(atOffset: offset) 24 | self.init(command: command) 25 | } 26 | 27 | public init(pointer: UnsafeRawPointer) { 28 | let command: dyld_info_command = pointer.get() 29 | self.init(command: command) 30 | } 31 | } 32 | 33 | private extension DyldInfoLC { 34 | init(command: dyld_info_command) { 35 | rebaseOffset = command.rebase_off 36 | rebaseSize = command.rebase_size 37 | bindOffset = command.bind_off 38 | bindSize = command.bind_size 39 | weakBindOffset = command.weak_bind_off 40 | weakBindSize = command.weak_bind_size 41 | lazyBindOffset = command.lazy_bind_off 42 | lazyBindSize = command.lazy_bind_size 43 | exportOffset = command.export_off 44 | exportSize = command.export_size 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DyldInfoOnlyLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DyldInfoOnlyLC: LoadCommand { 9 | public static let id: UInt32 = LC_DYLD_INFO_ONLY 10 | 11 | public let rebaseOffset: UInt32 12 | public let rebaseSize: UInt32 13 | public let bindOffset: UInt32 14 | public let bindSize: UInt32 15 | public let weakBindOffset: UInt32 16 | public let weakBindSize: UInt32 17 | public let lazyBindOffset: UInt32 18 | public let lazyBindSize: UInt32 19 | public let exportOffset: UInt32 20 | public let exportSize: UInt32 21 | 22 | public init(machData: Data, offset: Int) { 23 | let command: dyld_info_command = machData.get(atOffset: offset) 24 | self.init(command: command) 25 | } 26 | 27 | public init(pointer: UnsafeRawPointer) { 28 | let command: dyld_info_command = pointer.get() 29 | self.init(command: command) 30 | } 31 | } 32 | 33 | private extension DyldInfoOnlyLC { 34 | init(command: dyld_info_command) { 35 | rebaseOffset = command.rebase_off 36 | rebaseSize = command.rebase_size 37 | bindOffset = command.bind_off 38 | bindSize = command.bind_size 39 | weakBindOffset = command.weak_bind_off 40 | weakBindSize = command.weak_bind_size 41 | lazyBindOffset = command.lazy_bind_off 42 | lazyBindSize = command.lazy_bind_size 43 | exportOffset = command.export_off 44 | exportSize = command.export_size 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DylibCodeSignDrsLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DylibCodeSignDrsLC: LoadCommand { 9 | public static let id = UInt32(LC_DYLIB_CODE_SIGN_DRS) 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension DylibCodeSignDrsLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/DynamicSymbolTableLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct DynamicSymbolTableLC: LoadCommand { 9 | public static let id = UInt32(LC_DYSYMTAB) 10 | 11 | public let localSymbolIndex: UInt32 12 | public let localSymbolCount: UInt32 13 | public let definedExternalSymbolIndex: UInt32 14 | public let definedExternalSymbolCount: UInt32 15 | public let undefinedExternalSymbolIndex: UInt32 16 | public let undefinedExternalSymbolCount: UInt32 17 | public let tableOfContentOffset: UInt32 18 | public let tableOfContentCount: UInt32 19 | public let moduleOfTableOffset: UInt32 20 | public let moduleOfTableCount: UInt32 21 | public let externalReferencedSymbolOffset: UInt32 22 | public let externalReferencedSymbolCount: UInt32 23 | public let indirectSymbolOffset: UInt32 24 | public let indirectSymbolCount: UInt32 25 | public let externalRelocationOffset: UInt32 26 | public let externalRelocationCount: UInt32 27 | public let localRelocationOffset: UInt32 28 | public let localRelocationCount: UInt32 29 | 30 | public init(machData: Data, offset: Int) { 31 | let command: dysymtab_command = machData.get(atOffset: offset) 32 | self.init(command: command) 33 | } 34 | 35 | public init(pointer: UnsafeRawPointer) { 36 | let command: dysymtab_command = pointer.get() 37 | self.init(command: command) 38 | } 39 | } 40 | 41 | private extension DynamicSymbolTableLC { 42 | init(command: dysymtab_command) { 43 | localSymbolIndex = command.ilocalsym 44 | localSymbolCount = command.nlocalsym 45 | definedExternalSymbolIndex = command.iextdefsym 46 | definedExternalSymbolCount = command.nextdefsym 47 | undefinedExternalSymbolIndex = command.iundefsym 48 | undefinedExternalSymbolCount = command.nundefsym 49 | tableOfContentOffset = command.tocoff 50 | tableOfContentCount = command.ntoc 51 | moduleOfTableOffset = command.modtaboff 52 | moduleOfTableCount = command.nmodtab 53 | externalReferencedSymbolOffset = command.extrefsymoff 54 | externalReferencedSymbolCount = command.nextrefsyms 55 | indirectSymbolOffset = command.indirectsymoff 56 | indirectSymbolCount = command.nindirectsyms 57 | externalRelocationOffset = command.extreloff 58 | externalRelocationCount = command.nextrel 59 | localRelocationOffset = command.locreloff 60 | localRelocationCount = command.nlocrel 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/EncryptionInfo64LC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct EncryptionInfo64LC: LoadCommand { 9 | public static let id = UInt32(LC_ENCRYPTION_INFO_64) 10 | 11 | public let cryptOffset: UInt32 12 | public let cryptSize: UInt32 13 | public let cryptID: UInt32 14 | public let pad: UInt32 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: encryption_info_command_64 = machData.get(atOffset: offset) 18 | self.init(command: command) 19 | } 20 | 21 | public init(pointer: UnsafeRawPointer) { 22 | let command: encryption_info_command_64 = pointer.get() 23 | self.init(command: command) 24 | } 25 | } 26 | 27 | private extension EncryptionInfo64LC { 28 | init(command: encryption_info_command_64) { 29 | cryptOffset = command.cryptoff 30 | cryptSize = command.cryptsize 31 | cryptID = command.cryptid 32 | pad = command.pad 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/EncryptionInfoLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct EncryptionInfoLC: LoadCommand { 9 | public static let id = UInt32(LC_ENCRYPTION_INFO) 10 | 11 | public let cryptOffset: UInt32 12 | public let cryptSize: UInt32 13 | public let cryptID: UInt32 14 | 15 | public init(machData: Data, offset: Int) { 16 | let command: encryption_info_command = machData.get(atOffset: offset) 17 | self.init(command: command) 18 | } 19 | 20 | public init(pointer: UnsafeRawPointer) { 21 | let command: encryption_info_command = pointer.get() 22 | self.init(command: command) 23 | } 24 | } 25 | 26 | private extension EncryptionInfoLC { 27 | init(command: encryption_info_command) { 28 | cryptOffset = command.cryptoff 29 | cryptSize = command.cryptsize 30 | cryptID = command.cryptid 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/FixedVMFileLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct FixedVMFileLC: LoadCommand { 9 | public static let id = UInt32(LC_FVMFILE) 10 | 11 | public let name: String 12 | 13 | public let headerAddress: UInt32 /* files virtual address */ 14 | 15 | public init(machData: Data, offset: Int) { 16 | let fvmfile: fvmfile_command = machData.get(atOffset: offset) 17 | headerAddress = fvmfile.header_addr 18 | name = String(data: machData, offset: offset, commandSize: Int(fvmfile.cmdsize), loadCommandString: fvmfile.name) 19 | } 20 | 21 | public init(pointer: UnsafeRawPointer) { 22 | let fvmfile: fvmfile_command = pointer.get() 23 | headerAddress = fvmfile.header_addr 24 | name = String(loadCommandPointer: pointer, commandSize: Int(fvmfile.cmdsize), loadCommandString: fvmfile.name) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/FunctionStartsLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct FunctionStartsLC: LoadCommand { 9 | public static let id = UInt32(LC_FUNCTION_STARTS) 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension FunctionStartsLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/IDDylibLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct IDDylibLC: LoadCommand { 9 | public static let id = UInt32(LC_ID_DYLIB) 10 | 11 | public let name: String 12 | public let date: Date 13 | public let currentVersion: Version 14 | public let compatibilityVersion: Version 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: dylib_command = machData.get(atOffset: offset) 18 | let dylib = command.dylib 19 | let name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 20 | self.init(dylib: dylib, name: name) 21 | } 22 | 23 | public init(pointer: UnsafeRawPointer) { 24 | let command: dylib_command = pointer.get() 25 | let dylib = command.dylib 26 | let name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 27 | self.init(dylib: dylib, name: name) 28 | } 29 | } 30 | 31 | private extension IDDylibLC { 32 | init(dylib: dylib, name: String) { 33 | self.name = name 34 | date = Date(timeIntervalSince1970: TimeInterval(dylib.timestamp)) 35 | currentVersion = Version(machVersion: dylib.current_version) 36 | compatibilityVersion = Version(machVersion: dylib.compatibility_version) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/IDDylinkerLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct IDDylinkerLC: LoadCommand { 9 | public static let id = UInt32(LC_ID_DYLINKER) 10 | 11 | public let name: String 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: dylinker_command = machData.get(atOffset: offset) 15 | name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.name) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: dylinker_command = pointer.get() 20 | name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.name) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LazyLoadDylibLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LazyLoadDylibLC: LoadCommand { 9 | public static let id = UInt32(LC_LAZY_LOAD_DYLIB) 10 | 11 | public let name: String 12 | public let date: Date 13 | public let currentVersion: Version 14 | public let compatibilityVersion: Version 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: dylib_command = machData.get(atOffset: offset) 18 | let dylib = command.dylib 19 | let name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 20 | self.init(dylib: dylib, name: name) 21 | } 22 | 23 | public init(pointer: UnsafeRawPointer) { 24 | let command: dylib_command = pointer.get() 25 | let dylib = command.dylib 26 | let name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 27 | self.init(dylib: dylib, name: name) 28 | } 29 | } 30 | 31 | private extension LazyLoadDylibLC { 32 | init(dylib: dylib, name: String) { 33 | self.name = name 34 | date = Date(timeIntervalSince1970: TimeInterval(dylib.timestamp)) 35 | currentVersion = Version(machVersion: dylib.current_version) 36 | compatibilityVersion = Version(machVersion: dylib.compatibility_version) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LinkerOptimizationHintLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LinkerOptimizationHintLC: LoadCommand { 9 | public static let id = UInt32(LC_LINKER_OPTIMIZATION_HINT) 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension LinkerOptimizationHintLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LinkerOptionLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LinkerOptionLC: LoadCommand { 9 | public static let id = UInt32(LC_LINKER_OPTION) 10 | 11 | public let count: UInt32 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: linker_option_command = machData.get(atOffset: offset) 15 | self.init(command: command) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: linker_option_command = pointer.get() 20 | self.init(command: command) 21 | } 22 | } 23 | 24 | private extension LinkerOptionLC { 25 | init(command: linker_option_command) { 26 | count = command.count 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LoadCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol LoadCommand { 8 | static var id: UInt32 { get } 9 | init(machData: Data, offset: Int) 10 | init(pointer: UnsafeRawPointer) 11 | } 12 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LoadDylibLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LoadDylibLC: LoadCommand { 9 | public static let id = UInt32(LC_LOAD_DYLIB) 10 | 11 | public let name: String 12 | public let date: Date 13 | public let currentVersion: Version 14 | public let compatibilityVersion: Version 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: dylib_command = machData.get(atOffset: offset) 18 | let dylib = command.dylib 19 | let name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 20 | self.init(dylib: dylib, name: name) 21 | } 22 | 23 | public init(pointer: UnsafeRawPointer) { 24 | let command: dylib_command = pointer.get() 25 | let dylib = command.dylib 26 | let name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 27 | self.init(dylib: dylib, name: name) 28 | } 29 | } 30 | 31 | private extension LoadDylibLC { 32 | init(dylib: dylib, name: String) { 33 | self.name = name 34 | date = Date(timeIntervalSince1970: TimeInterval(dylib.timestamp)) 35 | currentVersion = Version(machVersion: dylib.current_version) 36 | compatibilityVersion = Version(machVersion: dylib.compatibility_version) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LoadDylinkerLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LoadDylinkerLC: LoadCommand { 9 | public static let id = UInt32(LC_LOAD_DYLINKER) 10 | 11 | public let name: String 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: dylinker_command = machData.get(atOffset: offset) 15 | name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.name) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: dylinker_command = pointer.get() 20 | name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.name) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LoadUpwardDylibLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LoadUpwardDylibLC: LoadCommand { 9 | public static let id: UInt32 = LC_LOAD_UPWARD_DYLIB 10 | 11 | public let name: String 12 | public let date: Date 13 | public let currentVersion: Version 14 | public let compatibilityVersion: Version 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: dylib_command = machData.get(atOffset: offset) 18 | let dylib = command.dylib 19 | let name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 20 | self.init(dylib: dylib, name: name) 21 | } 22 | 23 | public init(pointer: UnsafeRawPointer) { 24 | let command: dylib_command = pointer.get() 25 | let dylib = command.dylib 26 | let name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 27 | self.init(dylib: dylib, name: name) 28 | } 29 | } 30 | 31 | private extension LoadUpwardDylibLC { 32 | init(dylib: dylib, name: String) { 33 | self.name = name 34 | date = Date(timeIntervalSince1970: TimeInterval(dylib.timestamp)) 35 | currentVersion = Version(machVersion: dylib.current_version) 36 | compatibilityVersion = Version(machVersion: dylib.compatibility_version) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/LoadWeakDylibLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct LoadWeakDylibLC: LoadCommand { 9 | public static let id = UInt32(LC_LOAD_WEAK_DYLIB) 10 | 11 | public let name: String 12 | public let date: Date 13 | public let currentVersion: Version 14 | public let compatibilityVersion: Version 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: dylib_command = machData.get(atOffset: offset) 18 | let dylib = command.dylib 19 | let name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 20 | self.init(dylib: dylib, name: name) 21 | } 22 | 23 | public init(pointer: UnsafeRawPointer) { 24 | let command: dylib_command = pointer.get() 25 | let dylib = command.dylib 26 | let name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 27 | self.init(dylib: dylib, name: name) 28 | } 29 | } 30 | 31 | private extension LoadWeakDylibLC { 32 | init(dylib: dylib, name: String) { 33 | self.name = name 34 | date = Date(timeIntervalSince1970: TimeInterval(dylib.timestamp)) 35 | currentVersion = Version(machVersion: dylib.current_version) 36 | compatibilityVersion = Version(machVersion: dylib.compatibility_version) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/MainLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct MainLC: LoadCommand { 9 | public static let id: UInt32 = LC_MAIN 10 | 11 | public let entryOffset: UInt64 /* file (__TEXT) offset of main() */ 12 | public let stackSize: UInt64 /* if not zero, initial stack size */ 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: entry_point_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: entry_point_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension MainLC { 26 | init(command: entry_point_command) { 27 | entryOffset = command.entryoff 28 | stackSize = command.stacksize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/NoteLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct NoteLC: LoadCommand { 9 | public static let id = UInt32(LC_NOTE) 10 | public let owner: String 11 | public let fileOffset: UInt64 12 | public let fileSize: UInt64 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: note_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: note_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension NoteLC { 26 | init(command: note_command) { 27 | owner = String(tuple16: command.data_owner) 28 | fileOffset = command.offset 29 | fileSize = command.size 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/PrebindChecksumLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct PrebindChecksumLC: LoadCommand { 9 | public static let id = UInt32(LC_PREBIND_CKSUM) 10 | public let checkSum: UInt32 11 | 12 | public init(machData: Data, offset: Int) { 13 | let command: prebind_cksum_command = machData.get(atOffset: offset) 14 | self.init(command: command) 15 | } 16 | 17 | public init(pointer: UnsafeRawPointer) { 18 | let command: prebind_cksum_command = pointer.get() 19 | self.init(command: command) 20 | } 21 | } 22 | 23 | private extension PrebindChecksumLC { 24 | init(command: prebind_cksum_command) { 25 | checkSum = command.cksum 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/ReexportedDylibLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct ReexportedDylibLC: LoadCommand { 9 | public static let id = UInt32(LC_REEXPORT_DYLIB) 10 | 11 | public let name: String 12 | public let date: Date 13 | public let currentVersion: Version 14 | public let compatibilityVersion: Version 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: dylib_command = machData.get(atOffset: offset) 18 | let dylib = command.dylib 19 | let name = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 20 | self.init(dylib: dylib, name: name) 21 | } 22 | 23 | public init(pointer: UnsafeRawPointer) { 24 | let command: dylib_command = pointer.get() 25 | let dylib = command.dylib 26 | let name = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: dylib.name) 27 | self.init(dylib: dylib, name: name) 28 | } 29 | } 30 | 31 | private extension ReexportedDylibLC { 32 | init(dylib: dylib, name: String) { 33 | self.name = name 34 | date = Date(timeIntervalSince1970: TimeInterval(dylib.timestamp)) 35 | currentVersion = Version(machVersion: dylib.current_version) 36 | compatibilityVersion = Version(machVersion: dylib.compatibility_version) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/Routines64LC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct Routines64LC: LoadCommand { 9 | public static let id = UInt32(LC_ROUTINES_64) 10 | 11 | public let initAddress: UInt64 12 | public let initModule: UInt64 13 | public let reserved1: UInt64 14 | public let reserved2: UInt64 15 | public let reserved3: UInt64 16 | public let reserved4: UInt64 17 | public let reserved5: UInt64 18 | public let reserved6: UInt64 19 | 20 | public init(machData: Data, offset: Int) { 21 | let command: routines_command_64 = machData.get(atOffset: offset) 22 | self.init(command: command) 23 | } 24 | 25 | public init(pointer: UnsafeRawPointer) { 26 | let command: routines_command_64 = pointer.get() 27 | self.init(command: command) 28 | } 29 | } 30 | 31 | private extension Routines64LC { 32 | init(command: routines_command_64) { 33 | initAddress = command.init_address 34 | initModule = command.init_module 35 | reserved1 = command.reserved1 36 | reserved2 = command.reserved2 37 | reserved3 = command.reserved3 38 | reserved4 = command.reserved4 39 | reserved5 = command.reserved5 40 | reserved6 = command.reserved6 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/RoutinesLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct RoutinesLC: LoadCommand { 9 | public static let id = UInt32(LC_ROUTINES) 10 | 11 | public let initAddress: UInt32 12 | public let initModule: UInt32 13 | public let reserved1: UInt32 14 | public let reserved2: UInt32 15 | public let reserved3: UInt32 16 | public let reserved4: UInt32 17 | public let reserved5: UInt32 18 | public let reserved6: UInt32 19 | 20 | public init(machData: Data, offset: Int) { 21 | let command: routines_command = machData.get(atOffset: offset) 22 | self.init(command: command) 23 | } 24 | 25 | public init(pointer: UnsafeRawPointer) { 26 | let command: routines_command = pointer.get() 27 | self.init(command: command) 28 | } 29 | } 30 | 31 | private extension RoutinesLC { 32 | init(command: routines_command) { 33 | initAddress = command.init_address 34 | initModule = command.init_module 35 | reserved1 = command.reserved1 36 | reserved2 = command.reserved2 37 | reserved3 = command.reserved3 38 | reserved4 = command.reserved4 39 | reserved5 = command.reserved5 40 | reserved6 = command.reserved6 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/RunPathLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct RunPathLC: LoadCommand { 9 | public static let id: UInt32 = LC_RPATH 10 | 11 | public let path: String 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: rpath_command = machData.get(atOffset: offset) 15 | path = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.path) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: rpath_command = pointer.get() 20 | path = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.path) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/Segment64LC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct Segment64LC: LoadCommand { 9 | public static let id = UInt32(LC_SEGMENT_64) 10 | 11 | public let segmentName: String /* segment name */ 12 | public let vmAddress: UInt64 /* memory address of this segment */ 13 | public let vmSize: UInt64 /* memory size of this segment */ 14 | public let fileOffset: UInt64 /* file offset of this segment */ 15 | public let fileSize: UInt64 /* amount to map from the file */ 16 | public let maxProtection: vm_prot_t /* maximum VM protection */ 17 | public let initProtection: vm_prot_t /* initial VM protection */ 18 | public let sectionCount: UInt32 /* number of sections in segment */ 19 | public let flags: UInt32 /* flags */ 20 | 21 | public let sectionHeaders: [SectionHeader64] 22 | 23 | public init(machData: Data, offset: Int) { 24 | let command: segment_command_64 = machData.get(atOffset: offset) 25 | let sections: [section_64] = machData.get(atOffset: offset + MemoryLayout.stride, count: Int(command.nsects)) 26 | self.init(command: command, sections: sections) 27 | } 28 | 29 | public init(pointer: UnsafeRawPointer) { 30 | let command: segment_command_64 = pointer.get() 31 | let sections_pointer = pointer.advanced(by: MemoryLayout.stride) 32 | let sections: [section_64] = sections_pointer.get(count: Int(command.nsects)) 33 | self.init(command: command, sections: sections) 34 | } 35 | } 36 | 37 | private extension Segment64LC { 38 | init(command: segment_command_64, sections: [section_64]) { 39 | segmentName = String(tuple16: command.segname) 40 | vmAddress = command.vmaddr 41 | vmSize = command.vmsize 42 | fileOffset = command.fileoff 43 | fileSize = command.filesize 44 | maxProtection = command.maxprot 45 | initProtection = command.initprot 46 | sectionCount = command.nsects 47 | flags = command.flags 48 | 49 | sectionHeaders = sections.map { section -> SectionHeader64 in SectionHeader64(section: section) } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SegmentLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SegmentLC: LoadCommand { 9 | public static let id = UInt32(LC_SEGMENT) 10 | 11 | public let segmentName: String 12 | public let vmAddress: UInt32 /* memory address of this segment */ 13 | public let vmSize: UInt32 /* memory size of this segment */ 14 | public let fileOffset: UInt32 /* file offset of this segment */ 15 | public let fileSize: UInt32 /* amount to map from the file */ 16 | public let maxProtection: vm_prot_t /* maximum VM protection */ 17 | public let initProtection: vm_prot_t /* initial VM protection */ 18 | public let sectionCount: UInt32 /* number of sections in segment */ 19 | public let flags: UInt32 /* flags */ 20 | 21 | public let sectionHeaders: [SectionHeader] 22 | 23 | public init(machData: Data, offset: Int) { 24 | let command: segment_command = machData.get(atOffset: offset) 25 | let sections: [section] = machData.get(atOffset: offset + MemoryLayout.stride, count: Int(command.nsects)) 26 | self.init(command: command, sections: sections) 27 | } 28 | 29 | public init(pointer: UnsafeRawPointer) { 30 | let command: segment_command = pointer.get() 31 | let sections_pointer = pointer.advanced(by: MemoryLayout.stride) 32 | let sections: [section] = sections_pointer.get(count: Int(command.nsects)) 33 | self.init(command: command, sections: sections) 34 | } 35 | } 36 | 37 | private extension SegmentLC { 38 | init(command: segment_command, sections: [section]) { 39 | segmentName = String(tuple16: command.segname) 40 | vmAddress = command.vmaddr 41 | vmSize = command.vmsize 42 | fileOffset = command.fileoff 43 | fileSize = command.filesize 44 | maxProtection = command.maxprot 45 | initProtection = command.initprot 46 | sectionCount = command.nsects 47 | flags = command.flags 48 | 49 | sectionHeaders = sections.map { section -> SectionHeader in SectionHeader(section: section) } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SegmentSplitInfoLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SegmentSplitInfoLC: LoadCommand { 9 | public static let id = UInt32(LC_SEGMENT_SPLIT_INFO) 10 | 11 | public let dataOffset: UInt32 12 | public let dataSize: UInt32 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: linkedit_data_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: linkedit_data_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension SegmentSplitInfoLC { 26 | init(command: linkedit_data_command) { 27 | dataOffset = command.dataoff 28 | dataSize = command.datasize 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SourceVersionLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SourceVersionLC: LoadCommand { 9 | public static let id = UInt32(LC_SOURCE_VERSION) 10 | 11 | let version: SourceVersion 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: source_version_command = machData.get(atOffset: offset) 15 | self.init(command: command) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: source_version_command = pointer.get() 20 | self.init(command: command) 21 | } 22 | } 23 | 24 | private extension SourceVersionLC { 25 | init(command: source_version_command) { 26 | version = SourceVersion(version: command.version) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SubClientLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SubClientLC: LoadCommand { 9 | public static let id = UInt32(LC_SUB_CLIENT) 10 | 11 | public let client: String 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: sub_client_command = machData.get(atOffset: offset) 15 | client = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.client) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: sub_client_command = pointer.get() 20 | client = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.client) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SubFrameworkLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SubFrameworkLC: LoadCommand { 9 | public static let id = UInt32(LC_SUB_FRAMEWORK) 10 | 11 | public let umbrella: String 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: sub_framework_command = machData.get(atOffset: offset) 15 | umbrella = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.umbrella) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: sub_framework_command = pointer.get() 20 | umbrella = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.umbrella) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SubLibraryLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SubLibraryLC: LoadCommand { 9 | public static let id = UInt32(LC_SUB_LIBRARY) 10 | public let subLibrary: String 11 | 12 | public init(machData: Data, offset: Int) { 13 | let command: sub_library_command = machData.get(atOffset: offset) 14 | subLibrary = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.sub_library) 15 | } 16 | 17 | public init(pointer: UnsafeRawPointer) { 18 | let command: sub_library_command = pointer.get() 19 | subLibrary = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.sub_library) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SubUmbrellaLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SubUmbrellaLC: LoadCommand { 9 | public static let id = UInt32(LC_SUB_UMBRELLA) 10 | 11 | public let subUmbrella: String 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: sub_umbrella_command = machData.get(atOffset: offset) 15 | subUmbrella = String(data: machData, offset: offset, commandSize: Int(command.cmdsize), loadCommandString: command.sub_umbrella) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: sub_umbrella_command = pointer.get() 20 | subUmbrella = String(loadCommandPointer: pointer, commandSize: Int(command.cmdsize), loadCommandString: command.sub_umbrella) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/SymbolTableLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SymbolTableLC: LoadCommand { 9 | public static let id = UInt32(LC_SYMTAB) 10 | 11 | public let symbolTableOffset: UInt32 12 | public let numberOfSymbols: UInt32 13 | public let stringTableOffset: UInt32 14 | public let stringTableSize: UInt32 15 | 16 | public init(machData: Data, offset: Int) { 17 | let command: symtab_command = machData.get(atOffset: offset) 18 | self.init(command: command) 19 | } 20 | 21 | public init(pointer: UnsafeRawPointer) { 22 | let command: symtab_command = pointer.get() 23 | self.init(command: command) 24 | } 25 | } 26 | 27 | private extension SymbolTableLC { 28 | init(command: symtab_command) { 29 | symbolTableOffset = command.symoff 30 | numberOfSymbols = command.nsyms 31 | stringTableOffset = command.stroff 32 | stringTableSize = command.strsize 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/ThreadLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct ThreadLC: LoadCommand { 9 | public static let id = UInt32(LC_THREAD) 10 | 11 | public init(machData: Data, offset: Int) {} 12 | 13 | public init(pointer: UnsafeRawPointer) {} 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/TwoLevelHintsLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct TwoLevelHintsLC: LoadCommand { 9 | public static let id = UInt32(LC_TWOLEVEL_HINTS) 10 | public let hintOffset: UInt32 11 | public let hintCount: UInt32 12 | 13 | public init(machData: Data, offset: Int) { 14 | let command: twolevel_hints_command = machData.get(atOffset: offset) 15 | self.init(command: command) 16 | } 17 | 18 | public init(pointer: UnsafeRawPointer) { 19 | let command: twolevel_hints_command = pointer.get() 20 | self.init(command: command) 21 | } 22 | } 23 | 24 | private extension TwoLevelHintsLC { 25 | init(command: twolevel_hints_command) { 26 | hintOffset = command.offset 27 | hintCount = command.nhints 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/UUIDLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | @_implementationOnly import MachCore 7 | import MachO 8 | 9 | public struct UUIDLC: LoadCommand { 10 | public static let id = UInt32(LC_UUID) 11 | 12 | public let uuid: UUID 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: uuid_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: uuid_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension UUIDLC { 26 | init(command: uuid_command) { 27 | uuid = UUID(uuid: command.uuid) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/UnixThreadLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct UnixThreadLC: LoadCommand { 9 | public static let id = UInt32(LC_UNIXTHREAD) 10 | 11 | public init(machData: Data, offset: Int) {} 12 | 13 | public init(pointer: UnsafeRawPointer) {} 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/VersionMinIphoneosLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct VersionMinIphoneosLC: LoadCommand { 9 | public static let id = UInt32(LC_VERSION_MIN_IPHONEOS) 10 | 11 | public let version: Version 12 | public let sdk: Version 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: version_min_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: version_min_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension VersionMinIphoneosLC { 26 | init(command: version_min_command) { 27 | version = Version(machVersion: command.version) 28 | sdk = Version(machVersion: command.sdk) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/VersionMinMacosxLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct VersionMinMacosxLC: LoadCommand { 9 | public static let id = UInt32(LC_VERSION_MIN_MACOSX) 10 | 11 | public let version: Version 12 | public let sdk: Version 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: version_min_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: version_min_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension VersionMinMacosxLC { 26 | init(command: version_min_command) { 27 | version = Version(machVersion: command.version) 28 | sdk = Version(machVersion: command.sdk) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/VersionMinTvosLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct VersionMinTvosLC: LoadCommand { 9 | public static let id = UInt32(LC_VERSION_MIN_TVOS) 10 | 11 | public let version: Version 12 | public let sdk: Version 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: version_min_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: version_min_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension VersionMinTvosLC { 26 | init(command: version_min_command) { 27 | version = Version(machVersion: command.version) 28 | sdk = Version(machVersion: command.sdk) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/LoadCommand/VersionMinWatchosLC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct VersionMinWatchosLC: LoadCommand { 9 | public static let id = UInt32(LC_VERSION_MIN_WATCHOS) 10 | 11 | public let version: Version 12 | public let sdk: Version 13 | 14 | public init(machData: Data, offset: Int) { 15 | let command: version_min_command = machData.get(atOffset: offset) 16 | self.init(command: command) 17 | } 18 | 19 | public init(pointer: UnsafeRawPointer) { 20 | let command: version_min_command = pointer.get() 21 | self.init(command: command) 22 | } 23 | } 24 | 25 | private extension VersionMinWatchosLC { 26 | init(command: version_min_command) { 27 | version = Version(machVersion: command.version) 28 | sdk = Version(machVersion: command.sdk) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Mach/Functions/Mach.Section.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public extension Mach { 9 | var cStrings: [String] { 10 | var mutableSelf = self 11 | return mutableSelf.sections.reduce(into: [String]()) { (result: inout [String], section: Section) in 12 | if section.segmentName == SegmentName.__TEXT && section.segmentName == SectionName.__cstring { 13 | let cstring = Section.__Text__cstring(machoData: data, range: section.range) 14 | result.append(contentsOf: cstring.value) 15 | } 16 | 17 | if section.segmentName == SegmentName.__RODATA && section.segmentName == SectionName.__cstring { 18 | let cstring = Section.__Text__cstring(machoData: data, range: section.range) 19 | result.append(contentsOf: cstring.value) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Mach/SubTypes/Mach.Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | public extension Mach { 6 | enum Error: Swift.Error { 7 | case magic(UInt32?) 8 | case fileType(UInt32) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Mach/SubTypes/Mach.Header.FileType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public extension Mach.Header { 9 | enum FileType { 10 | case object 11 | case execute 12 | case fvmlib 13 | case core 14 | case preload 15 | case dylib 16 | case dylinker 17 | case bundle 18 | case dylibStub 19 | case dsym 20 | case kextBundle 21 | case fileset 22 | 23 | init(fileType: UInt32) throws { 24 | switch Int32(fileType) { 25 | case MH_OBJECT: self = .object 26 | case MH_EXECUTE: self = .execute 27 | case MH_FVMLIB: self = .fvmlib 28 | case MH_CORE: self = .core 29 | case MH_PRELOAD: self = .preload 30 | case MH_DYLIB: self = .dylib 31 | case MH_DYLINKER: self = .dylinker 32 | case MH_BUNDLE: self = .bundle 33 | case MH_DYLIB_STUB: self = .dylibStub 34 | case MH_DSYM: self = .dsym 35 | case MH_KEXT_BUNDLE: self = .kextBundle 36 | case MH_FILESET: self = .fileset 37 | default: throw Mach.Error.fileType(fileType) 38 | } 39 | } 40 | } 41 | } 42 | 43 | extension Mach.Header.FileType: RawRepresentable { 44 | public typealias RawValue = UInt32 45 | 46 | public init?(rawValue: RawValue) { 47 | do { 48 | try self.init(fileType: rawValue) 49 | } catch { 50 | return nil 51 | } 52 | } 53 | 54 | public var rawValue: RawValue { fatalError("rawValue has not been implemented") } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Mach/SubTypes/Mach.Header._32_.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2022/10/7. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public extension Mach.Header { 9 | struct _32_ { 10 | public let magic: UInt32 11 | public let cpuType: CPUType 12 | public let cpuSubtype: CPUSubType 13 | public let fileType: FileType 14 | public let commandCount: UInt32 15 | public let commandSize: UInt32 16 | public let flags: Flags 17 | 18 | init(header: mach_header) throws { 19 | magic = header.magic 20 | cpuType = CPUType(cpuType: header.cputype) 21 | cpuSubtype = CPUSubType(cpu_subtype: header.cpusubtype) 22 | fileType = try FileType(fileType: header.filetype) 23 | commandCount = header.ncmds 24 | commandSize = header.sizeofcmds 25 | flags = Flags(rawValue: Int64(header.flags)) 26 | } 27 | } 28 | } 29 | 30 | public extension Mach.Header._32_ { 31 | static var rawSize: Int { 32 | MemoryLayout.size 33 | } 34 | 35 | static var rawStride: Int { 36 | MemoryLayout.stride 37 | } 38 | 39 | var rawSize: Int { 40 | MemoryLayout.size 41 | } 42 | 43 | var rawStride: Int { 44 | MemoryLayout.stride 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Mach/SubTypes/Mach.Header._64_.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2022/10/7. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public extension Mach.Header { 9 | struct _64_ { 10 | public let magic: UInt32 11 | public let cpuType: CPUType 12 | public let cpuSubtype: CPUSubType 13 | public let fileType: FileType 14 | public let commandCount: UInt32 15 | public let commandSize: UInt32 16 | public let flags: Flags 17 | public let reserved: UInt32 18 | 19 | public init(header: mach_header_64) throws { 20 | magic = header.magic 21 | cpuType = CPUType(cpuType: header.cputype) 22 | cpuSubtype = CPUSubType(cpu_subtype: header.cpusubtype) 23 | fileType = try FileType(fileType: header.filetype) 24 | commandCount = header.ncmds 25 | commandSize = header.sizeofcmds 26 | flags = Flags(rawValue: Int64(header.flags)) 27 | reserved = header.reserved 28 | } 29 | } 30 | } 31 | 32 | public extension Mach.Header._64_ { 33 | static var rawSize: Int { 34 | MemoryLayout.size 35 | } 36 | 37 | static var rawStride: Int { 38 | MemoryLayout.stride 39 | } 40 | 41 | var rawSize: Int { 42 | MemoryLayout.size 43 | } 44 | 45 | var rawStride: Int { 46 | MemoryLayout.stride 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Mach/SubTypes/Mach.Header.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Darwin.Mach.machine 6 | import Foundation 7 | 8 | public extension Mach { 9 | enum Header { 10 | case _32(_32_) 11 | case _64(_64_) 12 | } 13 | } 14 | 15 | public extension Mach.Header { 16 | var rawCpuType: cpu_type_t { 17 | switch self { 18 | case let ._32(header): 19 | return header.cpuType.rawValue 20 | case let ._64(header): 21 | return header.cpuType.rawValue 22 | } 23 | } 24 | 25 | var rawCpuSubtype: cpu_subtype_t { 26 | switch self { 27 | case let ._32(header): 28 | return header.cpuSubtype.rawValue 29 | case let ._64(header): 30 | return header.cpuSubtype.rawValue 31 | } 32 | } 33 | 34 | var rawFileType: UInt32 { 35 | switch self { 36 | case let ._32(header): 37 | return header.fileType.rawValue 38 | case let ._64(header): 39 | return header.fileType.rawValue 40 | } 41 | } 42 | 43 | var rawFlags: UInt32 { 44 | switch self { 45 | case let ._32(header): 46 | return UInt32(header.flags.rawValue) 47 | case let ._64(header): 48 | return UInt32(header.flags.rawValue) 49 | } 50 | } 51 | 52 | var commandCount: UInt32 { 53 | switch self { 54 | case let ._32(header): 55 | return header.commandCount 56 | case let ._64(header): 57 | return header.commandCount 58 | } 59 | } 60 | 61 | var commandSize: UInt32 { 62 | switch self { 63 | case let ._32(header): 64 | return header.commandSize 65 | case let ._64(header): 66 | return header.commandSize 67 | } 68 | } 69 | 70 | var rawTypeStride: Int { 71 | switch self { 72 | case let ._32(header): 73 | return header.rawStride 74 | case let ._64(header): 75 | return header.rawStride 76 | } 77 | } 78 | } 79 | 80 | public extension Mach.Header { 81 | var magic: UInt32 { 82 | switch self { 83 | case let ._32(content): 84 | return content.magic 85 | case let ._64(content): 86 | return content.magic 87 | } 88 | } 89 | 90 | var cpuType: CPUType { 91 | switch self { 92 | case let ._32(content): 93 | return content.cpuType 94 | case let ._64(content): 95 | return content.cpuType 96 | } 97 | } 98 | 99 | var fileType: FileType { 100 | switch self { 101 | case let ._32(content): 102 | return content.fileType 103 | case let ._64(content): 104 | return content.fileType 105 | } 106 | } 107 | 108 | var flags: Mach.Header.Flags { 109 | switch self { 110 | case let ._32(content): 111 | return content.flags 112 | case let ._64(content): 113 | return content.flags 114 | } 115 | } 116 | 117 | var is64bit: Bool { 118 | switch self { 119 | case ._32: 120 | return false 121 | case ._64: 122 | return true 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/ProcessMach/ProcessMach.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2021/8/13. 3 | // 4 | 5 | import CodeSignParser 6 | import struct MachO.loader.mach_header 7 | import struct MachO.loader.mach_header_64 8 | import var MachO.loader.MH_CIGAM 9 | import var MachO.loader.MH_CIGAM_64 10 | import var MachO.loader.MH_MAGIC 11 | import var MachO.loader.MH_MAGIC_64 12 | 13 | public struct ProcessMach { 14 | public let pointer: UnsafeRawPointer 15 | public let header: Header 16 | public let allLoadCommands: [LoadCommand] 17 | 18 | public init(headerPointer: UnsafePointer) throws { 19 | let baseHeader = headerPointer.pointee 20 | switch baseHeader.magic { 21 | case MH_MAGIC, MH_CIGAM: 22 | try self.init(headerPointer32: headerPointer) 23 | case MH_MAGIC_64, MH_CIGAM_64: 24 | try self.init(headerPointer64: headerPointer) 25 | default: 26 | throw Error.magic(baseHeader.magic) 27 | } 28 | } 29 | } 30 | 31 | private extension ProcessMach { 32 | init(headerPointer32: UnsafePointer) throws { 33 | pointer = UnsafeRawPointer(headerPointer32) 34 | header = try ._32(Header._32_(header: headerPointer32.pointee)) 35 | 36 | allLoadCommands = Self.parseLoadCommands(startPointer: pointer.advanced(by: header.rawTypeStride), count: header.commandCount) 37 | } 38 | 39 | init(headerPointer64: UnsafePointer) throws { 40 | pointer = UnsafeRawPointer(headerPointer64) 41 | let headerPointer = headerPointer64.withMemoryRebound(to: mach_header_64.self, capacity: 1) { $0 } 42 | header = try ._64(Header._64_(header: headerPointer.pointee)) 43 | 44 | allLoadCommands = Self.parseLoadCommands(startPointer: pointer.advanced(by: header.rawTypeStride), count: header.commandCount) 45 | } 46 | } 47 | 48 | public extension ProcessMach { 49 | var fileType: Header.FileType { header.fileType } 50 | 51 | var cpuType: CPUType { header.cpuType } 52 | 53 | var flags: Header.Flags { header.flags } 54 | } 55 | 56 | public extension ProcessMach { 57 | func loadCommands() -> [T]? { allLoadCommands.compactMap { $0 as? T } } 58 | 59 | func loadCommand() -> T? { loadCommands()?.first } 60 | } 61 | 62 | public extension ProcessMach { 63 | var stringTable: StringTable? { 64 | StringTable(processMach: self) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/ProcessMach/SubTypes/ProcessMach.Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2021/8/13. 3 | // 4 | 5 | public extension ProcessMach { 6 | typealias Error = Mach.Error 7 | } 8 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/ProcessMach/SubTypes/ProcessMach.Header.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import Darwin.Mach.machine 6 | import Foundation 7 | 8 | public extension ProcessMach { 9 | typealias Header = Mach.Header 10 | } 11 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Section/Other/Section.__RODATA__cstring.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | 7 | public extension Section { 8 | struct __RODATA__cstring: SectionContent { 9 | public typealias Value = [String] 10 | 11 | public let value: [String] 12 | 13 | public init(machoData: Data, range: Range) { 14 | value = machoData.subdata(in: range.intRange).split(separator: 0).compactMap { sequence -> String? in 15 | String(bytes: sequence, encoding: .utf8) 16 | } 17 | } 18 | 19 | public static let segmentName: SegmentName = .__RODATA 20 | public static let sectionName: SectionName = .__cstring 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Section/Section.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/24. 3 | // 4 | 5 | import CoreGraphics 6 | import Foundation 7 | import MachO 8 | 9 | public struct Section { 10 | public let name: String 11 | public let segmentName: String 12 | public let range: Range 13 | public let align: UInt64 // power(2,sectionHeader align) 14 | public let count: UInt64 15 | 16 | init(sectionHeader: SectionHeader, machoData: Data) { 17 | name = sectionHeader.sectionName 18 | range = Range(offset: UInt64(sectionHeader.offset), count: UInt64(sectionHeader.size)) 19 | segmentName = sectionHeader.segmentName 20 | align = UInt64(pow(CGFloat(2), CGFloat(sectionHeader.align))) 21 | count = UInt64(sectionHeader.size) / align 22 | } 23 | 24 | init(sectionHeader: SectionHeader64, machoData: Data) { 25 | name = sectionHeader.sectionName 26 | range = Range(offset: UInt64(sectionHeader.offset), count: sectionHeader.size) 27 | segmentName = sectionHeader.segmentName 28 | align = UInt64(pow(CGFloat(2), CGFloat(sectionHeader.align))) 29 | count = sectionHeader.size / align 30 | } 31 | } 32 | 33 | public extension Section { 34 | func content(machData: Data) -> T { 35 | T(machoData: machData, range: range) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Section/SectionContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | 7 | public protocol SectionContent { 8 | associatedtype Value 9 | var value: Value { get } 10 | init(machoData: Data, range: Range) 11 | 12 | static var segmentName: SegmentName { get } 13 | static var sectionName: SectionName { get } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Section/SectionName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SectionName: RawRepresentable { 9 | public typealias RawValue = String 10 | public let rawValue: RawValue 11 | 12 | public init(rawValue: String) { 13 | self.rawValue = rawValue 14 | } 15 | } 16 | 17 | extension SectionName: Equatable { 18 | static func == (lhs: String, rhs: Self) -> Bool { 19 | lhs == rhs.rawValue 20 | } 21 | } 22 | 23 | public extension SectionName { 24 | static let __text = Self(rawValue: SECT_TEXT) 25 | static let __cstring = Self(rawValue: "__cstring") 26 | static let __const = Self(rawValue: "__const") 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Section/__Text/__cstring/Section.__TEXT__cstring.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | 7 | public extension Section { 8 | struct __Text__cstring: SectionContent { 9 | public typealias Value = [String] 10 | public let value: Value 11 | 12 | public init(machoData: Data, range: Range) { 13 | value = machoData.subdata(in: range.intRange).split(separator: 0).compactMap { sequence -> String? in 14 | String(bytes: sequence, encoding: .utf8) 15 | } 16 | } 17 | 18 | public static var segmentName: SegmentName = .__TEXT 19 | 20 | public static var sectionName: SectionName = .__cstring 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Segment/Segment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/28. 3 | // 4 | 5 | import Foundation 6 | @_implementationOnly import MachCore 7 | 8 | public struct Segment { 9 | public let name: String 10 | public let vmRange: Range 11 | public let fileRange: Range 12 | public let sections: [Section] 13 | } 14 | 15 | public extension Segment { 16 | init(machData: Data, segmentLC: SegmentLC) { 17 | name = segmentLC.segmentName 18 | vmRange = Range(offset: UInt64(segmentLC.vmAddress), count: UInt64(segmentLC.vmSize)) 19 | fileRange = Range(offset: UInt64(segmentLC.fileOffset), count: UInt64(segmentLC.fileSize)) 20 | sections = segmentLC.sectionHeaders.map { Section(sectionHeader: $0, machoData: machData) } 21 | } 22 | 23 | init(machData: Data, segment64LC: Segment64LC) { 24 | name = segment64LC.segmentName 25 | vmRange = Range(offset: segment64LC.vmAddress, count: segment64LC.vmSize) 26 | fileRange = Range(offset: segment64LC.fileOffset, count: segment64LC.fileSize) 27 | sections = segment64LC.sectionHeaders.map { Section(sectionHeader: $0, machoData: machData) } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Segment/SegmentName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SegmentName: RawRepresentable { 9 | public typealias RawValue = String 10 | public let rawValue: RawValue 11 | 12 | public init(rawValue: String) { 13 | self.rawValue = rawValue 14 | } 15 | } 16 | 17 | extension SegmentName: Equatable { 18 | static func == (lhs: String, rhs: Self) -> Bool { 19 | lhs == rhs.rawValue 20 | } 21 | } 22 | 23 | public extension SegmentName { 24 | static let __TEXT = Self(rawValue: SEG_TEXT) 25 | static let __DATA = Self(rawValue: SEG_DATA) 26 | static let __DATA_CONST = Self(rawValue: "__DATA_CONST") 27 | static let __RODATA = Self(rawValue: "__RODATA") 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Symbols/StringTable.Symbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/10/12. 3 | // 4 | 5 | import Foundation 6 | 7 | public extension StringTable { 8 | struct Symbol { 9 | public let offset: Int 10 | public let value: String 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Symbols/StringTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/10/12. 3 | // 4 | 5 | public struct StringTable { 6 | let symbols: [Symbol] 7 | 8 | public init(symbols: [Symbol]) { 9 | self.symbols = symbols 10 | } 11 | } 12 | 13 | public extension StringTable { 14 | init?(mach: Mach) { 15 | guard let symbolTableLC: SymbolTableLC = mach.loadCommand() else { return nil } 16 | let offset = symbolTableLC.stringTableOffset 17 | let size = symbolTableLC.stringTableSize 18 | var currentOffset: UInt32 = 0 19 | var symbols = [Symbol]() 20 | while currentOffset < size { 21 | let string = mach.data.get(atOffset: Int(currentOffset + offset)) 22 | let symbol = Symbol(offset: Int(currentOffset + offset), value: string) 23 | symbols.append(symbol) 24 | switch string.count == 0 { 25 | case true: 26 | currentOffset += 1 27 | case false: 28 | currentOffset += UInt32(string.count + 1) 29 | } 30 | } 31 | self.symbols = symbols 32 | } 33 | } 34 | 35 | public extension StringTable { 36 | init?(processMach: ProcessMach) { 37 | guard let symbolTableLC: SymbolTableLC = processMach.loadCommand() else { return nil } 38 | let offset = symbolTableLC.stringTableOffset 39 | let size = symbolTableLC.stringTableSize 40 | var currentOffset: UInt32 = 0 41 | var symbols = [Symbol]() 42 | while currentOffset < size { 43 | let string = processMach.pointer.advanced(by: Int(currentOffset + offset)).getString() 44 | let symbol = Symbol(offset: Int(currentOffset + offset), value: string) 45 | symbols.append(symbol) 46 | currentOffset += UInt32(string.count + 1) 47 | } 48 | self.symbols = symbols 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Symbols/SymbolTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/10/12. 3 | // 4 | 5 | import Foundation 6 | import MachO 7 | 8 | public struct SymbolTable { 9 | let localSymbols: Range 10 | let externalSymbols: Range 11 | let undefinedSymbols: Range 12 | 13 | init?(mach: Mach) { 14 | guard let symtabLC: SymbolTableLC = mach.loadCommand() else { return nil } 15 | guard let dysymtabLC: DynamicSymbolTableLC = mach.loadCommand() else { return nil } 16 | let nlistSize: Int 17 | switch mach.header.is64bit { 18 | case true: 19 | nlistSize = MemoryLayout.stride 20 | case false: 21 | nlistSize = MemoryLayout.stride 22 | } 23 | let offset = symtabLC.symbolTableOffset 24 | let size = symtabLC.numberOfSymbols * UInt32(nlistSize) 25 | localSymbols = Range(offset: dysymtabLC.localSymbolIndex, count: dysymtabLC.localSymbolCount) 26 | externalSymbols = Range(offset: dysymtabLC.definedExternalSymbolIndex, count: dysymtabLC.definedExternalSymbolCount) 27 | undefinedSymbols = Range(offset: dysymtabLC.undefinedExternalSymbolIndex, count: dysymtabLC.undefinedExternalSymbolCount) 28 | var currentOffset = 0 29 | while currentOffset < size { 30 | switch mach.header.is64bit { 31 | case true: 32 | let nlist: nlist_64 = mach.data.get(atOffset: currentOffset + Int(offset)) 33 | nlist.n_un.n_strx 34 | case false: 35 | let nlist: nlist = mach.data.get(atOffset: currentOffset + Int(offset)) 36 | } 37 | currentOffset += nlistSize 38 | } 39 | // todo symbol table 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Version/SourceVersion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/28. 3 | // 4 | 5 | import Foundation 6 | 7 | public struct SourceVersion { 8 | public let a: UInt64 9 | public let b: UInt64 10 | public let c: UInt64 11 | public let d: UInt64 12 | public let e: UInt64 13 | 14 | init(version: UInt64) { 15 | a = (version >> 40) & 0xFFFFFF 16 | b = (version >> 30) & 0x3FF 17 | c = (version >> 20) & 0x3FF 18 | d = (version >> 10) & 0x3FF 19 | e = (version >> 0) & 0x3FF 20 | } 21 | } 22 | 23 | extension SourceVersion: CustomStringConvertible { 24 | public var description: String { "\(a).\(b).\(c).\(d).\(e)" } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/MachOParser/MachOTypes/Version/Version.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/7/27. 3 | // 4 | 5 | import Foundation 6 | 7 | public struct Version { 8 | public let major: UInt 9 | public let minor: UInt 10 | public let patch: UInt 11 | 12 | init(machVersion: UInt32) { 13 | major = UInt((machVersion & 0xFFFF_0000) >> 16) 14 | minor = UInt((machVersion & 0x0000_FF00) >> 8) 15 | patch = UInt((machVersion & 0x0000_00FF) >> 0) 16 | } 17 | } 18 | 19 | extension Version: CustomStringConvertible { 20 | public var description: String { "\(major).\(minor).\(patch)" } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftDemangler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | 7 | struct SwiftDemangler { private init() {} } 8 | 9 | extension SwiftDemangler { 10 | static func canDemangleFromRuntime(_ name: String) -> Bool { 11 | name.hasPrefix("So") || name.hasPrefix("$So") || name.hasPrefix("_$So") || name.hasPrefix("_T") 12 | } 13 | 14 | static func demangledFromRuntime(name: String) -> String { 15 | let fixedName: String = name.hasPrefix("So") ? "$s" + name : name 16 | guard canDemangleFromRuntime(fixedName) || canDemangleFromRuntime(name) else { return name } 17 | 18 | let bufLen = 128 // may be 128 is big enough 19 | var buf = [Int8](repeating: 0, count: bufLen) 20 | let retLen = _getDemangledName(fixedName, &buf, bufLen) 21 | 22 | if retLen > 0 && retLen < bufLen { 23 | let resultBuf: [UInt8] = buf[0.. String { 31 | if canDemangleFromRuntime(name) { return demangledFromRuntime(name: name) } 32 | guard name.isASCII else { return name } 33 | guard let type = _getTypeByMangledNameInContext(name, name.count, nil, nil) else { 34 | return name 35 | } 36 | let typeName = String(describing: type) 37 | return typeName 38 | } 39 | 40 | static func removeModulePrefix(typeName: String) -> String { 41 | if let idx = typeName.firstIndex(of: ".") { 42 | let useIdx = typeName.index(after: idx) 43 | return String(typeName.suffix(from: useIdx)) 44 | } 45 | 46 | return typeName 47 | } 48 | } 49 | 50 | @_silgen_name("swift_getTypeByMangledNameInContext") 51 | func _getTypeByMangledNameInContext(_ name: UnsafePointer, 52 | _ nameLength: Int, 53 | _ genericContext: UnsafeRawPointer?, 54 | _ genericArguments: UnsafeRawPointer?) -> Any.Type? 55 | 56 | @_silgen_name("swift_demangle_getDemangledName") 57 | func _getDemangledName(_ name: UnsafePointer?, _ output: UnsafeMutablePointer?, _ len: Int) -> Int 58 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Protocol/Description/SwiftMeta.ProtocolDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct ProtocolDescriptor { 9 | let flags: UInt32 10 | let parent: Int32 11 | let name: Int32 // offset 12 | let numRequirementsInSignature: UInt32 13 | let numRequirements: UInt32 14 | let associatedTypeNames: Int32 // offset 15 | } 16 | } 17 | 18 | extension SwiftMeta.ProtocolDescriptor { 19 | func nameOffset(start: Int) -> Int { 20 | start + 21 | MemoryLayout.size(ofValue: flags) + 22 | MemoryLayout.size(ofValue: parent) + 23 | Int(name) 24 | } 25 | 26 | func associatedTypeNamesOffset(start: Int) -> Int { 27 | start + 28 | MemoryLayout.size(ofValue: flags) + 29 | MemoryLayout.size(ofValue: parent) + 30 | MemoryLayout.size(ofValue: name) + 31 | MemoryLayout.size(ofValue: numRequirementsInSignature) + 32 | MemoryLayout.size(ofValue: numRequirements) + 33 | Int(associatedTypeNames) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Protocol/SwiftMeta.ProtocolType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | public extension SwiftMeta { 9 | struct ProtocolType { 10 | let raw: ProtocolDescriptor 11 | let offset: Int 12 | let name: String 13 | let associatedTypeNames: String 14 | let flag: Set 15 | 16 | init(raw: ProtocolDescriptor, offset: Int, name: String, associatedTypeNames: String) { 17 | self.raw = raw 18 | self.offset = offset 19 | self.name = name 20 | self.associatedTypeNames = associatedTypeNames 21 | flag = Set(rawValue: Int64(raw.flags)) 22 | } 23 | } 24 | } 25 | 26 | extension SwiftMeta.ProtocolType { 27 | /// https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataValues.h `class ProtocolDescriptorFlags` 28 | enum Flag { 29 | case isSwift 30 | case classConstraint 31 | case dispatchStrategyMask 32 | case dispatchStrategyShift 33 | case specialProtocolMask 34 | case specialProtocolShift 35 | case isResilient 36 | case objCReserved 37 | } 38 | } 39 | 40 | extension SwiftMeta.ProtocolType.Flag: Option { 41 | var value: Int64 { 42 | switch self { 43 | case .isSwift: 44 | return 1 << 0 45 | case .classConstraint: 46 | return 1 << 1 47 | case .dispatchStrategyMask: 48 | return 0xF << 2 49 | case .dispatchStrategyShift: 50 | return 2 51 | case .specialProtocolMask: 52 | return 0x0000_03C0 53 | case .specialProtocolShift: 54 | return 6 55 | case .isResilient: 56 | return 1 << 10 57 | case .objCReserved: 58 | return 0xFFFF_0000 59 | } 60 | } 61 | } 62 | 63 | extension SwiftMeta.ProtocolType.Flag: CustomStringConvertible { 64 | public var description: String { 65 | switch self { 66 | case .isSwift: 67 | return "IsSwift" 68 | case .classConstraint: 69 | return "ClassConstraint" 70 | case .dispatchStrategyMask: 71 | return "DispatchStrategyMask" 72 | case .dispatchStrategyShift: 73 | return "DispatchStrategyShift" 74 | case .specialProtocolMask: 75 | return "SpecialProtocolMask" 76 | case .specialProtocolShift: 77 | return "SpecialProtocolShift" 78 | case .isResilient: 79 | return "IsResilient" 80 | case .objCReserved: 81 | return "_ObjCReserved" 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/SwiftMeta.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/4. 3 | // 4 | 5 | import Foundation 6 | 7 | public enum SwiftMeta {} 8 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.ClassDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct ClassDescriptor { 9 | let flags: UInt32 10 | let parent: Int32 11 | let name: Int32 12 | let accessFunction: Int32 13 | let fieldDescriptor: Int32 14 | let superclassType: Int32 15 | let metadataNegativeSizeInWords: UInt32 16 | let metadataPositiveSizeInWords: UInt32 17 | let numImmediateMembers: UInt32 18 | let numFields: UInt32 19 | } 20 | } 21 | 22 | extension SwiftMeta.ClassDescriptor { 23 | func nameOffset(start: Int) -> Int { 24 | start + 25 | MemoryLayout.size(ofValue: flags) + 26 | MemoryLayout.size(ofValue: parent) + 27 | Int(name) 28 | } 29 | 30 | func accessFunctionOffset(start: Int) -> Int { 31 | start + 32 | MemoryLayout.size(ofValue: flags) + 33 | MemoryLayout.size(ofValue: parent) + 34 | MemoryLayout.size(ofValue: name) + 35 | Int(accessFunction) 36 | } 37 | 38 | func fieldDescriptorOffset(start: Int) -> Int { 39 | start + 40 | MemoryLayout.size(ofValue: flags) + 41 | MemoryLayout.size(ofValue: parent) + 42 | MemoryLayout.size(ofValue: name) + 43 | MemoryLayout.size(ofValue: accessFunction) + 44 | Int(fieldDescriptor) 45 | } 46 | 47 | func superclassTypeOffset(start: Int) -> Int? { 48 | if superclassType == 0 { return nil } 49 | return start + 50 | MemoryLayout.size(ofValue: flags) + 51 | MemoryLayout.size(ofValue: parent) + 52 | MemoryLayout.size(ofValue: name) + 53 | MemoryLayout.size(ofValue: accessFunction) + 54 | MemoryLayout.size(ofValue: fieldDescriptor) + 55 | Int(superclassType) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.ContextDescriptorFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct ContextDescriptorFlags { 9 | let value: UInt32 10 | /// The kind of context this descriptor describes. 11 | let kind: ContextDescriptorKind? 12 | /// Whether the context being described is generic. 13 | let isGeneric: Bool 14 | /// Whether this is a unique record describing the referenced context. 15 | let isUnique: Bool 16 | /// The format version of the descriptor. Higher version numbers may have 17 | /// additional fields that aren't present in older versions. 18 | let version: UInt8 19 | /// The most significant two bytes of the flags word, which can have 20 | /// kind-specific meaning. 21 | let kindSpecificFlags: UInt16 22 | 23 | init(value: UInt32) { 24 | self.value = value 25 | 26 | kind = ContextDescriptorKind(value: value & 0x1F) 27 | isGeneric = (value & 0x80) != 0 28 | isUnique = (value & 0x40) != 0 29 | version = UInt8((value >> 8) & 0xFF) 30 | kindSpecificFlags = UInt16((value >> 16) & 0xFFFF) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.ContextDescriptorKind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | /// https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataValues.h `enum class ContextDescriptorKind` 9 | enum ContextDescriptorKind { 10 | /// This context descriptor represents a module. 11 | case module 12 | /// This context descriptor represents an extension. 13 | case `extension` 14 | /// This context descriptor represents an anonymous possibly-generic context 15 | /// such as a function body. 16 | case anonymous 17 | /// This context descriptor represents a protocol context. 18 | case `protocol` 19 | /// This context descriptor represents an opaque type alias. 20 | case opaque 21 | /// First kind that represents a type of any sort. 22 | // case Type_First 23 | /// This context descriptor represents a class. 24 | case `class` 25 | /// This context descriptor represents a struct. 26 | case `struct` 27 | /// This context descriptor represents an enum. 28 | case `enum` 29 | /// Last kind that represents a type of any sort. 30 | // case Type_Last = 31 31 | } 32 | } 33 | 34 | extension SwiftMeta.ContextDescriptorKind { 35 | init?(value: UInt32) { 36 | switch value { 37 | case 0: 38 | self = .module 39 | case 1: 40 | self = .extension 41 | case 2: 42 | self = .anonymous 43 | case 3: 44 | self = .protocol 45 | case 4: 46 | self = .opaque 47 | case 16: 48 | self = .class 49 | case 17: 50 | self = .struct 51 | case 18: 52 | self = .enum 53 | default: 54 | return nil 55 | } 56 | } 57 | } 58 | 59 | extension SwiftMeta.ContextDescriptorKind: CustomStringConvertible { 60 | public var description: String { 61 | switch self { 62 | case .module: 63 | return "Module" 64 | case .extension: 65 | return "Extension" 66 | case .anonymous: 67 | return "Anonymous" 68 | case .protocol: 69 | return "Protocol" 70 | case .opaque: 71 | return "OpaqueType" 72 | case .class: 73 | return "Class" 74 | case .struct: 75 | return "Struct" 76 | case .enum: 77 | return "Enum" 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.EnumDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct EnumDescriptor { 9 | let flags: UInt32 10 | let parent: Int32 11 | let name: Int32 12 | let accessFunction: Int32 13 | let fieldDescriptor: Int32 14 | let numPayloadCasesAndPayloadSizeOffset: UInt32 15 | let numEmptyCases: UInt32 16 | } 17 | } 18 | 19 | extension SwiftMeta.EnumDescriptor { 20 | func nameOffset(start: Int) -> Int { 21 | start + 22 | MemoryLayout.size(ofValue: flags) + 23 | MemoryLayout.size(ofValue: parent) + 24 | Int(name) 25 | } 26 | 27 | func accessFunctionOffset(start: Int) -> Int { 28 | start + 29 | MemoryLayout.size(ofValue: flags) + 30 | MemoryLayout.size(ofValue: parent) + 31 | MemoryLayout.size(ofValue: name) + 32 | Int(accessFunction) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.FieldDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/7. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct FieldDescriptor { 9 | let mangledTypeName: Int32 10 | let superclass: Int32 11 | let kind: UInt16 12 | let fieldRecordSize: UInt16 13 | let numFields: UInt32 14 | let fieldRecords: UnsafeRawPointer 15 | } 16 | } 17 | 18 | extension SwiftMeta.FieldDescriptor { 19 | func mangledTypeNameOffset(start: Int) -> Int { 20 | start + Int(mangledTypeName) 21 | } 22 | 23 | func fieldRecordsOffset(start: Int) -> Int { 24 | start + 25 | MemoryLayout.size(ofValue: mangledTypeName) + 26 | MemoryLayout.size(ofValue: superclass) + 27 | MemoryLayout.size(ofValue: kind) + 28 | MemoryLayout.size(ofValue: fieldRecordSize) + 29 | MemoryLayout.size(ofValue: numFields) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.FieldRecord.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/7. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct FieldRecord { 9 | let flags: UInt32 10 | let mangledTypeName: Int32 11 | let fieldName: Int32 12 | } 13 | } 14 | 15 | extension SwiftMeta.FieldRecord { 16 | func mangledTypeNameOffset(start: Int) -> Int { 17 | start + 18 | MemoryLayout.size(ofValue: flags) + 19 | Int(mangledTypeName) 20 | } 21 | 22 | func fieldNameOffset(start: Int) -> Int { 23 | start + 24 | MemoryLayout.size(ofValue: flags) + 25 | MemoryLayout.size(ofValue: mangledTypeName) + 26 | Int(fieldName) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.NominalDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct NominalDescriptor { 9 | let flags: UInt32 10 | let parent: Int32 11 | let name: Int32 12 | let accessFunction: Int32 13 | } 14 | } 15 | 16 | extension SwiftMeta.NominalDescriptor { 17 | func nameOffset(start: Int) -> Int { 18 | start + 19 | MemoryLayout.size(ofValue: flags) + 20 | MemoryLayout.size(ofValue: parent) + 21 | Int(name) 22 | } 23 | 24 | func accessFunctionOffset(start: Int) -> Int { 25 | start + 26 | MemoryLayout.size(ofValue: flags) + 27 | MemoryLayout.size(ofValue: parent) + 28 | MemoryLayout.size(ofValue: name) + 29 | Int(accessFunction) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/Description/SwiftMeta.StructDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/5. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct StructDescriptor { 9 | let flags: UInt32 10 | let parent: Int32 11 | let name: Int32 12 | let accessFunction: Int32 13 | let fieldDescriptor: Int32 14 | let numFields: UInt32 15 | let fieldOffsetVectorOffset: UInt32 16 | } 17 | } 18 | 19 | extension SwiftMeta.StructDescriptor { 20 | func nameOffset(start: Int) -> Int { 21 | start + 22 | MemoryLayout.size(ofValue: flags) + 23 | MemoryLayout.size(ofValue: parent) + 24 | Int(name) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/SwiftMeta.ClassType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/10. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct ClassType { 9 | let name: String 10 | let superName: String? 11 | 12 | let mangledName: String 13 | 14 | let fields: [Field] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/MachOSwiftParser/SwiftMeta/Type/SwiftMeta.Field.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/8/10. 3 | // 4 | 5 | import Foundation 6 | 7 | extension SwiftMeta { 8 | struct Field { 9 | let name: String 10 | let type: String 11 | 12 | let mangledTypeNameOffset: Int 13 | 14 | init(name: String, type: String, mangledTypeNameOffset: Int) { 15 | self.name = name 16 | self.type = type 17 | self.mangledTypeNameOffset = mangledTypeNameOffset 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/ObjCObfuscation/Mangling/SymbolMangling.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | 7 | protocol SymbolMangling { 8 | func mangleSymbols(_: ObfuscationSymbols) -> SymbolManglingMap 9 | } 10 | -------------------------------------------------------------------------------- /Sources/ObjCObfuscation/Symbol/Mach.CPUID.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | typealias CpuId = Int64 9 | 10 | extension Mach { 11 | var asCpuId: CpuId { 12 | (Int64(UInt32(bitPattern: header.rawCpuType)) << 32) | Int64(UInt32(bitPattern: header.rawCpuSubtype)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/ObjCObfuscation/Symbol/ObfuscationSymbols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct ObfuscationSymbols { 9 | let whiteList: ObjCSymbols 10 | let blackList: ObjCSymbols 11 | let removedList: ObjCSymbols 12 | let exportTriesPerCpuIdPerURL: [URL: [CpuId: Trie]] 13 | } 14 | -------------------------------------------------------------------------------- /Sources/ObjCObfuscation/Symbol/ObjCSymbols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | 7 | struct ObjCSymbols { 8 | let classes: Set 9 | let selectors: Set 10 | } 11 | -------------------------------------------------------------------------------- /Sources/ObjCObfuscation/Symbol/SymbolManglingMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mengyu Li on 2020/12/29. 3 | // 4 | 5 | import Foundation 6 | import MachOParser 7 | 8 | struct SymbolManglingMap { 9 | typealias ObfuscationTriePair = (unobfuscated: Trie, obfuscated: Trie) 10 | 11 | typealias TriePerCpu = [CpuId: ObfuscationTriePair] 12 | 13 | let selectors: [String: String] 14 | 15 | let classNames: [String: String] 16 | 17 | let exportTrieObfuscationMap: [URL: TriePerCpu] 18 | } 19 | -------------------------------------------------------------------------------- /Tests/MachOEditorTests/MachOEditorTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import MachOEditor 3 | import MachOParser 4 | import XCTest 5 | 6 | final class MachOEditorTests: XCTestCase { 7 | func testExample() throws {} 8 | 9 | static var allTests = [ 10 | ("testExample", testExample), 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /Tests/MachOParserTests/MachOParserTests.swift: -------------------------------------------------------------------------------- 1 | @testable import CodeSignParser 2 | import Foundation 3 | import MachO 4 | @testable import MachOParser 5 | import XCTest 6 | 7 | final class MachOParserTests: XCTestCase { 8 | func testCodeSignature() throws { 9 | let mach = try getMach() 10 | guard let codeSignature = mach.codeSignature else { return } 11 | print("\(codeSignature.codeDirectoryList)") 12 | if let entitlements = codeSignature.entitlements { 13 | print("\(entitlements)") 14 | } 15 | print("==========================") 16 | guard let codeSignatureLC: CodeSignatureLC = mach.loadCommand() else { return } 17 | let offset = Int(codeSignatureLC.dataOffset) 18 | let size = Int(codeSignatureLC.dataSize) 19 | let subData = mach.data.subdata(in: offset..<(offset + size)) 20 | let codeSignature2 = CodeSignature(codeSignatureData: subData) 21 | print("\(codeSignature2.codeDirectoryList)") 22 | if let entitlements2 = codeSignature2.entitlements { 23 | print("\(entitlements2)") 24 | } 25 | } 26 | 27 | func testExports() throws { 28 | let mach = try getMach() 29 | print(mach.exports) 30 | } 31 | 32 | func testStringTable() throws { 33 | let mach = try getMach() 34 | guard let symbols = mach.stringTable?.symbols else { return } 35 | symbols.forEach { 36 | print("0x\(String($0.offset, radix: 0x10, uppercase: true)) : \($0.value)") 37 | } 38 | } 39 | 40 | private func getMach(path: String = "/Applications/Xcode.app/Contents/MacOS/Xcode") throws -> Mach { 41 | let image = try Image(url: URL(fileURLWithPath: path)) 42 | switch image.content { 43 | case let .fat(fat): 44 | guard let arm64Mach = (fat.architectures.compactMap { architecture -> Mach? in 45 | if architecture.mach.cpuType == .arm64 { return architecture.mach } 46 | return nil 47 | }) 48 | .first else { fatalError() } 49 | return arm64Mach 50 | case let .mach(mach): 51 | return mach 52 | } 53 | } 54 | 55 | static var allTests = [ 56 | ("testCodeSignature", testCodeSignature), 57 | ] 58 | } 59 | 60 | extension MachOParserTests { 61 | func testProcessMach() throws { 62 | guard let headerFromDyld: UnsafePointer = _dyld_get_image_header(0) else { 63 | fatalError() 64 | } 65 | 66 | let processMach = try ProcessMach(headerPointer: headerFromDyld) 67 | print("\(processMach.header)") 68 | if let stringTable = processMach.stringTable { 69 | print("\(stringTable)") 70 | } 71 | } 72 | } 73 | --------------------------------------------------------------------------------