2424 objc_protocol_t
2525)
2626
27+ from DyldExtractor .macho .macho_context import MachOContext
2728from 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