Skip to content

Commit

Permalink
Merge pull request #124 from p-x9/feature/on-memory-dyld-cache
Browse files Browse the repository at this point in the history
Support dyld cache on memory
  • Loading branch information
p-x9 authored Oct 14, 2024
2 parents 351aaae + 9611e40 commit edb19a5
Show file tree
Hide file tree
Showing 24 changed files with 1,930 additions and 231 deletions.
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ In addition to file reading, parsing of images in memory by `_dyld_get_image_hea

### Load from memory

For reading from memory, use the `MachO` structure.
For reading from memory, use the `MachOImage` structure.

It can be initialized by using the Mach-O Header pointer obtained by `_dyld_get_image_header`.

Expand Down Expand Up @@ -65,24 +65,29 @@ case .fat(let fatFile): // Fat file

### Main properties and methods

Both `MachO` and `MachOFile` can use essentially the same properties and methods.
Both `MachOImage` and `MachOFile` can use essentially the same properties and methods.
The available methods are defined in the following file as the `MachORepresentable` protocol.

[MachORepresentable](./Sources/MachOKit/Protocol/MachORepresentable.swift)


### Dyld Cache

loading of `dyld_shared_cache` is also supported.
Loading of `dyld_shared_cache` is also supported.

The available methods are defined in the following file as the `DyldCacheRepresentable` protocol.

[DyldCacheRepresentable](./Sources/MachOKit/Protocol/DyldCacheRepresentable.swift)

#### Dyld Cache (File)

```swift
let path = "/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_x86_64h"
let path = "/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e"
let url = URL(fileURLWithPath: path)

let cache = try! DyldCache(url: url)
```

It is also possible to extract machO information contained in `dyld _shared _cache`.
It is also possible to extract machO information contained in `dyld_shared_cache`.
The machO extracted is of type `MachOFile`.
As with reading from a single MachO file, various analyses are possible.

Expand All @@ -102,6 +107,38 @@ for machO in machOs {
// ...
```

#### Dyld Cache (on memory)

On the Apple platform, the dyld cache is deployed in memory.

```swift
var size = 0
guard let ptr = _dyld_get_shared_cache_range(&size) else {
return
}
let cache = try! DyldCacheLoaded(ptr: ptr)
```

It is also possible to extract machO information contained in `dyld_shared_cache`.
The machO extracted is of type `MachOImage`.
As with reading from a single MachO image, various analyses are possible.

```swift
let machOs = cache.machOImages()
for machO in machOs {
print(
String(Int(bitPattern: machO.ptr), radix: 16),
machO.path!,
machO.header.ncmds
)
}

// 193438000 /usr/lib/libobjc.A.dylib 24
// 193489000 /usr/lib/dyld 15
// 193513000 /usr/lib/system/libsystem_blocks.dylib 24
// ...
```

### Example Codes

There are a variety of uses, but most show a basic example that prints output to the Test directory.
Expand All @@ -116,11 +153,16 @@ The following file contains sample code.
The following file contains sample code.
[MachOFilePrintTests](./Tests/MachOKitTests/MachOFilePrintTests.swift)

#### Dyld Cache
#### Dyld Cache (file)

The following file contains sample code.
[DyldCachePrintTests](./Tests/MachOKitTests/DyldCachePrintTests.swift)

#### Dyld Cache (on memory)

The following file contains sample code.
[DyldCacheLoadedPrintTests](./Tests/MachOKitTests/DyldCacheLoadedPrintTests.swift)

## Related Projects

