Created
November 15, 2021 02:22
-
-
Save Theldus/4c6c9770ee30909d49a240da9a29fa24 to your computer and use it in GitHub Desktop.
Recursively finds dependencies in Mach-O binaries, output a tree-like structure or a graph file via Graphviz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# RecOTool.py -- Recursively finds dependencies in Mach-O binaries | |
# | |
# This is free and unencumbered software released into the public domain. | |
# | |
# Run with Python2... I don't know why do not works with 3.... | |
# | |
# Usage | |
# python RecOTool.py /usr/bin/something -o deps.png (if Graphviz installed) | |
# python RecOTool.py /usr/bin/something -v (output only text, verbose mode | |
# enabled) | |
# python RecOTool.py /usr/bin/something -v -o deps.png | |
# (outputs a text output & a graph file) | |
import sys | |
import subprocess | |
import argparse | |
done = set() | |
dotfile = None | |
args = None | |
id = 0 | |
def printid(s): | |
print(' '*id + s) | |
def add_dot(n1, n2): | |
s1 = n1.split("/")[-1] | |
s2 = n2.split("/")[-1] | |
dotfile.write("\"{}\" -> \"{}\"\n".format(s1, s2)) | |
def otool(s): | |
if args.verbose: | |
printid('otooling {}'.format(s)) | |
printid('--------') | |
libs = set() | |
o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE) | |
for l in o.stdout: | |
if l[0] == '\t': | |
libs.add(l.split(' ', 1)[0][1:]) | |
return libs | |
def search(targ): | |
global id | |
global done | |
libs = otool(targ) | |
libs.difference_update(done) | |
for l in libs: | |
if args.verbose: | |
printid(l) | |
done.add(l) | |
add_dot(targ, l) | |
id=id+args.indent | |
search(l) | |
id=id-args.indent | |
def parse_args(): | |
p = argparse.ArgumentParser( | |
description='Recursively finds dependencies in a Mach-O binary') | |
p.add_argument("binary", type=str, help="Binary to be analyzed") | |
p.add_argument('-v', '--verbose', default=False, action="store_true", | |
help='Enable verbose output') | |
p.add_argument('-i', '--indent', help='Set indent level (default=4)', | |
type=int, default=4) | |
p.add_argument('-d', '--dot', help='Set output dot file', type=str, | |
default='out.dot') | |
p.add_argument('-o', '--output', | |
help='Enable and set output image filename (PNG)', type=str, | |
default='none') | |
return p.parse_args() | |
def main(): | |
global dotfile | |
global args | |
args = parse_args() | |
# Open dot file and search recursively | |
dotfile = open(args.dot, "w+") | |
dotfile.write("digraph deps {\n") | |
search(args.binary) | |
dotfile.write("}\n") | |
dotfile.close() | |
# Output PNG file | |
if args.output != 'none': | |
try: | |
subprocess.call(['dot', '-Tpng', args.dot, '-o', args.output]) | |
except: | |
sys.stderr.write( | |
"Unable to create graph, please check if Graphviz is " | |
"properly installed!\n") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment