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

Commit e43ca82

Browse files
committed
update
1 parent b721af5 commit e43ca82

File tree

2 files changed

+93
-9
lines changed

2 files changed

+93
-9
lines changed

bin/dyldex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def _extractImage(
163163
slide_info.processSlideInfo(extractionCtx)
164164
linkedit_optimizer.optimizeLinkedit(extractionCtx)
165165
stub_fixer.fixStubs(extractionCtx)
166-
objc_fixer.fixObjC(extractionCtx)
166+
# objc_fixer.fixObjC(extractionCtx)
167167

168168
writeProcedures = macho_offset.optimizeOffsets(extractionCtx)
169169

src/DyldExtractor/converter/objc_fixer.py

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,14 @@
2424
objc_protocol_t
2525
)
2626

27+
from DyldExtractor.macho.macho_context import MachOContext
2728
from DyldExtractor.macho.macho_structs import (
2829
LoadCommands,
2930
linkedit_data_command,
3031
mach_header_64,
3132
segment_command_64
3233
)
3334

34-
from DyldExtractor.dyld.dyld_structs import dyld_cache_mapping_info
35-
3635

3736
# Change modify the disasm_lite to accept an offset
3837
# This is used to speed up the disassembly, and should
@@ -399,6 +398,30 @@ def run(self):
399398

400399
self._createExtraSegment()
401400

401+
# Get __OBJC_RO from the libobjc.A.dylib image
402+
for image in self._dyldCtx.images:
403+
path = self._dyldCtx.fileCtx.readString(image.pathFileOffset)
404+
if b"libobjc.A.dylib" in path:
405+
offset, ctx = self._dyldCtx.convertAddr(image.address)
406+
libobjcImage = MachOContext(ctx.fileCtx, offset)
407+
if b"__OBJC_RO" in libobjcImage.segments:
408+
self._objcRoSeg = libobjcImage.segments[b"__OBJC_RO"].seg
409+
self._objcRwSeg = libobjcImage.segments[b"__OBJC_RW"].seg
410+
else:
411+
self._logger.error("libobjc does not contain __OBJC_RO")
412+
return
413+
break
414+
pass
415+
else:
416+
self._logger.error("Unable to find libobjc.A.dylib")
417+
return
418+
419+
self._objcRoRelativeNames = self._getMethodNameStorage()
420+
if not self._objcRoRelativeNames:
421+
print("Not using objc_ro")
422+
pass
423+
# return
424+
402425
# caches that map the original definition address
403426
# to its new processed address.
404427
self._categoryCache: Dict[int, int] = {}
@@ -433,6 +456,62 @@ def run(self):
433456
self._addExtraDataSeg()
434457
pass
435458

459+
def _getMethodNameStorage(self) -> bool:
460+
"""Check where method names are stored.
461+
462+
Starting around iOS 15, relative method names
463+
pointers are relative to the start of the __OBJC_RO of
464+
libobjc, instead of being relative to itself.
465+
This tries to detect which is being used.
466+
467+
Returns:
468+
A bool that determines if the method names
469+
are relative to the __OBJC_RO.
470+
"""
471+
472+
# TODO: Maybe there is a better way to detect this
473+
474+
# Get a method list
475+
methodListAddr = None
476+
for seg in self._machoCtx.segmentsI:
477+
for sect in seg.sectsI:
478+
if sect.segname == b"__objc_methlist":
479+
methodListAddr = sect.addr
480+
break
481+
pass
482+
if methodListAddr:
483+
break
484+
pass
485+
486+
if methodListAddr is None:
487+
self._logger.warning("Unable to determine the type of method name addressing") # noqa
488+
return False
489+
490+
methodListDef = self._slider.slideStruct(methodListAddr, objc_method_list_t)
491+
if methodListDef.entsize == objc_method_large_t.SIZE:
492+
# TODO: probably want to test at least 2 method lists
493+
return False
494+
495+
for i in range(methodListDef.count):
496+
methodAddr = (
497+
methodListAddr
498+
+ objc_method_list_t.SIZE
499+
+ (i * methodListDef.entsize)
500+
)
501+
methodDef = self._slider.slideStruct(methodAddr, objc_method_small_t)
502+
503+
# test if the offset is negative or greater than __OBJC_RO's size
504+
if methodDef.name <= 0 or methodDef.name > self._objcRoSeg.vmsize:
505+
return False
506+
507+
# if the offset results in a string with non ascii characters
508+
nameOff, ctx = self._dyldCtx.convertAddr(methodAddr + methodDef.name)
509+
name = ctx.fileCtx.readString(nameOff)
510+
if not all(c < 128 for c in name):
511+
return True
512+
513+
return False
514+
436515
def _createExtraSegment(self) -> None:
437516
"""Create an extra segment to store data in.
438517
"""
@@ -1029,6 +1108,14 @@ def _processMethodList(self, methodListAddr: int, noImp=False) -> int:
10291108
self._logger.error(f"Large method list at {hex(methodListAddr)}, has an entsize that doesn't match the size of objc_method_large_t") # noqa
10301109
return 0
10311110

1111+
if (
1112+
methodListAddr >= self._objcRoSeg.vmaddr
1113+
and methodListAddr < self._objcRoSeg.vmaddr + self._objcRoSeg.vmsize
1114+
):
1115+
pass
1116+
else:
1117+
self._logger.debug("method list outside")
1118+
10321119
# fix relative pointers after we reserve a new address for the method list
10331120
# contains a list of tuples of field offsets and their target addresses
10341121
relativeFixups: list[tuple[int, int]] = []
@@ -1045,13 +1132,13 @@ def _processMethodList(self, methodListAddr: int, noImp=False) -> int:
10451132

10461133
if methodDef.name:
10471134
nameAddr = methodAddr + methodDef.name
1048-
if (newNameAddr := self._processString(nameAddr, _logString=False)) is not None:
1135+
if (newNameAddr := self._processString(nameAddr)) is not None:
10491136
methodDef.name = newNameAddr - methodAddr
10501137

10511138
relativeFixups.append((methodOff, newNameAddr))
10521139
else:
10531140
methodDef.name = 0
1054-
self._logger.warning(f"Unable to get string at {hex(nameAddr)}, for method def at {hex(methodAddr)}") # noqa
1141+
# self._logger.warning(f"Unable to get string at {hex(nameAddr)}, for method def at {hex(methodAddr)}") # noqa
10551142
pass
10561143
else:
10571144
self._logger.debug("Null method name")
@@ -1113,7 +1200,7 @@ def _processMethodList(self, methodListAddr: int, noImp=False) -> int:
11131200
self._methodListCache[methodListAddr] = newMethodListAddr
11141201
return newMethodListAddr
11151202

1116-
def _processString(self, stringAddr: int, _logString=False) -> int:
1203+
def _processString(self, stringAddr: int) -> int:
11171204
if stringAddr in self._stringCache:
11181205
return self._stringCache[stringAddr]
11191206

@@ -1130,9 +1217,6 @@ def _processString(self, stringAddr: int, _logString=False) -> int:
11301217

11311218
stringData = ctx.fileCtx.readString(stringOff)
11321219
self._addExtraData(stringData)
1133-
1134-
if _logString:
1135-
self._logger.debug(stringData)
11361220
pass
11371221

11381222
self._stringCache[stringAddr] = newStringAddr

0 commit comments

Comments
 (0)