Skip to content
This repository was archived by the owner on Dec 28, 2025. It is now read-only.

Commit 6796ae2

Browse files
committed
update
1 parent 65ca894 commit 6796ae2

File tree

7 files changed

+214
-131
lines changed

7 files changed

+214
-131
lines changed

bin/dyldex

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,10 @@ def _getArguments():
5050
"""
5151

5252
parser = argparse.ArgumentParser()
53-
# TODO: update docs
5453
parser.add_argument(
5554
"dyld_path",
5655
type=pathlib.Path,
57-
help="A path to the target DYLD cache."
56+
help="A path to the target DYLD cache. If it is a split cache, use the path for the main cache (the one without a file type)." # noqa
5857
)
5958
parser.add_argument(
6059
"-e", "--extract",
@@ -161,9 +160,9 @@ def _extractImage(
161160

162161
extractionCtx = ExtractionContext(dyldCtx, machoCtx, statusBar, logger)
163162

164-
slide_info.processSlideInfo(extractionCtx)
163+
# slide_info.processSlideInfo(extractionCtx)
165164
linkedit_optimizer.optimizeLinkedit(extractionCtx)
166-
stub_fixer.fixStubs(extractionCtx)
165+
# stub_fixer.fixStubs(extractionCtx)
167166
objc_fixer.fixObjC(extractionCtx)
168167

169168
writeProcedures = macho_offset.optimizeOffsets(extractionCtx)
@@ -186,6 +185,7 @@ def _extractImage(
186185
file.close()
187186
pass
188187
pass
188+
pass
189189

190190

191191
def _filterImages(imagePaths: List[str], filterTerm: str):
@@ -270,3 +270,4 @@ def main():
270270

271271
if "__main__" == __name__:
272272
main()
273+
pass

bin/dyldex_all

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import multiprocessing
88
import pathlib
99
import signal
1010
import sys
11-
1211
import progressbar
1312

13+
from typing import (
14+
List,
15+
BinaryIO,
16+
Tuple
17+
)
18+
1419
from DyldExtractor.converter import (
1520
linkedit_optimizer,
1621
macho_offset,
@@ -21,6 +26,7 @@ from DyldExtractor.converter import (
2126
from DyldExtractor.dyld.dyld_context import DyldContext
2227
from DyldExtractor.extraction_context import ExtractionContext
2328
from DyldExtractor.macho.macho_context import MachOContext
29+
from DyldExtractor.file_context import FileContext
2430

2531
# check dependencies
2632
try:
@@ -82,6 +88,38 @@ def _workerInitializer():
8288
pass
8389

8490

91+
def _openSubCaches(
92+
mainCachePath: str,
93+
numSubCaches: int
94+
) -> Tuple[List[FileContext], List[BinaryIO]]:
95+
"""Create FileContext objects for each sub cache.
96+
97+
Assumes that each sub cache has the same base name as the
98+
main cache, and that the suffixes are preserved.
99+
100+
Also opens the symbols cache, and adds it to the end of
101+
the list.
102+
103+
Returns:
104+
A list of subcaches, and their file objects, which must be closed!
105+
"""
106+
subCaches = []
107+
subCachesFiles = []
108+
109+
subCacheSuffixes = [i for i in range(1, numSubCaches + 1)]
110+
subCacheSuffixes.append("symbols")
111+
for cacheSuffix in subCacheSuffixes:
112+
subCachePath = f"{mainCachePath}.{cacheSuffix}"
113+
cacheFileObject = open(subCachePath, mode="rb")
114+
cacheFileCtx = FileContext(cacheFileObject)
115+
116+
subCaches.append(cacheFileCtx)
117+
subCachesFiles.append(cacheFileObject)
118+
pass
119+
120+
return subCaches, subCachesFiles
121+
122+
85123
def _extractImage(
86124
dyldPath: pathlib.Path,
87125
outputDir: pathlib.Path,
@@ -113,43 +151,75 @@ def _extractImage(
113151

114152
# Process the image
115153
with open(dyldPath, "rb") as f:
116-
dyldFile = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
117-
dyldCtx = DyldContext(dyldFile)
118-
imageOffset = dyldCtx.convertAddr(dyldCtx.images[imageIndex].address)
119-
120-
machoFile = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY)
121-
machoCtx = MachOContext(machoFile, imageOffset)
122-
123-
extractionCtx = ExtractionContext(
124-
dyldCtx,
125-
machoCtx,
126-
_DummyProgressBar(),
127-
logger
128-
)
129-
154+
subCacheFiles: List[BinaryIO] = []
130155
try:
156+
dyldFileCtx = FileContext(f)
157+
dyldCtx = DyldContext(dyldFileCtx)
158+
159+
# add sub caches if there are any
160+
if dyldCtx.hasSubCaches():
161+
subCacheFileCtxs, subCacheFiles = _openSubCaches(
162+
dyldPath,
163+
dyldCtx.header.numSubCaches
164+
)
165+
dyldCtx.addSubCaches(subCacheFileCtxs)
166+
pass
167+
168+
machoOffset, context = dyldCtx.convertAddr(
169+
dyldCtx.images[imageIndex].address
170+
)
171+
machoCtx = MachOContext(
172+
context.fileCtx.makeCopy(copyMode=True),
173+
machoOffset
174+
)
175+
176+
# Add sub caches if necessary
177+
if dyldCtx.hasSubCaches():
178+
mappings = dyldCtx.mappings
179+
mainFileMap = next(
180+
(mapping[0] for mapping in mappings if mapping[1] == context)
181+
)
182+
machoCtx.addSubfiles(
183+
mainFileMap,
184+
((m, ctx.fileCtx.makeCopy(copyMode=True)) for m, ctx in mappings)
185+
)
186+
pass
187+
188+
extractionCtx = ExtractionContext(
189+
dyldCtx,
190+
machoCtx,
191+
_DummyProgressBar(),
192+
logger
193+
)
194+
131195
slide_info.processSlideInfo(extractionCtx)
132196
linkedit_optimizer.optimizeLinkedit(extractionCtx)
133197
stub_fixer.fixStubs(extractionCtx)
134198
objc_fixer.fixObjC(extractionCtx)
135-
macho_offset.optimizeOffsets(extractionCtx)
199+
200+
writeProcedures = macho_offset.optimizeOffsets(extractionCtx)
136201

137202
# write the file
138203
outputPath.parent.mkdir(parents=True, exist_ok=True)
139-
with open(outputPath, "wb+") as outFile:
140-
newMachoCtx = extractionCtx.machoCtx
141-
142-
# get the size of the new file
143-
linkeditSeg = newMachoCtx.segments[b"__LINKEDIT"].seg
144-
fileSize = linkeditSeg.fileoff + linkeditSeg.filesize
145-
146-
newMachoCtx.file.seek(0)
147-
outFile.write(newMachoCtx.file.read(fileSize))
204+
with open(outputPath, "wb") as outFile:
205+
for procedure in writeProcedures:
206+
outFile.seek(procedure.writeOffset)
207+
outFile.write(
208+
procedure.fileCtx.getBytes(procedure.readOffset, procedure.size)
209+
)
210+
pass
148211
pass
149212
pass
213+
150214
except Exception as e:
151215
logger.exception(e)
152216
pass
217+
218+
finally:
219+
for file in subCacheFiles:
220+
file.close()
221+
pass
222+
pass
153223
pass
154224

155225
handler.close()
@@ -186,11 +256,13 @@ def _main() -> None:
186256
# create a list of image paths
187257
imagePaths: list[str] = []
188258
with open(args.dyld_path, "rb") as f:
189-
dyldFile = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
190-
dyldCtx = DyldContext(dyldFile)
259+
dyldFileCtx = FileContext(f)
260+
dyldCtx = DyldContext(dyldFileCtx)
191261

192262
for image in dyldCtx.images:
193-
imagePath = dyldCtx.readString(image.pathFileOffset)[0:-1].decode("utf-8")
263+
imagePath = dyldCtx.fileCtx.readString(
264+
image.pathFileOffset
265+
)[0:-1].decode("utf-8")
194266
imagePaths.append(imagePath)
195267
pass
196268
pass

src/DyldExtractor/converter/slide_info.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ def _getMappingInfo(
276276

277277
# the version is encoded as the first uint32 field
278278
slideInfoOff = dyldCtx.header.slideInfoOffsetUnused
279-
slideInfoVer = dyldCtx.fileCtx.readFormat(slideInfoOff, "<I")[0]
279+
slideInfoVer = dyldCtx.fileCtx.readFormat("<I", slideInfoOff)[0]
280280

281281
if slideInfoVer not in _SlideInfoMap:
282282
logger.error("Unknown slide info version: " + slideInfoVer)

src/DyldExtractor/converter/stub_fixer.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ def getStubHelperData(self, address: int) -> int:
462462
if helperOff is None:
463463
return None
464464

465-
ldr, b, data = ctx.fileCtx.readFormat(helperOff, "<III")
465+
ldr, b, data = ctx.fileCtx.readFormat("<III", helperOff)
466466

467467
# verify
468468
if (
@@ -729,7 +729,7 @@ def _getStubOptimizedTarget(self, address: int) -> int:
729729
BR x16
730730
"""
731731

