Skip to content

Commit edb19a5

Browse files
authored
Merge pull request #124 from p-x9/feature/on-memory-dyld-cache
Support dyld cache on memory
2 parents 351aaae + 9611e40 commit edb19a5

24 files changed

+1930
-231
lines changed

README.md

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ In addition to file reading, parsing of images in memory by `_dyld_get_image_hea
2525

2626
### Load from memory
2727

28-
For reading from memory, use the `MachO` structure.
28+
For reading from memory, use the `MachOImage` structure.
2929

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

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

6666
### Main properties and methods
6767

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

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

73-
7473
### Dyld Cache
7574

76-
loading of `dyld_shared_cache` is also supported.
75+
Loading of `dyld_shared_cache` is also supported.
76+
77+
The available methods are defined in the following file as the `DyldCacheRepresentable` protocol.
78+
79+
[DyldCacheRepresentable](./Sources/MachOKit/Protocol/DyldCacheRepresentable.swift)
80+
81+
#### Dyld Cache (File)
7782

7883
```swift
79-
let path = "/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_x86_64h"
84+
let path = "/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e"
8085
let url = URL(fileURLWithPath: path)
8186

8287
let cache = try! DyldCache(url: url)
8388
```
8489

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

@@ -102,6 +107,38 @@ for machO in machOs {
102107
// ...
103108
```
104109

110+
#### Dyld Cache (on memory)
111+
112+
On the Apple platform, the dyld cache is deployed in memory.
113+
114+
```swift
115+
var size = 0
116+
guard let ptr = _dyld_get_shared_cache_range(&size) else {
117+
return
118+
}
119+
let cache = try! DyldCacheLoaded(ptr: ptr)
120+
```
121+
122+
It is also possible to extract machO information contained in `dyld_shared_cache`.
123+
The machO extracted is of type `MachOImage`.
124+
As with reading from a single MachO image, various analyses are possible.
125+
126+
```swift
127+
let machOs = cache.machOImages()
128+
for machO in machOs {
129+
print(
130+
String(Int(bitPattern: machO.ptr), radix: 16),
131+
machO.path!,
132+
machO.header.ncmds
133+
)
134+
}
135+
136+
// 193438000 /usr/lib/libobjc.A.dylib 24
137+
// 193489000 /usr/lib/dyld 15
138+
// 193513000 /usr/lib/system/libsystem_blocks.dylib 24
139+
// ...
140+
```
141+
105142
### Example Codes
106143

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

119-
#### Dyld Cache
156+
#### Dyld Cache (file)
120157

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

161+
#### Dyld Cache (on memory)
162+
163+
The following file contains sample code.
164+
[DyldCacheLoadedPrintTests](./Tests/MachOKitTests/DyldCacheLoadedPrintTests.swift)
165+
124166
## Related Projects
125167

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

