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

Commit cdefa19

Browse files
committed
Fixed stub_fixer.
1 parent 7b3d3e9 commit cdefa19

File tree

1 file changed

+63
-44
lines changed

1 file changed

+63
-44
lines changed

src/DyldExtractor/converter/stub_fixer.py

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def _enumerateExports(self) -> None:
9999
# get an initial list of dependencies
100100
if dylibs := self._machoCtx.getLoadCommand(DEP_LCS, multiple=True):
101101
for dylib in dylibs:
102-
if depInfo := self._getDepInfo(dylib):
102+
if depInfo := self._getDepInfo(dylib, self._machoCtx):
103103
depsQueue.append(depInfo)
104104
pass
105105

@@ -123,8 +123,8 @@ def _enumerateExports(self) -> None:
123123
if dylibs := depInfo.context.getLoadCommand(DEP_LCS, multiple=True):
124124
for dylib in dylibs:
125125
if dylib.cmd == LoadCommands.LC_REEXPORT_DYLIB:
126-
if depInfo := self._getDepInfo(dylib):
127-
depsQueue.append(depInfo)
126+
if info := self._getDepInfo(dylib, depInfo.context):
127+
depsQueue.append(info)
128128
pass
129129

130130
# check for any ReExport exports
@@ -137,8 +137,8 @@ def _enumerateExports(self) -> None:
137137

138138
for ordinal in reExportOrdinals:
139139
dylib = dylibs[ordinal - 1]
140-
if depInfo := self._getDepInfo(dylib):
141-
depsQueue.append(depInfo)
140+
if info := self._getDepInfo(dylib, depInfo.context):
141+
depsQueue.append(info)
142142
pass
143143
pass
144144

@@ -161,12 +161,12 @@ def _enumerateExports(self) -> None:
161161
self._logger.warning(f"No root export for ReExport with symbol {name}")
162162
pass
163163

164-
def _getDepInfo(self, dylib: dylib_command) -> _DependencyInfo:
164+
def _getDepInfo(self, dylib: dylib_command, context: MachOContext) -> _DependencyInfo:
165165
"""Given a dylib command, get dependency info.
166166
"""
167167

168168
dylibPathOff = dylib._fileOff_ + dylib.dylib.name.offset
169-
dylibPath = self._machoCtx.fileCtx.readString(dylibPathOff)
169+
dylibPath = context.fileCtx.readString(dylibPathOff)
170170
if dylibPath not in self._images:
171171
self._logger.warning(f"Unable to find dependency: {dylibPath}")
172172
return None
@@ -526,7 +526,7 @@ def getResolverData(self, address: int) -> Tuple[int, int]:
526526
return None
527527

