Skip to content

Commit

Permalink
Use 0 as load address for Mach-O libraries (#436)
Browse files Browse the repository at this point in the history
Co-authored-by: Florian Magin <[email protected]>
  • Loading branch information
fmagin and fmagin authored Nov 12, 2023
1 parent 042d959 commit a496989
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 15 deletions.
19 changes: 12 additions & 7 deletions cle/backends/macho/macho.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,19 @@ def __init__(self, *args, **kwargs):
self.linked_base = self.mapped_base = 0x4000
elif self.filetype == MachoFiletype.MH_DYLIB and self.is_main_bin:
# the segments of dylibs are just relative to the load address, i.e. the lowest segment addr is 0
# we need to set the load address to 0x100000000 because otherwise the loader will try to map the
# file to 0x400000, which is illegal for Mach-O
# But we can't set the linked base to request this, because the MachO Backend implementation
# we need to set the load address to something because otherwise the loader will try to map the
# file to 0x400000, which is technically illegal for Mach-O because of PAGEZERO
#
# The problem is that libraries also tend to have relative pointers (e.g. inside ObjC Metadata),
# which are rebased by parsing the rebase_blob, which isn't supported yet (but coming soon)
# so we set the base addr to 0 to make them work out without having to deal with this
# IDA and Ghidra both seem to handle it this way too
# AFAIU this isn't a problem with iOS15+ binaries anymore that use the new binding fixups
# but for now we just load all libraries, that are loaded as the main object, at address 0
#
# We can't set the linked base to request this, because the MachO Backend implementation
# uses this to recalculate the addresses
if self.arch.bits == 64:
self._custom_base_addr = 2**32
elif self.arch.bits == 32:
self._custom_base_addr = 0x4000
self._custom_base_addr = 0
elif self.filetype == MachoFiletype.MH_DYLIB and not self.is_main_bin:
# A Library is loaded as a dependency, this is fine, the loader will map it to somewhere above the main
# binary, so we don't need to do anything
Expand Down
16 changes: 8 additions & 8 deletions tests/test_macho_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def test_library_15():
ld = cle.Loader(TEST_BASE / "FrameWorkApp.app_15" / "Frameworks" / "dynamicLibrary.framework" / "dynamicLibrary")
lib = ld.main_object
assert isinstance(lib, MachO)
# The base address should be 0x10000000
# 0 implies that the Backend failed to treat the library segment addresses as _relative_ to the base address
# 0x400000 implies that the Loader chose the default base address for PIE binaries, which is invalid for Mach-O
assert ld.main_object.min_addr == 0x100000000
# The base address should be 0 until full rebasing support is implemented
# because the rebase blob isn't parsed yet, some internal pointers aren't rebased from their relative values
# and only work out correctly if the library is loaded at 0
assert ld.main_object.min_addr == 0


def test_library_14():
Expand All @@ -32,10 +32,10 @@ def test_library_14():
ld = cle.Loader(TEST_BASE / "FrameWorkApp.app_14" / "Frameworks" / "dynamicLibrary.framework" / "dynamicLibrary")
lib = ld.main_object
assert isinstance(lib, MachO)
# The base address should be 0x10000000
# 0 implies that the Backend failed to treat the library segment addresses as _relative_ to the base address
# 0x400000 implies that the Loader chose the default base address for PIE binaries, which is invalid for Mach-O
assert ld.main_object.min_addr == 0x100000000
# The base address should be 0 until full rebasing support is implemented
# because the rebase blob isn't parsed yet, some internal pointers aren't rebased from their relative values
# and only work out correctly if the library is loaded at 0
assert ld.main_object.min_addr == 0


def test_framework_ios15():
Expand Down

0 comments on commit a496989

Please sign in to comment.