732-
stubOff, ctx = self._dyldCtx.convertAddr(address)
732+
stubOff, ctx = self._dyldCtx.convertAddr(address) or (None, None)
733733
if stubOff is None:
734734
return None
735735

@@ -762,7 +762,7 @@ def _getAuthStubNormalTarget(self, address: int) -> int:
762762
11 0a 1f d7 braa x16=>__auth_stubs::_CCRandomCopyBytes,x17
763763
"""
764764

765-
stubOff, ctx = self._dyldCtx.convertAddr(address)
765+
stubOff, ctx = self._dyldCtx.convertAddr(address) or (None, None)
766766
if stubOff is None:
767767
return None
768768

@@ -802,7 +802,7 @@ def _getAuthStubOptimizedTarget(self, address: int) -> int:
802802
1bfcb5d2c 20 00 20 d4 trap
803803
"""
804804

805-
stubOff, ctx = self._dyldCtx.convertAddr(address)
805+
stubOff, ctx = self._dyldCtx.convertAddr(address) or (None, None)
806806
if stubOff is None:
807807
return None
808808

@@ -835,7 +835,7 @@ def _getAuthStubResolverTarget(self, address: int) -> int:
835835
1f 0a 1f d6 braaz x16=>FUN_195bee070
836836
"""
837837

838-
stubOff, ctx = self._dyldCtx.convertAddr(address)
838+
stubOff, ctx = self._dyldCtx.convertAddr(address) or (None, None)
839839
if stubOff is None:
840840
return None
841841

tests/run_all_images.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def runForAllImages(
5151
break
5252

5353
imageOffset = dyldCtx.convertAddr(imageData.address)
54-
imagePath = dyldCtx.readString(imageData.pathFileOffset)[0:-1]
54+
imagePath = dyldCtx.fileCtx.readString(imageData.pathFileOffset)[0:-1]
5555
imagePath = imagePath.decode("utf-8")
5656
imageName = imagePath.split("/")[-1]
5757

0 commit comments

Comments
 (0)