528528
# test stp and mov
529-
stp, mov = ctx.fileCtx.readFormat(stubOff, "<II")
529+
stp, mov = ctx.fileCtx.readFormat("<II", stubOff)
530530
if (
531531
(stp & 0x7FC00000) != 0x29800000
532532
or (mov & 0x7F3FFC00) != 0x11000000
@@ -563,16 +563,16 @@ def getResolverData(self, address: int) -> Tuple[int, int]:
563563
return None
564564

565565
# Test if there is a stp before the bl and a ldp before the braaz
566-
adrp = ctx.fileCtx.readFormat(blInstrOff + 4, "<I")[0]
567-
ldp = ctx.fileCtx.readFormat(branchInstrOff - 4, "<I")[0]
566+
adrp = ctx.fileCtx.readFormat("<I", blInstrOff + 4)[0]
567+
ldp = ctx.fileCtx.readFormat("<I", branchInstrOff - 4)[0]
568568
if (
569569
(adrp & 0x9F00001F) != 0x90000010
570570
or (ldp & 0x7FC00000) != 0x28C00000
571571
):
572572
return None
573573

574574
# Hopefully it's a resolver...
575-
imm = (ctx.fileCtx.readFormat(blInstrOff, "<I")[0] & 0x3FFFFFF) << 2
575+
imm = (ctx.fileCtx.readFormat("<I", blInstrOff)[0] & 0x3FFFFFF) << 2
576576
imm = self.signExtend(imm, 28)
577577
blResult = address + (blInstrOff - stubOff) + imm
578578

@@ -619,7 +619,7 @@ def _getStubNormalLdrAddr(self, address: int) -> int:
619619
if stubOff is None:
620620
return None
621621

622-
adrp, ldr, br = ctx.fileCtx.readFormat(stubOff, "<III")
622+
adrp, ldr, br = ctx.fileCtx.readFormat("<III", stubOff)
623623

624624
# verify
625625
if (
@@ -657,8 +657,8 @@ def _getAuthStubNormalLdrAddr(self, address: int) -> int:
657657
return None
658658

659659
adrp, add, ldr, braa = ctx.fileCtx.readFormat(
660-
stubOff,
661-
"<IIII"
660+
"<IIII",
661+
stubOff
662662
)
663663

664664
# verify
@@ -696,7 +696,7 @@ def _getStubNormalTarget(self, address: int) -> int:
696696
if stubOff is None:
697697
return None
698698

699-
adrp, ldr, br = ctx.fileCtx.readFormat(stubOff, "<III")
699+
adrp, ldr, br = ctx.fileCtx.readFormat("<III", stubOff)
700700

701701
# verify
702702
if (
@@ -729,7 +729,7 @@ def _getStubOptimizedTarget(self, address: int) -> int:
729729
if stubOff is None:
730730
return None
731731

732-
adrp, add, br = ctx.fileCtx.readFormat(stubOff, "<III")
732+
adrp, add, br = ctx.fileCtx.readFormat("<III", stubOff)
733733

734734
# verify
735735
if (
@@ -762,7 +762,7 @@ def _getAuthStubNormalTarget(self, address: int) -> int:
762762
if stubOff is None:
763763
return None
764764

765-
adrp, add, ldr, braa = ctx.fileCtx.readFormat(stubOff, "<IIII")
765+
adrp, add, ldr, braa = ctx.fileCtx.readFormat("<IIII", stubOff)
766766

767767
# verify
768768
if (
@@ -802,7 +802,7 @@ def _getAuthStubOptimizedTarget(self, address: int) -> int:
802802
if stubOff is None:
803803
return None
804804

805-
adrp, add, br, trap = ctx.fileCtx.readFormat(stubOff, "<IIII")
805+
adrp, add, br, trap = ctx.fileCtx.readFormat("<IIII", stubOff)
806806

807807
# verify
808808
if (
@@ -835,7 +835,7 @@ def _getAuthStubResolverTarget(self, address: int) -> int:
835835
if stubOff is None:
836836
return None
837837

838-
adrp, ldr, braaz = ctx.fileCtx.readFormat(stubOff, "<III")
838+
adrp, ldr, braaz = ctx.fileCtx.readFormat("<III", stubOff)
839839

840840
# verify
841841
if (
@@ -1117,8 +1117,8 @@ def _addToMap(ptrSymbol: bytes, ptrAddr: int, section: section_64):
11171117

11181118
# Try to symbolize though indirect symbol entries
11191119
symbolIndex = linkeditFile.readFormat(
1120-
self._dysymtab.indirectsymoff + ((sect.reserved1 + i) * 4),
1121-
"<I"
1120+
"<I",
1121+
self._dysymtab.indirectsymoff + ((sect.reserved1 + i) * 4)
11221122
)[0]
11231123
if (
11241124
symbolIndex != 0
@@ -1127,7 +1127,7 @@ def _addToMap(ptrSymbol: bytes, ptrAddr: int, section: section_64):
11271127
and symbolIndex != (INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL)
11281128
):
11291129
symbolEntry = nlist_64(
1130-
linkeditFile,
1130+
linkeditFile.file,
11311131
self._symtab.symoff + (symbolIndex * nlist_64.SIZE)
11321132
)
11331133
symbol = linkeditFile.readString(
@@ -1254,6 +1254,12 @@ def _addToMap(stubName: bytes, stubAddr: int):
12541254
self._machoCtx.segments[b"__LINKEDIT"].seg.vmaddr
12551255
)
12561256

1257+
textFile = self._machoCtx.fileForAddr(
1258+
self._machoCtx.segments[b"__TEXT"].seg.vmaddr
1259+
)
1260+
1261+
symbolPtrFile = None
1262+
12571263
for segment in self._machoCtx.segmentsI:
12581264
for sect in segment.sectsI:
12591265
if sect.flags & SECTION_TYPE == S_SYMBOL_STUBS:
@@ -1267,8 +1273,8 @@ def _addToMap(stubName: bytes, stubAddr: int):
12671273

12681274
# Try to symbolize though indirect symbol entries
12691275
symbolIndex = linkeditFile.readFormat(
1270-
self._dysymtab.indirectsymoff + ((sect.reserved1 + i) * 4),
1271-
"<I"
1276+
"<I",
1277+
self._dysymtab.indirectsymoff + ((sect.reserved1 + i) * 4)
12721278
)[0]
12731279

12741280
if (
@@ -1339,24 +1345,32 @@ def _addToMap(stubName: bytes, stubAddr: int):
13391345
elif stubFormat == _StubFormat.StubOptimized:
13401346
# only need to relink stub
13411347
newStub = self._arm64Utils.generateStubNormal(stubAddr, symPtrAddr)
1342-
stubOff, ctx = self._dyldCtx.convertAddr(stubAddr)
1343-
ctx.fileCtx.writeBytes(stubOff, newStub)
1348+
stubOff = self._dyldCtx.convertAddr(stubAddr)[0]
1349+
textFile.writeBytes(stubOff, newStub)
13441350
continue
13451351

13461352
elif stubFormat == _StubFormat.AuthStubNormal:
13471353
# only need to relink symbol pointer
1348-
symPtrOff, ctx = self._dyldCtx.convertAddr(symPtrAddr)
1349-
ctx.fileCtx.writeBytes(symPtrOff, struct.pack("<Q", stubAddr))
1354+
symPtrOff = self._dyldCtx.convertAddr(symPtrAddr)[0]
1355+
1356+
if symbolPtrFile:
1357+
symbolPtrFile = self._machoCtx.fileForAddr(symPtrAddr)
1358+
pass
1359+
1360+
symbolPtrFile.writeBytes(symPtrOff, struct.pack("<Q", stubAddr))
13501361
continue
13511362

13521363
elif stubFormat == _StubFormat.AuthStubOptimized:
13531364
# need to relink both the stub and the symbol pointer
1354-
symPtrOff, ctx = self._dyldCtx.convertAddr(symPtrAddr)
1355-
ctx.fileCtx.writeBytes(symPtrOff, struct.pack("<Q", stubAddr))
1365+
symPtrOff = self._dyldCtx.convertAddr(symPtrAddr)[0]
1366+
if symbolPtrFile:
1367+
symbolPtrFile = self._machoCtx.fileForAddr(symPtrAddr)
1368+
pass
1369+
symbolPtrFile.writeBytes(symPtrOff, struct.pack("<Q", stubAddr))
13561370

13571371
newStub = self._arm64Utils.generateAuthStubNormal(stubAddr, symPtrAddr)
13581372
stubOff, ctx = self._dyldCtx.convertAddr(stubAddr)
1359-
ctx.fileCtx.writeBytes(stubOff, newStub)
1373+
textFile.writeBytes(stubOff, newStub)
13601374
continue
13611375

13621376
elif stubFormat == _StubFormat.AuthStubResolver:
@@ -1395,14 +1409,15 @@ def _fixCallsites(self, stubMap: Dict[bytes, Tuple[int]]) -> None:
13951409
# Section offsets by section_64.offset are sometimes
13961410
# inaccurate, like in libcrypto.dylib
13971411
textOff = self._dyldCtx.convertAddr(textAddr)[0]
1412+
textFile = self._machoCtx.fileForAddr(textAddr)
13981413

13991414
for sectOff in range(0, textSect.size, 4):
14001415
# We are only looking for bl and b instructions only.
14011416
# Theses instructions are only identical by their top
14021417
# most byte. By only looking at the top byte, we can
14031418
# save a lot of time.
14041419
instrOff = textOff + sectOff
1405-
instrTop = self._machoCtx.file[instrOff + 3] & 0xFC
1420+
instrTop = textFile.file[instrOff + 3] & 0xFC
14061421

14071422
if (
14081423
instrTop != 0x94 # bl
@@ -1411,7 +1426,7 @@ def _fixCallsites(self, stubMap: Dict[bytes, Tuple[int]]) -> None:
14111426
continue
14121427

14131428
# get the target of the branch
1414-
brInstr = self._machoCtx.readFormat(instrOff, "<I")[0]
1429+
brInstr = textFile.readFormat("<I", instrOff)[0]
14151430
imm26 = brInstr & 0x3FFFFFF
14161431
brOff = self._arm64Utils.signExtend(imm26 << 2, 28)
14171432

@@ -1428,7 +1443,7 @@ def _fixCallsites(self, stubMap: Dict[bytes, Tuple[int]]) -> None:
14281443
# Sometimes there are bytes of data in the text section
14291444
# that match the bl and b filter, these seem to follow a
14301445
# BR or other branch, skip these.
1431-
lastInstTop = self._machoCtx.file[instrOff + 3] & 0xFC
1446+
lastInstTop = textFile.file[instrOff + 3] & 0xFC
14321447
if (
14331448
lastInstTop == 0x94 # bl
14341449
or lastInstTop == 0x14 # b
@@ -1442,7 +1457,7 @@ def _fixCallsites(self, stubMap: Dict[bytes, Tuple[int]]) -> None:
14421457
stubSymbol = next((sym for sym in funcSymbols if sym in stubMap), None)
14431458
if not stubSymbol:
14441459
# Same as above
1445-
lastInstTop = self._machoCtx.file[instrOff + 3] & 0xFC
1460+
lastInstTop = textFile.file[instrOff + 3] & 0xFC
14461461
if (
14471462
lastInstTop == 0x94 # bl
14481463
or lastInstTop == 0x14 # b
@@ -1457,7 +1472,7 @@ def _fixCallsites(self, stubMap: Dict[bytes, Tuple[int]]) -> None:
14571472
stubAddr = stubMap[stubSymbol][0]
14581473
imm26 = (stubAddr - brAddr) >> 2
14591474
brInstr = (brInstr & 0xFC000000) | imm26
1460-
struct.pack_into("<I", self._machoCtx.file, instrOff, brInstr)
1475+
struct.pack_into("<I", textFile.file, instrOff, brInstr)
14611476

14621477
self._statusBar.update(status="Fixing Callsites")
14631478
pass
@@ -1481,6 +1496,10 @@ def _fixIndirectSymbols(
14811496

14821497
self._statusBar.update(status="Fixing Indirect Symbols")
14831498

1499+
linkeditFile = self._machoCtx.fileForAddr(
1500+
self._machoCtx.segments[b"__LINKEDIT"].seg.vmaddr
1501+
)
1502+
14841503
currentSymbolIndex = self._dysymtab.iundefsym + self._dysymtab.nundefsym
14851504
currentStringIndex = self._symtab.strsize
14861505

@@ -1498,7 +1517,7 @@ def _fixIndirectSymbols(
14981517
self._statusBar.update()
14991518

15001519
entryOffset = self._dysymtab.indirectsymoff + (i * 4)
1501-
entry = self._machoCtx.readFormat(entryOffset, "<I")[0]
1520+
entry = linkeditFile.readFormat("<I", entryOffset)[0]
15021521

15031522
if entry != 0:
15041523
continue
@@ -1521,7 +1540,7 @@ def _fixIndirectSymbols(
15211540
currentStringIndex += len(stubSymbol)
15221541

15231542
# update the indirect entry and add it
1524-
self._machoCtx.writeBytes(
1543+
linkeditFile.writeBytes(
15251544
entryOffset,
15261545
struct.pack("<I", currentSymbolIndex)
15271546
)
@@ -1542,7 +1561,7 @@ def _fixIndirectSymbols(
15421561
self._statusBar.update()
15431562

15441563
entryOffset = self._dysymtab.indirectsymoff + (i * 4)
1545-
entry = self._machoCtx.readFormat(entryOffset, "<I")[0]
1564+
entry = linkeditFile.readFormat("<I", entryOffset)[0]
15461565

15471566
if entry != 0:
15481567
continue
@@ -1565,7 +1584,7 @@ def _fixIndirectSymbols(
15651584
currentStringIndex += len(ptrSymbol)
15661585

15671586
# update the indirect entry and add it
1568-
self._machoCtx.writeBytes(
1587+
linkeditFile.writeBytes(
15691588
entryOffset,
15701589
struct.pack("<I", currentSymbolIndex)
15711590
)
@@ -1586,7 +1605,7 @@ def _fixIndirectSymbols(
15861605
self._statusBar.update()
15871606

15881607
entryOffset = self._dysymtab.indirectsymoff + (i * 4)
1589-
entry = self._machoCtx.readFormat(entryOffset, "<I")[0]
1608+
entry = linkeditFile.readFormat("<I", entryOffset)[0]
15901609

15911610
if entry != 0:
15921611
continue
@@ -1602,7 +1621,7 @@ def _fixIndirectSymbols(
16021621
self._statusBar.update()
16031622

16041623
entryOffset = self._dysymtab.indirectsymoff + (i * 4)
1605-
entry = self._machoCtx.readFormat(entryOffset, "<I")[0]
1624+
entry = linkeditFile.readFormat("<I", entryOffset)[0]
16061625

16071626
if entry != 0:
16081627
continue
@@ -1626,11 +1645,11 @@ def _fixIndirectSymbols(
16261645
self._statusBar.update()
16271646

16281647
# add the new data and update the load commands
1629-
self._machoCtx.writeBytes(
1648+
linkeditFile.writeBytes(
16301649
self._symtab.symoff + (self._symtab.nsyms * nlist_64.SIZE),
16311650
newSymbols
16321651
)
1633-
self._machoCtx.writeBytes(
1652+
linkeditFile.writeBytes(
16341653
self._symtab.stroff + self._symtab.strsize,
16351654
newStrings
16361655
)

0 commit comments

Comments
 (0)