def test_no_space_for_new_load_command(self) -> None: # Given a binary with 0x5630 bytes of free space at the end of the Mach-O header binary = MachoParser(self.THIN_PATH).get_arm64_slice() assert binary dylib_path = "@rpath/load_cmd_with_32_chrcters" # If I have a dylib load command which will take up `0x20 + len(dylib_path) = 0x38` bytes # Then I should be able to add this load command exactly 344 times before the binary runs out of space for _ in range(344): binary = binary.insert_load_dylib_cmd(dylib_path) with pytest.raises(NoEmptySpaceForLoadCommandError): binary.insert_load_dylib_cmd(dylib_path)
def test_add_load_command(self) -> None: # Given a binary with some known load-commands binary = MachoParser(self.CLASSLIST_DATA_CONST).get_arm64_slice() assert binary original_dylibs = [ "/System/Library/Frameworks/Foundation.framework/Foundation", "/usr/lib/libobjc.A.dylib", "/usr/lib/libSystem.B.dylib", "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", "/System/Library/Frameworks/Security.framework/Security", "/System/Library/Frameworks/UIKit.framework/UIKit", ] found_dylibs = [binary.dylib_name_for_library_ordinal(i + 1) for i in range(len(binary.load_dylib_commands))] assert found_dylibs == original_dylibs # If I create a new binary with an inserted load command modified_binary = binary.insert_load_dylib_cmd("@rpath/Frameworks/Interject.framework/Interject") modified_dylibs = [ "/System/Library/Frameworks/Foundation.framework/Foundation", "/usr/lib/libobjc.A.dylib", "/usr/lib/libSystem.B.dylib", "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", "/System/Library/Frameworks/Security.framework/Security", "/System/Library/Frameworks/UIKit.framework/UIKit", "@rpath/Frameworks/Interject.framework/Interject", ] found_dylibs = [ modified_binary.dylib_name_for_library_ordinal(i + 1) for i in range(len(modified_binary.load_dylib_commands)) ] assert found_dylibs == modified_dylibs
def test_write_thin_binary(self) -> None: binary = MachoParser(self.THIN_PATH).get_arm64_slice() assert binary original_dylibs = [binary.dylib_name_for_library_ordinal(i + 1) for i in range(len(binary.load_dylib_commands))] # Given I add a load command to a binary new_dylib_name = "@rpath/Frameworks/Interject.framework/Interject" modified_binary = binary.insert_load_dylib_cmd(new_dylib_name) with TemporaryDirectory() as tempdir: output_binary_path = pathlib.Path(tempdir) / "modified_binary" # If I write the binary to disk, then parse the on-disk version modified_binary.write_binary(output_binary_path) on_disk_binary = MachoParser(output_binary_path).get_arm64_slice() assert on_disk_binary # Then the new on-disk binary contains the modification new_dylibs = [ on_disk_binary.dylib_name_for_library_ordinal(i + 1) for i in range(len(on_disk_binary.load_dylib_commands)) ] assert new_dylibs == original_dylibs + [new_dylib_name]