- [MachOKitSPM](https://github.com/p-x9/MachOKit-SPM)
Expand All @@ -132,6 +174,10 @@ The following file contains sample code.
- [AntiFishHook](https://github.com/p-x9/swift-anti-fishhook)
A Swift library to deactivate fishhook. (Anti-FishHook)

### Other binary type
- [ELFKit](https://github.com/p-x9/ELFKit)
Elf format

## License

MachOKit is released under the MIT License. See [LICENSE](./LICENSE)
82 changes: 12 additions & 70 deletions Sources/MachOKit/DyldCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public class DyldCache {
public class DyldCache: DyldCacheRepresentable {
/// URL of loaded dyld cache file
public let url: URL
let fileHandle: FileHandle
Expand Down Expand Up @@ -346,74 +346,11 @@ extension DyldCache {
}

extension DyldCache {
public func fileOffset(of address: UInt64) -> UInt64? {
guard let mapping = mappingInfo(for: address) else {
return nil
}
return address - mapping.address + mapping.fileOffset
}

public func address(of fileOffset: UInt64) -> UInt64? {
guard let mapping = mappingInfo(forFileOffset: fileOffset) else {
return nil
}
return fileOffset - mapping.fileOffset + mapping.address
}


public func mappingInfo(for address: UInt64) -> DyldCacheMappingInfo? {
guard let mappings = self.mappingInfos else { return nil }
for mapping in mappings {
if mapping.address <= address,
address < mapping.address + mapping.size {
return mapping
}
}
return nil
}

public func mappingInfo(
forFileOffset offset: UInt64
) -> DyldCacheMappingInfo? {
guard let mappings = self.mappingInfos else { return nil }
for mapping in mappings {
if mapping.fileOffset <= offset,
offset < mapping.fileOffset + mapping.size {
return mapping
}
}
return nil
}

public func mappingAndSlideInfo(
for address: UInt64
) -> DyldCacheMappingAndSlideInfo? {
guard let mappings = self.mappingAndSlideInfos else { return nil }
for mapping in mappings {
if mapping.address <= address,
address < mapping.address + mapping.size {
return mapping
}
}
return nil
}

public func mappingAndSlideInfo(
forFileOffset offset: UInt64
) -> DyldCacheMappingAndSlideInfo? {
guard let mappings = self.mappingAndSlideInfos else { return nil }
for mapping in mappings {
if mapping.fileOffset <= offset,
offset < mapping.fileOffset + mapping.size {
return mapping
}
}
return nil
}
}

extension DyldCache {
// https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/common/MetadataVisitor.cpp#L262
/// File offset after rebasing performed on the specified file offset
/// - Parameter offset: target file offset
/// - Returns: rebased file offset
///
/// [dyld Implementation](https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/common/MetadataVisitor.cpp#L262)
public func resolveRebase(at offset: UInt64) -> UInt64? {
guard let mappingInfos,
let unslidLoadAddress = mappingInfos.first?.address else {
Expand Down Expand Up @@ -489,7 +426,12 @@ extension DyldCache {
return runtimeOffset + onDiskDylibChainedPointerBaseAddress
}

// https://github.com/apple-oss-distributions/dyld/blob/a571176e8e00c47e95b95e3156820ebec0cbd5e6/common/MetadataVisitor.cpp#L424
/// File offset after optional rebasing performed on the specified file offset
/// - Parameter offset: target file offset
/// - Returns: optional rebased file offset
///
/// [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/a571176e8e00c47e95b95e3156820ebec0cbd5e6/common/MetadataVisitor.cpp#L424)
/// `resolveOptionalRebase` differs from `resolveRebase` in that rebasing may or may not actually take place.
public func resolveOptionalRebase(at offset: UInt64) -> UInt64? {
guard let mappingInfos,
let unslidLoadAddress = mappingInfos.first?.address else {
Expand Down
72 changes: 72 additions & 0 deletions Sources/MachOKit/DyldCacheLoaded+SubCaches.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// DyldCacheLoaded+SubCaches.swift
//
//
// Created by p-x9 on 2024/10/10
//
//

import Foundation

extension DyldCacheLoaded {
public struct SubCaches: Sequence {
public let basePointer: UnsafeRawPointer
public let numberOfSubCaches: Int
public let subCacheEntryType: DyldSubCacheEntryType

public func makeIterator() -> Iterator {
.init(
basePointer: basePointer,
numberOfSubCaches: numberOfSubCaches,
subCacheEntryType: subCacheEntryType
)
}
}
}

extension DyldCacheLoaded.SubCaches {
public struct Iterator: IteratorProtocol {
public typealias Element = DyldSubCacheEntry

public let basePointer: UnsafeRawPointer
public let numberOfSubCaches: Int
public let subCacheEntryType: DyldSubCacheEntryType

private var nextOffset: Int = 0
private var nextIndex: Int = 0

public init(
basePointer: UnsafeRawPointer,
numberOfSubCaches: Int,
subCacheEntryType: DyldSubCacheEntryType
) {
self.basePointer = basePointer
self.numberOfSubCaches = numberOfSubCaches
self.subCacheEntryType = subCacheEntryType
}

public mutating func next() -> DyldSubCacheEntry? {
guard nextIndex < numberOfSubCaches else {
return nil
}

defer {
nextIndex += 1
nextOffset += subCacheEntryType.layoutSize
}

switch subCacheEntryType {
case .general:
let ptr = UnsafeMutableRawPointer(mutating: basePointer)
.advanced(by: nextOffset)
.assumingMemoryBound(to: DyldSubCacheEntryGeneral.Layout.self)
return .general(.init(layout: ptr.pointee, index: nextIndex))
case .v1:
let ptr = UnsafeMutableRawPointer(mutating: basePointer)
.advanced(by: nextOffset)
.assumingMemoryBound(to: DyldSubCacheEntryV1.Layout.self)
return .v1(.init(layout: ptr.pointee, index: nextIndex))
}
}
}
}
Loading

0 comments on commit edb19a5

Please sign in to comment.