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

Commit 808a5ad

Browse files
committed
Fixed issue with objc image index
1 parent 72584a3 commit 808a5ad

File tree

2 files changed

+199
-23
lines changed

2 files changed

+199
-23
lines changed

src/DyldExtractor/converter/objc_fixer.py

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@
3131
objc_protocol_list_t,
3232
objc_protocol_t,
3333
relative_list_list_t,
34-
relative_list_t
34+
relative_list_t,
35+
objc_opt_t_V12,
36+
objc_opt_t_V15a,
37+
objc_headeropt_ro_t,
38+
objc_header_info_ro_t_64
3539
)
3640

3741
from DyldExtractor.macho.macho_structs import (
@@ -419,13 +423,10 @@ def run(self):
419423
return
420424

421425
# Get image index
422-
imageAddr = self._machoCtx.segments[b"__TEXT"].seg.vmaddr
423-
for i, image in enumerate(self._dyldCtx.images):
424-
if image.address == imageAddr:
425-
self._imageIndex = i
426-
break
427-
pass
428-
426+
self._imageIndex = self._getImageIndex()
427+
if self._imageIndex == -1:
428+
self._logger.error("Unable to get objc image index")
429+
return
429430

430431
self._checkMethodNameStorage()
431432

@@ -929,22 +930,20 @@ def _processClassData(self, classDataAddr: int, isStubClass=False) -> int:
929930
classDataDef.name = self._processString(classDataDef.name)
930931
pass
931932

932-
baseMethodsAddr = classDataDef.baseMethods
933-
if baseMethodsAddr & 0x1:
934-
baseMethodsAddr = self._findInImageRelList(baseMethodsAddr & ~0x1)
935-
if baseMethodsAddr:
933+
if classDataDef.baseMethods & 0x1:
934+
classDataDef.baseMethods = self._findInImageRelList(classDataDef.baseMethods & ~0x1)
935+
if classDataDef.baseMethods:
936936
classDataDef.baseMethods = self._processMethodList(
937-
baseMethodsAddr,
937+
classDataDef.baseMethods,
938938
noImp=isStubClass
939939
)
940940
pass
941941

942942

943-
baseProtocolsAddr = classDataDef.baseProtocols
944-
if baseProtocolsAddr & 0x1:
945-
baseProtocolsAddr = self._findInImageRelList(baseProtocolsAddr & ~0x1)
946-
if baseProtocolsAddr:
947-
classDataDef.baseProtocols = self._processProtocolList(baseProtocolsAddr)
943+
if classDataDef.baseProtocols & 0x1:
944+
classDataDef.baseProtocols = self._findInImageRelList(classDataDef.baseProtocols & ~0x1)
945+
if classDataDef.baseProtocols:
946+
classDataDef.baseProtocols = self._processProtocolList(classDataDef.baseProtocols)
948947
pass
949948

950949
if classDataDef.ivars:
@@ -958,11 +957,10 @@ def _processClassData(self, classDataAddr: int, isStubClass=False) -> int:
958957
)
959958
pass
960959

961-
basePropertiesAddr = classDataDef.baseProperties
962-
if basePropertiesAddr & 0x1:
963-
basePropertiesAddr = self._findInImageRelList(basePropertiesAddr & ~0x1)
964-
if basePropertiesAddr:
965-
classDataDef.baseProperties = self._processPropertyList(basePropertiesAddr)
960+
if classDataDef.baseProperties & 0x1:
961+
classDataDef.baseProperties = self._findInImageRelList(classDataDef.baseProperties & ~0x1)
962+
if classDataDef.baseProperties:
963+
classDataDef.baseProperties = self._processPropertyList(classDataDef.baseProperties)
966964
pass
967965

968966
# add or update data
@@ -1425,6 +1423,56 @@ def _findInImageRelList(self, relListListAddr: int) -> int:
14251423
return relListAddr + offset
14261424

14271425
return 0
1426+
1427+
def _getImageIndex(self) -> int:
1428+
"""Get the ObjC specific image index.
1429+
1430+
Returns:
1431+
The image index or -1 if not found.
1432+
"""
1433+
1434+
# Read headeropt offset
1435+
objcOptAddr = None
1436+
for seg in self._libobjcImage.segments.values():
1437+
if b"__objc_opt_ro" in seg.sects:
1438+
objcOptAddr = seg.sects[b"__objc_opt_ro"].addr
1439+
break
1440+
if objcOptAddr is None:
1441+
self._logger.error("Unable to find __objc_opt_ro section")
1442+
return -1
1443+
1444+
# Get header opt offset
1445+
objcOptOff, objcOptFile = self._dyldCtx.convertAddr(objcOptAddr)
1446+
objcOptVer = objcOptFile.readFormat("<I", objcOptOff)[0]
1447+
1448+
if objcOptVer in (12, 13):
1449+
headerOptOff = objc_opt_t_V12(objcOptFile.file, objcOptOff).headeropt_offset
1450+
elif objcOptVer in (15, 16):
1451+
headerOptOff = objc_opt_t_V15a(objcOptFile.file, objcOptOff).headeropt_ro_offset
1452+
else:
1453+
self._logger.error(f"Unknown objc_opt_t version: {objcOptVer}")
1454+
return -1
1455+
if headerOptOff == 0:
1456+
self._logger.error("libobjc does not have objc_headeropt_ro_t")
1457+
return -1
1458+
headerOptAddr = objcOptAddr + headerOptOff
1459+
1460+
# Find image index
1461+
imageAddr = self._machoCtx.segments[b"__TEXT"].seg.vmaddr
1462+
headerOptDataOff, headerOptFile = self._dyldCtx.convertAddr(headerOptAddr)
1463+
headerOpt = objc_headeropt_ro_t(headerOptFile.file, headerOptDataOff)
1464+
1465+
for i in range(headerOpt.count):
1466+
infoOff = objc_headeropt_ro_t.SIZE + (i * headerOpt.entsize)
1467+
infoAddr = headerOptAddr + infoOff
1468+
infoDataOff = headerOptDataOff + infoOff
1469+
info = objc_header_info_ro_t_64(headerOptFile.file, infoDataOff)
1470+
1471+
if info.mhdr_offset + infoAddr == imageAddr:
1472+
return i
1473+
1474+
return -1
1475+
14281476

