def test_parse_ivars(self) -> None: parser = MachoParser(TestObjcRuntimeDataParser.CATEGORY_PATH) binary = parser.get_arm64_slice() assert binary dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) # Given I read a class with a known ivar layout cls = [x for x in objc_parser.classes if x.name == "AamvaPDF417"][0] # If I read its parsed ivar layout parsed_ivars = [(ivar.name, ivar.class_name, ivar.field_offset) for ivar in cls.ivars] correct_ivar_layout = [ ("_fields", '@"NSMutableDictionary"', 8), ("fields_desc", '@"NSDictionary"', 16), ("found_bar_codes", '@"NSDictionary"', 24), ("source", '@"NSString"', 32), ("data_element_separator", "S", 40), ("record_separator", "S", 42), ("segment_terminator", "S", 44), ("file_type", '@"NSString"', 48), ("number_of_entries", "i", 56), ("header_length", "i", 60), ("aamva_version_number", "i", 64), ("jurisdiction_version_number", "i", 68), ("mandatory", '@"NSDictionary"', 72), ("optional", '@"NSDictionary"', 80), ("_failed", '@"NSMutableArray"', 88), ] # Then the correct data is provided assert sorted(parsed_ivars) == sorted(correct_ivar_layout)
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_class_conforming_protocols(self) -> None: def check_class_conformed_protocols( class_name: str, correct_protocols: List[str]) -> None: cls = [x for x in objc_parser.classes if x.name == class_name][0] assert cls is not None conformed_protocol_names = [x.name for x in cls.protocols] assert conformed_protocol_names == correct_protocols parser = MachoParser(TestObjcRuntimeDataParser.CATEGORY_PATH) binary = parser.get_arm64_slice() assert binary dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) check_class_conformed_protocols( "CDVInAppBrowserViewController", [ "CDVScreenOrientationDelegate", "WKNavigationDelegate", "WKUIDelegate", "WKScriptMessageHandler" ], ) check_class_conformed_protocols( "_TtC26Digital_Advisory_Solutions21LicenceViewController", ["UITableViewDataSource"]) # this class doesn't conform to any protocols check_class_conformed_protocols("BadFilesDetector", [])
def test_ios13_absolute_method_lists(self): # Given a binary compiled with a minimum deployment target of iOS 13 parser = MachoParser( TestObjcRuntimeDataParser.IOS13_ABSOLUTE_METHOD_LIST_BIN_PATH) binary = parser.get_arm64_slice() binary.get_minimum_deployment_target() # When the Objective C methods within the binary are parsed dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) selref_selector_map = objc_parser.selrefs_to_selectors() # Then the method structures are correctly parsed assert len(selref_selector_map) == 3 s1 = selref_selector_map[VirtualMemoryPointer(0x10000D380)] assert s1.implementation is None assert s1.is_external_definition is True assert s1.name == "role" s2 = selref_selector_map[VirtualMemoryPointer(0x10000D388)] assert s2.implementation is None assert s2.is_external_definition is True assert s2.name == "initWithName:sessionRole:" s3 = selref_selector_map[VirtualMemoryPointer(0x10000D378)] assert s3.implementation == VirtualMemoryPointer(0x100006354) assert s3.is_external_definition is False assert s3.name == "viewDidLoad"
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_section_name_collision(self) -> None: # Given I provide a binary which has two sections with the same name binary = MachoParser(self.MULTIPLE_CONST_SECTIONS).get_arm64_slice() assert binary # If I read the two sections text_const = binary.section_with_name("__const", "__TEXT") data_const = binary.section_with_name("__const", "__DATA") # Then I get two sections assert text_const is not None assert data_const is not None # And each section contains the correct information assert text_const.address == 0x1A0D0 assert data_const.address == 0x1C458
def test_read_classlist_data_const_segment(self) -> None: # Given a binary which stores the __objc_classlist section in the __DATA_CONST segment binary_with_data_classlist = MachoParser(TestMachoBinary.CLASSLIST_DATA_CONST).get_arm64_slice() assert binary_with_data_classlist # If I read the __objc_classlist pointer section locations, entries = binary_with_data_classlist.read_pointer_section("__objc_classlist") correct_locations = [0x100008098, 0x1000080A0, 0x1000080A8] correct_entries = [0x10000D3C0, 0x10000D438, 0x10000D488] # Then I get the correct data assert sorted(locations) == sorted(correct_locations) assert sorted(entries) == sorted(correct_entries)
def test_read_classlist_data_segment(self) -> None: # Given a binary which stores the __objc_classlist section in the __DATA segment binary_with_data_classlist = MachoParser(TestMachoBinary.THIN_PATH).get_arm64_slice() assert binary_with_data_classlist # If I read the __objc_classlist pointer section locations, entries = binary_with_data_classlist.read_pointer_section("__objc_classlist") correct_locations = [0x100008178, 0x100008180, 0x100008188, 0x100008190] correct_entries = [0x100009120, 0x100009170, 0x1000091E8, 0x100009238] # Then I get the correct data assert sorted(locations) == sorted(correct_locations) assert sorted(entries) == sorted(correct_entries)
def test_write_fat_binary(self) -> None: # Given I add a load command to the arm64 slice of an armv7/arm64 FAT file parser = MachoParser(self.FAT_PATH) binary = parser.get_arm64_slice() assert binary original_dylibs = [binary.dylib_name_for_library_ordinal(i + 1) for i in range(len(binary.load_dylib_commands))] 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_fat" armv7_binary = parser.get_armv7_slice() assert armv7_binary # If I write the FAT to disk with both slices, then parse the on-disk version MachoBinary.write_fat([armv7_binary, modified_binary], output_binary_path) on_disk_fat_parser = MachoParser(output_binary_path) assert len(on_disk_fat_parser.slices) == 2 # Then I get a FAT with valid slices armv7 = on_disk_fat_parser.get_armv7_slice() assert armv7 is not None assert len(armv7.segments) == 4 arm64 = on_disk_fat_parser.get_arm64_slice() assert arm64 is not None assert len(arm64.segments) == 4 # And the arm64 segment contains the new load command new_dylibs = [arm64.dylib_name_for_library_ordinal(i + 1) for i in range(len(arm64.load_dylib_commands))] assert new_dylibs == original_dylibs + [new_dylib_name]
def test_find_protocols(self) -> None: parser = MachoParser(TestObjcRuntimeDataParser.FAT_PATH) binary = parser.get_arm64_slice() assert binary dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) protocols = objc_parser.protocols assert len(protocols) == 3 correct_protocols = [ "NSObject", "NSURLSessionDelegate", "UIApplicationDelegate" ] for p in correct_protocols: assert p in [a.name for a in protocols]
def setup_method(self) -> None: self.parser = MachoParser(pathlib.Path(TestMachoBinary.THIN_PATH)) # ensure only one slice is returned with a thin Mach-O slices = self.parser.slices assert len(slices) == 1 self.binary = self.parser.slices[0] assert self.binary is not None
def main(): arg_parser = argparse.ArgumentParser(description="bitcode_retriever clone") arg_parser.add_argument("binary_path", metavar="binary_path", type=str, help="Path to Bitcode binary") args = arg_parser.parse_args() parser = MachoParser(pathlib.Path(args.binary_path)) print(f"Reading {len(parser.slices)} Mach-O slices...") for binary in parser.slices: # Does the slice contain a Bitcode archive? bitcode_segment = binary.segment_with_name("__LLVM") if not bitcode_segment: continue # Dump the Bitcode adjacent to this file, named <arch>.xar bitcode_segment = binary.segment_with_name("__LLVM") if not bitcode_segment: raise ValueError(f"The provided Mach-O does not contain Bitcode.") xar_data = binary.get_bytes(bitcode_segment.fileoff, bitcode_segment.filesize) # Place the bitcode adjacent to this file, named <arch>.xar output_path = pathlib.Path( __file__).parent / f"{binary.cpu_type.name.lower()}.xar" with open(output_path, "xb") as f: f.write(xar_data) print( f"Dumped {binary.cpu_type.name.lower()} Bitcode XAR to {output_path}" )
def main(): arg_parser = argparse.ArgumentParser( description="Add a load command to a binary") arg_parser.add_argument("binary_path", type=str, help="Path to binary") arg_parser.add_argument( "output_path", type=str, help="Path to write the modified binary (must not already exist)") arg_parser.add_argument( "load_path", type=str, help="The dylib load path to be added to the binary") args = arg_parser.parse_args() parser = MachoParser(pathlib.Path(args.binary_path)) # Add the load command to each slice of the Mach-O modified_binaries = [] for binary in parser.slices: # Inserting load commands is currently supported on 64-bit binaries only if not binary.is_64bit: continue modified_binary = binary.insert_load_dylib_cmd(args.load_path) modified_binaries.append(modified_binary) # Create an output FAT with each of the modified binaries MachoBinary.write_fat(modified_binaries, pathlib.Path(args.output_path))
def test_protocol_32bit(self) -> None: parser = MachoParser(TestObjcRuntimeDataParser.PROTOCOL_32BIT_PATH) binary = parser.get_armv7_slice() assert binary dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) assert len(objc_parser.classes) == 66 test_cls = [ x for x in objc_parser.classes if x.name == "Pepsico_iPhoneAppDelegate" ][0] assert len(test_cls.protocols) == 2 proto_names = [x.name for x in test_cls.protocols] assert proto_names == [ "UIApplicationDelegate", "UITabBarControllerDelegate" ]
def dump_memory(parser: MachoParser, start_address: int, size: int) -> None: # XXX(PT): Modified from strongarm-cli data = parser.get_bytes(StaticFilePointer(start_address), size) # split to 16 byte regions region_size = 16 current_index = 0 while True: if current_index >= size or current_index >= len(data): break # grab the next grouping of bytes byte_region = data[current_index : current_index + region_size] region_start = start_address + current_index print(f"{region_start:#011x}", end="\t\t") ascii_rep = "|" for idx, byte in enumerate(byte_region): print("{:02x}".format(byte), end=" ") # indent every 8 bytes if idx > 0 and (idx + 1) % 8 == 0: print("\t", end="") ascii_byte = chr(byte) if 32 <= byte < 127 else "." ascii_rep += ascii_byte ascii_rep += "|" print(ascii_rep) current_index += region_size
def main() -> None: # XXX(PT): Change this if you want to run a quick script! Write it in strongarm_script() script = False # end of config arg_parser = argparse.ArgumentParser(description="Mach-O Analyzer") arg_parser.add_argument("--verbose", action="store_true", help="Output extra info while analyzing") arg_parser.add_argument("binary_path", metavar="binary_path", type=str, help="Path to binary to analyze") args = arg_parser.parse_args() def configure_logger() -> None: root = logging.getLogger() root.setLevel(logging.DEBUG) ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO) formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s") ch.setFormatter(formatter) root.addHandler(ch) configure_logger() if args.verbose: logging.getLogger().setLevel(logging.DEBUG) print_header(args) parser = MachoParser(pathlib.Path(args.binary_path)) # print slice info print("Slices:") for macho_slice in parser.slices: print(f"\t{macho_slice.cpu_type.name} Mach-O slice") binary = pick_macho_slice(parser) print(f"Reading {binary.cpu_type.name} slice\n\n") analyzer = MachoAnalyzer.get_analyzer(binary) shell = StrongarmShell(binary, analyzer) if script: print("Running provided script...\n\n") strongarm_script(binary, analyzer) else: autorun_cmd = "info metadata segments sections loads" print(f"Auto-running '{autorun_cmd}'\n\n") shell.run_command(autorun_cmd) # this will return False once the shell exists while shell.process_command(): pass print("May your arms be beefy and your binaries unencrypted")
def test_ios14_build_version_cmd(self): # Given a binary compiled with a minimum deployment target of iOS 14 parser = MachoParser( TestObjcRuntimeDataParser.IOS14_RELATIVE_METHOD_LIST_BIN_PATH) binary = parser.get_arm64_slice() # When I query properties such as the minimum deployment target, deployment platform, # and build tool versions # Then the correct data is parsed and returned assert binary.get_minimum_deployment_target() == LooseVersion("14.0.0") assert binary.get_build_version_platform( ) == MachoBuildVersionPlatform.IOS build_tool_versions = binary.get_build_tool_versions() assert len(build_tool_versions) == 1 ld_version = build_tool_versions[0] assert ld_version.tool == MachoBuildTool.LD assert ld_version.version == 0x2610000
def test_write_bytes_thin_virtual(self) -> None: # Given a thin binary with file_type == 0x2 binary = MachoParser(pathlib.Path(TestMachoBinary.CLASSLIST_DATA_CONST)).get_arm64_slice() assert binary assert binary.file_type == 0x2 # If I patch the virtual bytes of the file_type field to hold a different value new_file_type_val = 10 # This field is a 32-bit little-endian encoded int new_file_type_bytes = new_file_type_val.to_bytes(4, "little") modified_binary = binary.write_bytes(new_file_type_bytes, 0x10000000C, virtual=True) # Then the modified binary's raw bytes contain the correct data modified_header = modified_binary.get_contents_from_address(0x100000000, 32, True) assert modified_header == bytearray( b"\xcf\xfa\xed\xfe\x0c\x00\x00\x01\x00\x00\x00\x00\x0a\x00\x00\x00\x18\x00\x00\x00H\x0b\x00\x00\x85\x00 \x00\x00\x00\x00\x00" # noqa: E501 ) # And the MachoBinary attribute contains the correct value assert modified_binary.file_type == 10
def test_parse_protocol(self) -> None: parser = MachoParser(TestObjcRuntimeDataParser.FAT_PATH) binary = parser.get_arm64_slice() assert binary dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) protocols = objc_parser.protocols # look at one protocol session_protocol = [ p for p in protocols if p.name == "NSURLSessionDelegate" ][0] assert len(session_protocol.selectors) == 3 correct_selectors = [ "URLSession:didBecomeInvalidWithError:", "URLSession:didReceiveChallenge:completionHandler:", "URLSessionDidFinishEventsForBackgroundURLSession:", ] for s in correct_selectors: assert s in [sel.name for sel in session_protocol.selectors]
def test_write_struct(self) -> None: # Given a thin binary with certain values for its first segment binary = MachoParser(pathlib.Path(TestMachoBinary.THIN_PATH)).get_arm64_slice() assert binary segment = binary.segments[0] assert segment.name == "__PAGEZERO" assert segment.vmaddr == 0x0 assert segment.vmsize == 0x100000000 assert segment.offset == 0x0 assert segment.size == 0x0 assert segment.maxprot == 0 assert segment.initprot == 0 assert segment.section_count == 0 assert segment.flags == 0 # If I create a new structure and overwrite the original structure with it new_segment = MachoSegmentCommand64() new_segment.cmd = 0x19 new_segment.cmdsize = 0x48 new_segment.segname = b"__FAKESEG" new_segment.vmaddr = 0x111 new_segment.vmsize = 0x222 new_segment.fileoff = 0x333 new_segment.filesize = 0x444 new_segment.maxprot = 5 new_segment.initprot = 6 new_segment.nsects = 7 new_segment.flags = 8 # Then we get a MachoBinary which can be successfully parsed, and contains the modified structure modified_binary = binary.write_struct(new_segment, segment.cmd.binary_offset) segment = modified_binary.segments[0] assert segment.name == "__FAKESEG" assert segment.vmaddr == 0x111 assert segment.vmsize == 0x222 assert segment.offset == 0x333 assert segment.size == 0x444 assert segment.maxprot == 5 assert segment.initprot == 6 assert segment.section_count == 7 assert segment.flags == 8
def test_path_for_external_symbol(self) -> None: parser = MachoParser(TestObjcRuntimeDataParser.FAT_PATH) binary = parser.slices[0] dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) correct_map = { "_NSLog": "/System/Library/Frameworks/Foundation.framework/Foundation", "_NSStringFromCGRect": "/System/Library/Frameworks/UIKit.framework/UIKit", "_NSStringFromClass": "/System/Library/Frameworks/Foundation.framework/Foundation", "_OBJC_CLASS_$_NSURLCredential": "/System/Library/Frameworks/Foundation.framework/Foundation", "_OBJC_CLASS_$_UIFont": "/System/Library/Frameworks/UIKit.framework/UIKit", "_OBJC_CLASS_$_UILabel": "/System/Library/Frameworks/UIKit.framework/UIKit", "_OBJC_CLASS_$_UIResponder": "/System/Library/Frameworks/UIKit.framework/UIKit", "_OBJC_CLASS_$_UIViewController": "/System/Library/Frameworks/UIKit.framework/UIKit", "_OBJC_METACLASS_$_NSObject": "/usr/lib/libobjc.A.dylib", "_OBJC_METACLASS_$_UILabel": "/System/Library/Frameworks/UIKit.framework/UIKit", "_OBJC_METACLASS_$_UIResponder": "/System/Library/Frameworks/UIKit.framework/UIKit", "_OBJC_METACLASS_$_UIViewController": "/System/Library/Frameworks/UIKit.framework/UIKit", "_SecTrustEvaluate": "/System/Library/Frameworks/Security.framework/Security", "_UIApplicationMain": "/System/Library/Frameworks/UIKit.framework/UIKit", "___CFConstantStringClassReference": "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", "__objc_empty_cache": "/usr/lib/libobjc.A.dylib", "_objc_autoreleasePoolPop": "/usr/lib/libobjc.A.dylib", "_objc_autoreleasePoolPush": "/usr/lib/libobjc.A.dylib", "_objc_msgSend": "/usr/lib/libobjc.A.dylib", "_objc_msgSendSuper2": "/usr/lib/libobjc.A.dylib", "_objc_release": "/usr/lib/libobjc.A.dylib", "_objc_retain": "/usr/lib/libobjc.A.dylib", "_objc_retainAutoreleasedReturnValue": "/usr/lib/libobjc.A.dylib", "_objc_storeStrong": "/usr/lib/libobjc.A.dylib", "_rand": "/usr/lib/libSystem.B.dylib", "dyld_stub_binder": "/usr/lib/libSystem.B.dylib", } for symbol in correct_map: assert objc_parser.path_for_external_symbol( symbol) == correct_map[symbol] assert objc_parser.path_for_external_symbol( "XXX_fake_symbol_XXX") is None
def test_read_encrypted_info(self) -> None: encrypted_binary = MachoParser(TestMachoBinary.ENCRYPTED_PATH).get_armv7_slice() assert encrypted_binary with pytest.raises(BinaryEncryptedError): # encrypted region is 0x4000 to 0x18000 encrypted_binary.get_bytes(StaticFilePointer(0x5000), 0x1000) # read from unencrypted section should not raise encrypted_binary.get_bytes(StaticFilePointer(0x3000), 0x500)
def setup_method(self) -> None: parser = MachoParser(TestFunctionAnalyzer.FAT_PATH) self.binary = parser.slices[0] self.analyzer = MachoAnalyzer.get_analyzer(self.binary) self.implementations = self.analyzer.get_imps_for_sel( "URLSession:didReceiveChallenge:completionHandler:") self.instructions = self.implementations[0].instructions self.imp_addr = self.instructions[0].address assert self.imp_addr == TestFunctionAnalyzer.URL_SESSION_DELEGATE_IMP_ADDR self.function_analyzer = ObjcFunctionAnalyzer(self.binary, self.instructions)
def test_function_starts_command(self) -> None: # Given a binary that contains functions binary_with_functions = MachoParser(TestMachoBinary.CLASSLIST_DATA_CONST).get_arm64_slice() assert binary_with_functions # And I get the function starts command function_starts = binary_with_functions._function_starts_cmd assert function_starts # The command has the expected attributes assert function_starts.cmd == 0x26 assert function_starts.cmdsize == 0x10 assert function_starts.dataoff == 0x10680 assert function_starts.datasize == 0x18 assert function_starts.sizeof == 0x10 assert function_starts.binary_offset == 0xB38
def main(): arg_parser = argparse.ArgumentParser(description="hexdump clone") arg_parser.add_argument("binary_path", type=str, help="Path to binary") arg_parser.add_argument( "-s", dest="start_address_str", type=str, help="Byte-count to skip before beginning hexdump (base16)" ) arg_parser.add_argument("-n", dest="count", type=int, help="Number of bytes to hex-dump") arg_parser.set_defaults(start_address_str="0x0", count=0x100000000) args = arg_parser.parse_args() parser = MachoParser(pathlib.Path(args.binary_path)) # Convert hex string to int start_address = int(args.start_address_str, 16) dump_memory(parser, start_address, args.count)
def test_ios14_relative_method_lists(self): # Given a binary compiled with a minimum deployment target of iOS 14 parser = MachoParser( TestObjcRuntimeDataParser.IOS14_RELATIVE_METHOD_LIST_BIN_PATH) binary = parser.get_arm64_slice() binary.get_minimum_deployment_target() # When the Objective C methods within the binary are parsed dyld_info_parser = DyldInfoParser(binary) objc_parser = ObjcRuntimeDataParser(binary, dyld_info_parser) selref_selector_map = objc_parser.selrefs_to_selectors() # Then the method structures are correctly parsed assert len(selref_selector_map) == 7 external_sel = selref_selector_map[VirtualMemoryPointer(0x10000C0E0)] assert external_sel.implementation is None assert external_sel.is_external_definition is True assert external_sel.name == "evaluateJavaScript:inFrame:inContentWorld:completionHandler:" internal_sel = selref_selector_map[VirtualMemoryPointer(0x10000C0B0)] assert internal_sel.implementation == VirtualMemoryPointer(0x100007BFC) assert internal_sel.is_external_definition is False assert internal_sel.name == "usesWebView"
def main(): arg_parser = argparse.ArgumentParser( description="dump a binary's entitlements") arg_parser.add_argument("binary_path", metavar="binary_path", type=str, help="Path to binary") args = arg_parser.parse_args() parser = MachoParser(pathlib.Path(args.binary_path)) for binary in parser.slices: print(f"Entitlements of {binary.cpu_type.name.lower()} slice:") print(binary.get_entitlements().decode()) print()
def test_get_dylib_id(self) -> None: # Given an executable binary, it has no dylib ID binary = MachoParser(self.THIN_PATH).get_arm64_slice() assert binary assert not binary.dylib_id() # Given a dylib, it has a dylib ID which is parsed correctly expected_dylib_id = "@rpath/BroadSoftDialpadFramework.framework/BroadSoftDialpadFramework" binary = MachoParser(self.MULTIPLE_CONST_SECTIONS).get_arm64_slice() assert binary assert binary.dylib_id() == expected_dylib_id
def main(): arg_parser = argparse.ArgumentParser(description="nm clone") arg_parser.add_argument( "binary_path", metavar="binary_path", type=str, help="Path to binary whose symbol table should be output") arg_parser.add_argument( "-m", action="store_true", help= "Increase verbosity. Display the source library of imported symbols & list sections of exported symbols.", ) args = arg_parser.parse_args() parser = MachoParser(pathlib.Path(args.binary_path)) for fat_slice in parser.slices: print_binary_symbols(fat_slice, args.verbose)
def main(): arg_parser = argparse.ArgumentParser(description="strings clone") arg_parser.add_argument( "binary_path", metavar="binary_path", type=str, help="Path to binary whose strings should be printed") args = arg_parser.parse_args() parser = MachoParser(pathlib.Path(args.binary_path)) # Get the unique strings from all slices all_strings = set() for fat_slice in parser.slices: # Parsing the string table requires a MachoAnalyzer analyzer = MachoAnalyzer.get_analyzer(fat_slice) all_strings.update(analyzer.strings()) for string in all_strings: print(string)