diff --git a/cle/backends/macho/macho.py b/cle/backends/macho/macho.py index 2c9b1389..65ac7793 100644 --- a/cle/backends/macho/macho.py +++ b/cle/backends/macho/macho.py @@ -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 diff --git a/tests/test_macho_libs.py b/tests/test_macho_libs.py index 7d40f417..3ccff73e 100644 --- a/tests/test_macho_libs.py +++ b/tests/test_macho_libs.py @@ -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(): @@ -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():