14291477
def _finalizeFutureClasses(self) -> None:
14301478
extraSegStart = self._extraDataHead - len(self._extraData)

src/DyldExtractor/objc/objc_structs.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,131 @@ def getOffset(self) -> int:
312312

313313
def getImageIndex(self) -> int:
314314
return self.offsetAndIndex & 0xFFFF
315+
316+
317+
class objc_opt_t_V12(Structure):
318+
319+
version: int
320+
selopt_offset: int
321+
headeropt_offset: int
322+
clsopt_offset: int
323+
324+
_fields_ = [
325+
("version", c_uint32),
326+
("selopt_offset", c_int32),
327+
("headeropt_offset", c_int32),
328+
("clsopt_offset", c_int32),
329+
]
330+
331+
332+
class objc_opt_t_V13(Structure):
333+
334+
version: int
335+
selopt_offset: int
336+
headeropt_offset: int
337+
clsopt_offset: int
338+
protocolopt_offset: int
339+
340+
_fields_ = [
341+
("version", c_uint32),
342+
("selopt_offset", c_int32),
343+
("headeropt_offset", c_int32),
344+
("clsopt_offset", c_int32),
345+
("protocolopt_offset", c_int32),
346+
]
347+
348+
349+
class objc_opt_t_V15a(Structure):
350+
351+
version: int
352+
flags: int
353+
selopt_offset: int
354+
headeropt_ro_offset: int
355+
clsopt_offset: int
356+
protocolopt_offset: int
357+
headeropt_rw_offset: int
358+
359+
_fields_ = [
360+
("version", c_uint32),
361+
("flags", c_uint32),
362+
("selopt_offset", c_int32),
363+
("headeropt_ro_offset", c_int32),
364+
("clsopt_offset", c_int32),
365+
("protocolopt_offset", c_int32),
366+
("headeropt_rw_offset", c_int32),
367+
]
368+
369+
370+
class objc_opt_t_V15b(Structure):
371+
372+
version: int
373+
flags: int
374+
selopt_offset: int
375+
headeropt_ro_offset: int
376+
clsopt_offset: int
377+
unused_protocolopt_offset: int
378+
headeropt_rw_offset: int
379+
protocolopt_offset: int
380+
381+
_fields_ = [
382+
("version", c_uint32),
383+
("flags", c_uint32),
384+
("selopt_offset", c_int32),
385+
("headeropt_ro_offset", c_int32),
386+
("clsopt_offset", c_int32),
387+
("unused_protocolopt_offset", c_int32),
388+
("headeropt_rw_offset", c_int32),
389+
("protocolopt_offset", c_int32),
390+
]
391+
392+
393+
class objc_opt_t_V16(Structure):
394+
395+
version: int
396+
flags: int
397+
selopt_offset: int
398+
headeropt_ro_offset: int
399+
unused_clsopt_offset: int
400+
unused_protocolopt_offset: int # This is now 0 as we've moved to the new protocolopt_offset # noqa
401+
headeropt_rw_offset: int
402+
unused_protocolopt2_offset: int
403+
largeSharedCachesClassOffset: int
404+
largeSharedCachesProtocolOffset: int
405+
relativeMethodSelectorBaseAddressOffset: int # Relative method list selectors are offsets from this address # noqa
406+
407+
_fields_ = [
408+
("version", c_uint32),
409+
("flags", c_uint32),
410+
("selopt_offset", c_int32),
411+
("headeropt_ro_offset", c_int32),
412+
("unused_clsopt_offset", c_int32),
413+
("unused_protocolopt_offset", c_int32),
414+
("headeropt_rw_offset", c_int32),
415+
("unused_protocolopt2_offset", c_int32),
416+
("largeSharedCachesClassOffset", c_int32),
417+
("largeSharedCachesProtocolOffset", c_int32),
418+
("relativeMethodSelectorBaseAddressOffset", c_int64),
419+
]
420+
421+
422+
class objc_headeropt_ro_t(Structure):
423+
SIZE = 8
424+
425+
count: int
426+
entsize: int
427+
428+
_fields_ = [
429+
("count", c_uint32),
430+
("entsize", c_uint32),
431+
]
432+
433+
434+
class objc_header_info_ro_t_64(Structure):
435+
436+
mhdr_offset: int
437+
info_offset: int
438+
439+
_fields_ = [
440+
("mhdr_offset", c_int64),
441+
("info_offset", c_int64),
442+
]

0 commit comments

Comments
 (0)