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

Commit 689b1ce

Browse files
committed
fixed linkedit_optimizer
1 parent d6eefb6 commit 689b1ce

File tree

3 files changed

+88
-18
lines changed

3 files changed

+88
-18
lines changed

src/DyldExtractor/converter/linkedit_optimizer.py

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import struct
2+
from typing import Union, Type
23

34
from DyldExtractor.extraction_context import ExtractionContext
45

56
from DyldExtractor.dyld.dyld_structs import (
67
dyld_cache_local_symbols_info,
7-
dyld_cache_local_symbols_entry
8+
dyld_cache_local_symbols_entry,
9+
dyld_cache_local_symbols_entry2
810
)
11+
from DyldExtractor.file_context import FileContext
912

1013
from DyldExtractor.macho.macho_constants import *
1114
from DyldExtractor.macho.macho_structs import (
@@ -211,6 +214,39 @@ def startSymbolContext(self, newLinkedit: bytearray) -> None:
211214
self.newSymbolTableOffset = len(newLinkedit)
212215
pass
213216

217+
def getLocalSymsEntryStruct(
218+
self,
219+
symbolsCache: FileContext,
220+
symbolsInfo: dyld_cache_local_symbols_info
221+
) -> Union[
222+
Type[dyld_cache_local_symbols_entry],
223+
Type[dyld_cache_local_symbols_entry2]
224+
]:
225+
"""Get the correct struct for the local symbol entries.
226+
227+
If the struct version could not be found,
228+
return None.
229+
"""
230+
231+
# get the offset to the first image, and to the
232+
# second image. Assumes that they are next to each other.
233+
image1 = self.dyldCtx.convertAddr(self.dyldCtx.images[0].address)[0]
234+
image1 = struct.pack("<I", image1)
235+
image2 = self.dyldCtx.convertAddr(self.dyldCtx.images[1].address)[0]
236+
image2 = struct.pack("<I", image2)
237+
238+
entriesOff = symbolsInfo._fileOff_ + symbolsInfo.entriesOffset
239+
image1Off = symbolsCache.file.find(image1, entriesOff)
240+
image2Off = symbolsCache.file.find(image2, entriesOff)
241+
242+
structSize = image2Off - image1Off
243+
if structSize == dyld_cache_local_symbols_entry.SIZE:
244+
return dyld_cache_local_symbols_entry
245+
elif structSize == dyld_cache_local_symbols_entry2.SIZE:
246+
return dyld_cache_local_symbols_entry2
247+
else:
248+
return None
249+
214250
def copyLocalSymbols(self, newLinkedit: bytearray) -> None:
215251
self.statusBar.update(status="Copy Local Symbols")
216252

@@ -220,13 +256,26 @@ def copyLocalSymbols(self, newLinkedit: bytearray) -> None:
220256
symbolsCache.header.localSymbolsOffset
221257
)
222258

259+
entryStruct = self.getLocalSymsEntryStruct(
260+
symbolsCache.fileCtx,
261+
localSymbolsInfo
262+
)
263+
if not entryStruct:
264+
self.logger.error("Unable to get local symbol entries structure.")
265+
return
266+
267+
dylibOffset = (
268+
self.machoCtx.segments[b"__TEXT"].seg.vmaddr
269+
- self.dyldCtx.header.sharedRegionStart
270+
)
271+
223272
localSymbolsEntriesInfo = None
224273
for i in range(localSymbolsInfo.entriesCount):
225-
entryOff = (i * dyld_cache_local_symbols_entry.SIZE)
274+
entryOff = (i * entryStruct.SIZE)
226275
entryOff += localSymbolsInfo._fileOff_ + localSymbolsInfo.entriesOffset
227276

228-
entry = dyld_cache_local_symbols_entry(symbolsCache.fileCtx.file, entryOff)
229-
if entry.dylibOffset == self.machoCtx.fileOffset:
277+
entry = entryStruct(symbolsCache.fileCtx.file, entryOff)
278+
if entry.dylibOffset == dylibOffset:
230279
localSymbolsEntriesInfo = entry
231280
break
232281

@@ -251,7 +300,7 @@ def copyLocalSymbols(self, newLinkedit: bytearray) -> None:
251300
symbolStrOff = localSymbolsInfo._fileOff_ + localSymbolsInfo.stringsOffset
252301

253302
for offset in range(entriesStart, entriesEnd, nlist_64.SIZE):
254-
symbolEnt = nlist_64(self.dyldCtx.file, offset)
303+
symbolEnt = nlist_64(symbolsCache.fileCtx.file, offset)
255304
name = symbolsCache.fileCtx.readString(symbolStrOff + symbolEnt.n_strx)
256305

257306
# copy data
@@ -282,10 +331,10 @@ def copyExportedSymbols(self, newLinkedit: bytearray) -> None:
282331

283332
for entryIndex in range(entriesStart, entriesEnd):
284333
entryOff = self.symTabCmd.symoff + (entryIndex * nlist_64.SIZE)
285-
entry = nlist_64(self.dyldCtx.file, entryOff)
334+
entry = nlist_64(self.linkeditFile.file, entryOff)
286335

287336
nameOff = symbolStrOff + entry.n_strx
288-
name = self.dyldCtx.readString(nameOff)
337+
name = self.linkeditFile.readString(nameOff)
289338

