Skip to content

Commit

Permalink
Fixed issue with objc image index
Browse files Browse the repository at this point in the history
  • Loading branch information
arandomdev committed Jul 15, 2023
1 parent 72584a3 commit 808a5ad
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 23 deletions.
94 changes: 71 additions & 23 deletions src/DyldExtractor/converter/objc_fixer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
objc_protocol_list_t,
objc_protocol_t,
relative_list_list_t,
relative_list_t
relative_list_t,
objc_opt_t_V12,
objc_opt_t_V15a,
objc_headeropt_ro_t,
objc_header_info_ro_t_64
)

from DyldExtractor.macho.macho_structs import (
Expand Down Expand Up @@ -419,13 +423,10 @@ def run(self):
return

# Get image index
imageAddr = self._machoCtx.segments[b"__TEXT"].seg.vmaddr
for i, image in enumerate(self._dyldCtx.images):
if image.address == imageAddr:
self._imageIndex = i
break
pass

self._imageIndex = self._getImageIndex()
if self._imageIndex == -1:
self._logger.error("Unable to get objc image index")
return

self._checkMethodNameStorage()

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

baseMethodsAddr = classDataDef.baseMethods
if baseMethodsAddr & 0x1:
baseMethodsAddr = self._findInImageRelList(baseMethodsAddr & ~0x1)
if baseMethodsAddr:
if classDataDef.baseMethods & 0x1:
classDataDef.baseMethods = self._findInImageRelList(classDataDef.baseMethods & ~0x1)
if classDataDef.baseMethods:
classDataDef.baseMethods = self._processMethodList(
baseMethodsAddr,
classDataDef.baseMethods,
noImp=isStubClass
)
pass


baseProtocolsAddr = classDataDef.baseProtocols
if baseProtocolsAddr & 0x1:
baseProtocolsAddr = self._findInImageRelList(baseProtocolsAddr & ~0x1)
if baseProtocolsAddr:
classDataDef.baseProtocols = self._processProtocolList(baseProtocolsAddr)
if classDataDef.baseProtocols & 0x1:
classDataDef.baseProtocols = self._findInImageRelList(classDataDef.baseProtocols & ~0x1)
if classDataDef.baseProtocols:
classDataDef.baseProtocols = self._processProtocolList(classDataDef.baseProtocols)
pass

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

basePropertiesAddr = classDataDef.baseProperties
if basePropertiesAddr & 0x1:
basePropertiesAddr = self._findInImageRelList(basePropertiesAddr & ~0x1)
if basePropertiesAddr:
classDataDef.baseProperties = self._processPropertyList(basePropertiesAddr)
if classDataDef.baseProperties & 0x1:
classDataDef.baseProperties = self._findInImageRelList(classDataDef.baseProperties & ~0x1)
if classDataDef.baseProperties:
classDataDef.baseProperties = self._processPropertyList(classDataDef.baseProperties)
pass

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

return 0

def _getImageIndex(self) -> int:
"""Get the ObjC specific image index.
Returns:
The image index or -1 if not found.
"""

# Read headeropt offset
objcOptAddr = None
for seg in self._libobjcImage.segments.values():
if b"__objc_opt_ro" in seg.sects:
objcOptAddr = seg.sects[b"__objc_opt_ro"].addr
break
if objcOptAddr is None:
self._logger.error("Unable to find __objc_opt_ro section")
return -1

# Get header opt offset
objcOptOff, objcOptFile = self._dyldCtx.convertAddr(objcOptAddr)
objcOptVer = objcOptFile.readFormat("<I", objcOptOff)[0]

if objcOptVer in (12, 13):
headerOptOff = objc_opt_t_V12(objcOptFile.file, objcOptOff).headeropt_offset
elif objcOptVer in (15, 16):
headerOptOff = objc_opt_t_V15a(objcOptFile.file, objcOptOff).headeropt_ro_offset
else:
self._logger.error(f"Unknown objc_opt_t version: {objcOptVer}")
return -1
if headerOptOff == 0:
self._logger.error("libobjc does not have objc_headeropt_ro_t")
return -1
headerOptAddr = objcOptAddr + headerOptOff

# Find image index
imageAddr = self._machoCtx.segments[b"__TEXT"].seg.vmaddr
headerOptDataOff, headerOptFile = self._dyldCtx.convertAddr(headerOptAddr)
headerOpt = objc_headeropt_ro_t(headerOptFile.file, headerOptDataOff)

for i in range(headerOpt.count):
infoOff = objc_headeropt_ro_t.SIZE + (i * headerOpt.entsize)
infoAddr = headerOptAddr + infoOff
infoDataOff = headerOptDataOff + infoOff
info = objc_header_info_ro_t_64(headerOptFile.file, infoDataOff)

if info.mhdr_offset + infoAddr == imageAddr:
return i

return -1


def _finalizeFutureClasses(self) -> None:
extraSegStart = self._extraDataHead - len(self._extraData)
Expand Down
128 changes: 128 additions & 0 deletions src/DyldExtractor/objc/objc_structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,131 @@ def getOffset(self) -> int:

def getImageIndex(self) -> int:
return self.offsetAndIndex & 0xFFFF


class objc_opt_t_V12(Structure):

version: int
selopt_offset: int
headeropt_offset: int
clsopt_offset: int

_fields_ = [
("version", c_uint32),
("selopt_offset", c_int32),
("headeropt_offset", c_int32),
("clsopt_offset", c_int32),
]


class objc_opt_t_V13(Structure):

version: int
selopt_offset: int
headeropt_offset: int
clsopt_offset: int
protocolopt_offset: int

_fields_ = [
("version", c_uint32),
("selopt_offset", c_int32),
("headeropt_offset", c_int32),
("clsopt_offset", c_int32),
("protocolopt_offset", c_int32),
]


class objc_opt_t_V15a(Structure):

version: int
flags: int
selopt_offset: int
headeropt_ro_offset: int
clsopt_offset: int
protocolopt_offset: int
headeropt_rw_offset: int

_fields_ = [
("version", c_uint32),
("flags", c_uint32),
("selopt_offset", c_int32),
("headeropt_ro_offset", c_int32),
("clsopt_offset", c_int32),
("protocolopt_offset", c_int32),
("headeropt_rw_offset", c_int32),
]


class objc_opt_t_V15b(Structure):

version: int
flags: int
selopt_offset: int
headeropt_ro_offset: int
clsopt_offset: int
unused_protocolopt_offset: int
headeropt_rw_offset: int
protocolopt_offset: int

_fields_ = [
("version", c_uint32),
("flags", c_uint32),
("selopt_offset", c_int32),
("headeropt_ro_offset", c_int32),
("clsopt_offset", c_int32),
("unused_protocolopt_offset", c_int32),
("headeropt_rw_offset", c_int32),
("protocolopt_offset", c_int32),
]


class objc_opt_t_V16(Structure):

version: int
flags: int
selopt_offset: int
headeropt_ro_offset: int
unused_clsopt_offset: int
unused_protocolopt_offset: int # This is now 0 as we've moved to the new protocolopt_offset # noqa
headeropt_rw_offset: int
unused_protocolopt2_offset: int
largeSharedCachesClassOffset: int
largeSharedCachesProtocolOffset: int
relativeMethodSelectorBaseAddressOffset: int # Relative method list selectors are offsets from this address # noqa

_fields_ = [
("version", c_uint32),
("flags", c_uint32),
("selopt_offset", c_int32),
("headeropt_ro_offset", c_int32),
("unused_clsopt_offset", c_int32),
("unused_protocolopt_offset", c_int32),
("headeropt_rw_offset", c_int32),
("unused_protocolopt2_offset", c_int32),
("largeSharedCachesClassOffset", c_int32),
("largeSharedCachesProtocolOffset", c_int32),
("relativeMethodSelectorBaseAddressOffset", c_int64),
]


class objc_headeropt_ro_t(Structure):
SIZE = 8

count: int
entsize: int

_fields_ = [
("count", c_uint32),
("entsize", c_uint32),
]


class objc_header_info_ro_t_64(Structure):

mhdr_offset: int
info_offset: int

_fields_ = [
("mhdr_offset", c_int64),
("info_offset", c_int64),
]

0 comments on commit 808a5ad

Please sign in to comment.