{"id":13993767,"url":"https://github.com/datatheorem/strongarm","last_synced_at":"2025-04-04T07:09:54.696Z","repository":{"id":41174360,"uuid":"257929055","full_name":"datatheorem/strongarm","owner":"datatheorem","description":"Mach-O analysis library 💪","archived":false,"fork":false,"pushed_at":"2023-09-08T14:04:56.000Z","size":26403,"stargazers_count":356,"open_issues_count":6,"forks_count":33,"subscribers_count":15,"default_branch":"release","last_synced_at":"2025-04-02T03:18:09.676Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datatheorem.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-04-22T14:41:01.000Z","updated_at":"2025-03-30T15:36:21.000Z","dependencies_parsed_at":"2023-01-22T16:00:32.256Z","dependency_job_id":"2836d341-e883-4261-9c93-f0e2c44582cd","html_url":"https://github.com/datatheorem/strongarm","commit_stats":{"total_commits":1683,"total_committers":8,"mean_commits":210.375,"dds":0.1717171717171717,"last_synced_commit":"7207d14e13d95df5530c7b36cff45fdb0c0e9923"},"previous_names":[],"tags_count":174,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datatheorem%2Fstrongarm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datatheorem%2Fstrongarm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datatheorem%2Fstrongarm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datatheorem%2Fstrongarm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datatheorem","download_url":"https://codeload.github.com/datatheorem/strongarm/tar.gz/refs/heads/release","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247135147,"owners_count":20889421,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-09T14:02:32.604Z","updated_at":"2025-04-04T07:09:54.617Z","avatar_url":"https://github.com/datatheorem.png","language":"Python","readme":"strongarm\n-----------------\n\n![Build](https://travis-ci.com/datatheorem/strongarm-dataflow.svg?token=tAHgetxjRpiWdtpcAxqw\u0026branch=master)\n[![PyPI version](https://img.shields.io/pypi/v/strongarm-ios.svg)](https://pypi.org/project/strongarm-ios/)\n[![PyVersion badge](https://img.shields.io/badge/py-3.7%20%7C%203.8%20%7C%203.9-brightgreen.svg)](https://shields.io/)\n\n*strongarm* is a full-featured, cross-platform ARM64 Mach-O analysis library.\n\nstrongarm is production-ready and is used throughout DataTheorem's iOS static analyzer stack.\n\n![REPL example](repl_example.png)\n\nThis repo contains multiple tools to explore strongarm and the API. In the `scripts` folder,\nseveral popular Mach-O analysis tools have been reimplemented in strongarm, to demonstrate real API usage.\nAs strongarm is cross-platform, all of these tools are as well:\n\n- `strongarm-cli`: Static analysis REPL (try me!)\n- `class-dump`: Dump the Objective-C class information from a Mach-O with Objective-C declaration syntax\n- `insert_dylib`: Add a load command to a Mach-O\n- `dsc_symbolicate`: Given a dyld_shared_cache, generate a symbol map from the embedded system images\n- `nm`: List the symbol table of a Mach-O\n- `lipo`: Thin or fatten Mach-O files and slices\n- `hexdump`: Output the hex content of a byte range in a file\n- `strings`: Output the C-strings in a Mach-O\n- `dump_entitlements`: Print the code-signing information\n- `bitcode_retriever`: Extract the XAR archive containing LLVM bitcode from a Mach-O\n\nInstallation\n-----------\n\nstrongarm is supported on macOS and Linux.\n\n# Via pip\n\n    pip install strongarm-ios\n\n# Via git (for local development)\n\nTo setup a local environment:\n\n    git clone ...\n    cd strongarm\n    python -m venv .venv\n    source .venv/bin/activate\n    pip install -U pip setuptools wheel 'pip-tools\u003c7.0.0'\n    pip-sync requirements.txt requirements-dev.txt\n\nIf you modify requirements.in or requirements-dev.in:\n\n    pip-compile requirements.in\n    pip-compile requirements-dev.in\n    pip-sync requirements.txt requirements-dev.txt\n    git add requirements-dev.in requirements-dev.txt\n\nFeatures\n-----------\n\n- Access and cross-reference Mach-O info via an API\n- Dataflow analysis\n- Function-boundary detection\n\n### Mach-O parsing:\n\n- Metadata (architecture, endianness, etc)\n- Load commands\n- Symbol tables\n- String tables\n- Code signature\n- Dyld info\n- Objective-C info (classes, categories, protocols, methods, ivars, etc)\n\n### Mach-O analysis:\n\n- Cross-references (xrefs) of code and strings\n- Function boundary detection \u0026 disassembly\n- Track constant data movement in assembly\n- Dyld bound symbols \u0026 implementation stubs\n- Parse constant NSStrings and C strings\n- Basic block analysis\n\n### Mach-O editing:\n\n- Load command insertion\n- Write Mach-O structures\n- Byte-edit binaries\n\nQuickstart\n-----------\n\nPass an input file to `MachoParser`, which will read a Mach-O or FAT and provide access to individual `MachoBinary` slices.\n\n```python\nimport pathlib\nfrom strongarm.macho import MachoParser, MachoBinary\n\n# Load an input file\nparser = MachoParser(pathlib.Path(\"~/Documents/MyApp.app/MyApp\"))\n# Read the ARM64 slice and perform some operations\nbinary: MachoBinary = parser.get_arm64_slice()\nprint(binary.get_entitlements().decode())\nprint(hex(binary.section_with_name(\"__text\", \"__TEXT\").address))\n```\n\nAdvanced analysis\n-----------------\n\nSome APIs which require more memory or cross-referencing are available through `MachoAnalyzer`\n\n```python\nfrom pathlib import Path\nfrom strongarm.macho import MachoParser, MachoBinary, MachoAnalyzer\n\nmacho_parser = MachoParser(Path(\"~/Documents/MyApp.app/MyApp\"))\nbinary: MachoBinary = macho_parser.get_arm64_slice()\n# A MachoAnalyzer wraps a binary and allows deeper analysis\nanalyzer = MachoAnalyzer.get_analyzer(binary)\n\n# Find all calls to -[UIAlertView init] in the binary\nprint(analyzer.objc_calls_to([\"_OBJC_CLASS_$_UIAlertView\"], [\"init\"], requires_class_and_sel_found=False))\n\n# Print some interesting info\nprint(analyzer.imported_symbol_names_to_pointers)   # All the dynamically linked symbols which will be bound at runtime\nprint(analyzer.exported_symbol_names_to_pointers)   # All the symbols which this binary defines and exports\nprint(analyzer.get_functions())                     # Entry-point list of the binary. Each of these can be wrapped in an ObjcFunctionAnalyzer\nprint(analyzer.strings())                           # __cstring segment\nprint(analyzer.get_imps_for_sel(\"viewDidLoad\"))     # Convenience accessor for an ObjcFunctionAnalyzer\n\n# Print the Objective-C class information\nfor objc_cls in analyzer.objc_classes():\n    print(objc_cls.name)\n    for objc_ivar in objc_cls.ivars:\n        print(f\"\\tivar: {objc_ivar.name}\")\n    for objc_sel in objc_cls.selectors:\n        print(f\"\\tmethod: {objc_sel.name} @ {hex(objc_sel.implementation)}\")\n```\n\nCode analysis\n--------------\n\nOnce you have a handle to a `FunctionAnalyzer`, representing a source code function, you can analyze the code:\n\n```python\nfrom pathlib import Path\nfrom strongarm.macho import MachoParser, MachoBinary, MachoAnalyzer\nfrom strongarm.objc import ObjcFunctionAnalyzer\n\nmacho_parser = MachoParser(Path(\"~/Documents/MyApp.app/MyApp\"))\nbinary: MachoBinary = macho_parser.get_arm64_slice()\nanalyzer = MachoAnalyzer.get_analyzer(binary)\nfunction_analyzer = ObjcFunctionAnalyzer.get_function_analyzer_for_signature(binary, \"ViewController\", \"viewDidLoad\")\nprint(function_analyzer.basic_blocks)   # Find the basic block boundaries\n\n# Print some interesting info about Objective-C method calls in the function\nfor instr in function_analyzer.instructions:\n    if not instr.is_msgSend_call:\n        continue\n    \n    # In an Objective-C message send, x0 stores the receiver and x1 stores the selector being messaged.\n    classref = function_analyzer.get_register_contents_at_instruction(\"x0\", instr)\n    selref = function_analyzer.get_register_contents_at_instruction(\"x1\", instr)\n    \n    class_name = analyzer.class_name_for_class_pointer(classref.value)\n    selector = analyzer.selector_for_selref(selref.value).name\n   \n    # Prints \"0x100000000: _objc_msgSend(_OBJC_CLASS_$_UIView, @selector(alloc));\"\n    print(f\"{hex(instr.address)}: {instr.symbol}({class_name}, @selector({selector}));\")\n```\n\nModifying Mach-O's\n--------------\n\nYou can also modify Mach-O's by overwriting structures or inserting load commands:\n```python\nfrom pathlib import Path\nfrom strongarm.macho import MachoParser, MachoBinary\nfrom strongarm.macho.macho_definitions import MachoSymtabCommand\n\nmacho_parser = MachoParser(Path(\"~/Documents/MyApp.app/MyApp\"))\n# Overwrite a structure\nbinary: MachoBinary = macho_parser.get_arm64_slice()\nnew_symbol_table = MachoSymtabCommand()\nnew_symbol_table.nsyms = 0\nmodified_binary = binary.write_struct(new_symbol_table, binary.symtab.address, virtual=True)\n\n# Add a load command\nmodified_binary = modified_binary.insert_load_dylib_cmd(\"/System/Frameworks/UIKit.framework/UIKit\")\n\n# Write the modified binary to a file\nMachoBinary.write_binary(Path(__file__).parent / \"modified_binary\")\n```\n\n`MachoBinary` provides several functions to faciliate binary modifications.\n\nAs modifying a `MachoBinary` may invalidate its public attributes, these APIs return a new `MachoBinary` object,\nwhich is re-parsed with the edits.\n\n```python\n# Write raw bytes or Mach-O structures to a binary\nMachoBinary.write_bytes(self, data: bytes, address: int, virtual=False) -\u003e MachoBinary\nMachoBinary.write_struct(self, struct: Structure, address: int, virtual=False) -\u003e MachoBinary\n\n# Insert a load command\nMachoBinary.insert_load_dylib_cmd(dylib_path: str) -\u003e MachoBinary\n\n# Flush a modified slice to a thin Mach-O file, or a list of slices to a FAT Mach-O file:\nMachoBinary.write_binary(self, path: pathlib.Path) -\u003e None\n@staticmethod\nMachoBinary.write_fat(slices: List[MachoBinary], path: pathlib.Path) -\u003e None\n```\n\nTo make several modifications to a `MachoBinary` while triggering only one extra parse, use a `MachoBinaryWriter`:\n\n```python\nfrom pathlib import Path\nfrom strongarm.macho import MachoParser, MachoBinary\nfrom ctypes import c_uint64, sizeof\nfrom strongarm.macho.macho_binary_writer import MachoBinaryWriter\n\nmacho_parser = MachoParser(Path(\"~/Documents/MyApp.app/MyApp\"))\nbinary: MachoBinary = macho_parser.get_arm64_slice()\n# Initialise a batch binary writer\nwriter = MachoBinaryWriter(binary)\n\n# Make a series of changes to the binary\nwith writer:\n    for i in range(5):\n        writer.write_word(word=c_uint64(0xdeadbeef), address=0x1000 + (i * sizeof(c_uint64)), virtual=False)\n\n# `writer.modified_binary` contains the re-parsed binary containing the provided changes\n# Persist the modified binary to disk\nwriter.modified_binary.write_binary(Path(__file__) / \"modified_binary\")\n```\n\nLicense\n------------\n\nAGPL license\n","funding_links":[],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatatheorem%2Fstrongarm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatatheorem%2Fstrongarm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatatheorem%2Fstrongarm/lists"}