290339
# update variables and copy
291340
self.oldToNewSymbolIndexes[entryIndex] = self.symbolCtx.symbolsSize
@@ -317,10 +366,10 @@ def copyImportedSymbols(self, newLinkedit: bytearray) -> None:
317366

318367
for entryIndex in range(entriesStart, entriesEnd):
319368
entryOff = self.symTabCmd.symoff + (entryIndex * nlist_64.SIZE)
320-
entry = nlist_64(self.dyldCtx.file, entryOff)
369+
entry = nlist_64(self.linkeditFile.file, entryOff)
321370

322371
nameOff = symbolStrOff + entry.n_strx
323-
name = self.dyldCtx.readString(nameOff)
372+
name = self.linkeditFile.readString(nameOff)
324373

325374
# update variables and copy
326375
self.oldToNewSymbolIndexes[entryIndex] = self.symbolCtx.symbolsSize
@@ -357,7 +406,7 @@ def addRedactedSymbol(self, newLinkedit: bytearray) -> None:
357406
indirectStart = self.dynSymTabCmd.indirectsymoff
358407
indirectEnd = indirectStart + (self.dynSymTabCmd.nindirectsyms * 4)
359408
for offset in range(indirectStart, indirectEnd, 4):
360-
symbolIndex = self.dyldCtx.getBytes(offset, 4)
409+
symbolIndex = self.linkeditFile.getBytes(offset, 4)
361410
if symbolIndex == b"\x00\x00\x00\x00":
362411
self.redactedSymbolCount += 1
363412

@@ -385,7 +434,10 @@ def copyFunctionStarts(self, newLinkedit: bytearray) -> None:
385434
self.newFunctionStartsOffset = len(newLinkedit)
386435

387436
size = self.functionStartsCmd.datasize
388-
functionStarts = self.machoCtx.getBytes(self.functionStartsCmd.dataoff, size)
437+
functionStarts = self.linkeditFile.getBytes(
438+
self.functionStartsCmd.dataoff,
439+
size
440+
)
389441
newLinkedit.extend(functionStarts)
390442

391443
self.statusBar.update()
@@ -400,7 +452,7 @@ def copyDataInCode(self, newLinkedit: bytearray) -> None:
400452
self.newDataInCodeOffset = len(newLinkedit)
401453

402454
size = self.dataInCodeCmd.datasize
403-
dataInCode = self.machoCtx.getBytes(self.dataInCodeCmd.dataoff, size)
455+
dataInCode = self.linkeditFile.getBytes(self.dataInCodeCmd.dataoff, size)
404456
newLinkedit.extend(dataInCode)
405457

406458
self.statusBar.update()
@@ -421,7 +473,7 @@ def copyIndirectSymbolTable(self, newLinkedit: bytearray) -> None:
421473

422474
for offset in range(self.dynSymTabCmd.indirectsymoff, entriesEnd, 4):
423475
# Each entry is a 32bit index into the symbol table
424-
symbol = self.dyldCtx.getBytes(offset, 4)
476+
symbol = self.linkeditFile.getBytes(offset, 4)
425477
symbolIndex = struct.unpack("<I", symbol)[0]
426478

427479
if (
@@ -565,10 +617,11 @@ def optimizeLinkedit(extractionCtx: ExtractionContext) -> None:
565617
newLinkedit.extend(b"\x00" * 4)
566618

567619
# Set the new linkedit in the same location
568-
machoCtx = extractionCtx.machoCtx
569-
newLinkeditOff = machoCtx.segments[b"__LINKEDIT"].seg.fileoff
570-
machoCtx.file.seek(newLinkeditOff)
571-
machoCtx.file.write(newLinkedit)
620+
newLinkeditOff = extractionCtx.machoCtx.segments[b"__LINKEDIT"].seg.fileoff
621+
linkeditFile = extractionCtx.machoCtx.fileForAddr(
622+
extractionCtx.machoCtx.segments[b"__LINKEDIT"].seg.vmaddr
623+
)
624+
linkeditFile.writeBytes(newLinkeditOff, newLinkedit)
572625

573626
optimizer.updateLoadCommands(newLinkedit, newLinkeditOff)
574627

src/DyldExtractor/dyld/dyld_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def getSymbolsCache(self) -> "DyldContext":
122122
return self
123123

124124
for cache in self._subCaches:
125-
if self.header.symbolSubCacheUUID == cache.header.uuid:
125+
if bytes(self.header.symbolSubCacheUUID) == bytes(cache.header.uuid):
126126
return cache
127127
pass
128128

src/DyldExtractor/dyld/dyld_structs.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,23 @@ class dyld_cache_local_symbols_entry(Structure):
365365
]
366366

367367

368+
class dyld_cache_local_symbols_entry2(Structure):
369+
370+
SIZE = 16
371+
372+
dylibOffset: int # offset in cache file of start of dylib
373+
unknown: int # Unknown field, currently 0
374+
nlistStartIndex: int # start index of locals for this dylib
375+
nlistCount: int # number of local symbols for this dylib
376+
377+
_fields_ = [
378+
("dylibOffset", c_uint32),
379+
("unknown", c_uint32),
380+
("nlistStartIndex", c_uint32),
381+
("nlistCount", c_uint32),
382+
]
383+
384+
368385
class dyld_cache_patch_info(Structure):
369386

370387
patchTableArrayAddr: int # (unslid) address of array for dyld_cache_image_patches for each image

0 commit comments

Comments
 (0)