177+
### Other binary type
178+
- [ELFKit](https://github.com/p-x9/ELFKit)
179+
Elf format
180+
135181
## License
136182

137183
MachOKit is released under the MIT License. See [LICENSE](./LICENSE)

Sources/MachOKit/DyldCache.swift

Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import Foundation
1010

11-
public class DyldCache {
11+
public class DyldCache: DyldCacheRepresentable {
1212
/// URL of loaded dyld cache file
1313
public let url: URL
1414
let fileHandle: FileHandle
@@ -346,74 +346,11 @@ extension DyldCache {
346346
}
347347

348348
extension DyldCache {
349-
public func fileOffset(of address: UInt64) -> UInt64? {
350-
guard let mapping = mappingInfo(for: address) else {
351-
return nil
352-
}
353-
return address - mapping.address + mapping.fileOffset
354-
}
355-
356-
public func address(of fileOffset: UInt64) -> UInt64? {
357-
guard let mapping = mappingInfo(forFileOffset: fileOffset) else {
358-
return nil
359-
}
360-
return fileOffset - mapping.fileOffset + mapping.address
361-
}
362-
363-
364-
public func mappingInfo(for address: UInt64) -> DyldCacheMappingInfo? {
365-
guard let mappings = self.mappingInfos else { return nil }
366-
for mapping in mappings {
367-
if mapping.address <= address,
368-
address < mapping.address + mapping.size {
369-
return mapping
370-
}
371-
}
372-
return nil
373-
}
374-
375-
public func mappingInfo(
376-
forFileOffset offset: UInt64
377-
) -> DyldCacheMappingInfo? {
378-
guard let mappings = self.mappingInfos else { return nil }
379-
for mapping in mappings {
380-
if mapping.fileOffset <= offset,
381-
offset < mapping.fileOffset + mapping.size {
382-
return mapping
383-
}
384-
}
385-
return nil
386-
}
387-
388-
public func mappingAndSlideInfo(
389-
for address: UInt64
390-
) -> DyldCacheMappingAndSlideInfo? {
391-
guard let mappings = self.mappingAndSlideInfos else { return nil }
392-
for mapping in mappings {
393-
if mapping.address <= address,
394-
address < mapping.address + mapping.size {
395-
return mapping
396-
}
397-
}
398-
return nil
399-
}
400-
401-
public func mappingAndSlideInfo(
402-
forFileOffset offset: UInt64
403-
) -> DyldCacheMappingAndSlideInfo? {
404-
guard let mappings = self.mappingAndSlideInfos else { return nil }
405-
for mapping in mappings {
406-
if mapping.fileOffset <= offset,
407-
offset < mapping.fileOffset + mapping.size {
408-
return mapping
409-
}
410-
}
411-
return nil
412-
}
413-
}
414-
415-
extension DyldCache {
416-
// https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/common/MetadataVisitor.cpp#L262
349+
/// File offset after rebasing performed on the specified file offset
350+
/// - Parameter offset: target file offset
351+
/// - Returns: rebased file offset
352+
///
353+
/// [dyld Implementation](https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/common/MetadataVisitor.cpp#L262)
417354
public func resolveRebase(at offset: UInt64) -> UInt64? {
418355
guard let mappingInfos,
419356
let unslidLoadAddress = mappingInfos.first?.address else {
@@ -489,7 +426,12 @@ extension DyldCache {
489426
return runtimeOffset + onDiskDylibChainedPointerBaseAddress
490427
}
491428

492-
// https://github.com/apple-oss-distributions/dyld/blob/a571176e8e00c47e95b95e3156820ebec0cbd5e6/common/MetadataVisitor.cpp#L424
429+
/// File offset after optional rebasing performed on the specified file offset
430+
/// - Parameter offset: target file offset
431+
/// - Returns: optional rebased file offset
432+
///
433+
/// [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/a571176e8e00c47e95b95e3156820ebec0cbd5e6/common/MetadataVisitor.cpp#L424)
434+
/// `resolveOptionalRebase` differs from `resolveRebase` in that rebasing may or may not actually take place.
493435
public func resolveOptionalRebase(at offset: UInt64) -> UInt64? {
494436
guard let mappingInfos,
495437
let unslidLoadAddress = mappingInfos.first?.address else {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// DyldCacheLoaded+SubCaches.swift
3+
//
4+
//
5+
// Created by p-x9 on 2024/10/10
6+
//
7+
//
8+
9+
import Foundation
10+
11+
extension DyldCacheLoaded {
12+
public struct SubCaches: Sequence {
13+
public let basePointer: UnsafeRawPointer
14+
public let numberOfSubCaches: Int
15+
public let subCacheEntryType: DyldSubCacheEntryType
16+
17+
public func makeIterator() -> Iterator {
18+
.init(
19+
basePointer: basePointer,
20+
numberOfSubCaches: numberOfSubCaches,
21+
subCacheEntryType: subCacheEntryType
22+
)
23+
}
24+
}
25+
}
26+
27+
extension DyldCacheLoaded.SubCaches {
28+
public struct Iterator: IteratorProtocol {
29+
public typealias Element = DyldSubCacheEntry
30+
31+
public let basePointer: UnsafeRawPointer
32+
public let numberOfSubCaches: Int
33+
public let subCacheEntryType: DyldSubCacheEntryType
34+
35+
private var nextOffset: Int = 0
36+
private var nextIndex: Int = 0
37+
38+
public init(
39+
basePointer: UnsafeRawPointer,
40+
numberOfSubCaches: Int,
41+
subCacheEntryType: DyldSubCacheEntryType
42+
) {
43+
self.basePointer = basePointer
44+
self.numberOfSubCaches = numberOfSubCaches
45+
self.subCacheEntryType = subCacheEntryType
46+
}
47+
48+
public mutating func next() -> DyldSubCacheEntry? {
49+
guard nextIndex < numberOfSubCaches else {
50+
return nil
51+
}
52+
53+
defer {
54+
nextIndex += 1
55+
nextOffset += subCacheEntryType.layoutSize
56+
}
57+
58+
switch subCacheEntryType {
59+
case .general:
60+
let ptr = UnsafeMutableRawPointer(mutating: basePointer)
61+
.advanced(by: nextOffset)
62+
.assumingMemoryBound(to: DyldSubCacheEntryGeneral.Layout.self)
63+
return .general(.init(layout: ptr.pointee, index: nextIndex))
64+
case .v1:
65+
let ptr = UnsafeMutableRawPointer(mutating: basePointer)
66+
.advanced(by: nextOffset)
67+
.assumingMemoryBound(to: DyldSubCacheEntryV1.Layout.self)
68+
return .v1(.init(layout: ptr.pointee, index: nextIndex))
69+
}
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)