.. _getting_started-user-guide: ========== User Guide ========== Commands ======== Elfcall provides the following sub-commands via the ``elfcall`` command line client. Gen --- Gen is used to generate a symbol graph. This is arguably not going to be the best visualization for really large trees, however in this case you likely want to use the client to generate the same graph (we will show both). First, let's combine some example code from the repository (shown how to clone in :ref:`getting_started-installation`. .. code-block:: console cd data/ make cd ../ This will generate ``data/libfoo.so``, a small library we can use for demonstration. console ^^^^^^^ The default output prints to the console. Let's run gen and see what happens! .. code-block:: console $ elfcall gen data/libfoo.so The output is basic in that you will see the linked library followed by undefined symbols we needed. It's implied that these are needed symbols of ``libfoo.so``. .. code-block:: console $ elfcall gen data/libfoo.so ==/usr/lib/x86_64-linux-gnu/libstdc++.so.6== _ZNSt8ios_base4InitC1Ev _ZNSt8ios_base4InitD1Ev ==/lib/i386-linux-gnu/libc.so.6== __cxa_atexit __cxa_finalize The above shows where the undefined symbols in our binary of interest are found. Note that this isn't a graph, hence why we don't see any kind of entry for the main binary. You can add ``--debug`` to see what is searched and when symbols are found: .. code-block:: console $ elfcall --debug gen data/libfoo.so Looking for libstdc++.so.6 Found _ZNSt8ios_base4InitC1Ev -> libstdc++.so.6 Found _ZNSt8ios_base4InitD1Ev -> libstdc++.so.6 Looking for libm.so.6 Looking for libgcc_s.so.1 Looking for libc.so.6 Found __cxa_finalize -> libc.so.6 Found __cxa_atexit -> libc.so.6 Looking for ld-linux-x86-64.so.2 ==/usr/lib/x86_64-linux-gnu/libstdc++.so.6== _ZNSt8ios_base4InitC1Ev _ZNSt8ios_base4InitD1Ev ==/lib/x86_64-linux-gnu/libc.so.6== __cxa_atexit __cxa_finalize This is actually the default output "format" for gen - printing to the console. There are dIfferent formats for graphs are shown below. To do this same generation in Python: .. code-block:: python from elfcall.main import BinaryInterface cli = BinaryInterface("data/libfoo.so") # These are the same cli.gen() cli.gen(fmt="console") # Instead of printing, get json output results = cli.gen_output() The output "results" will have sets of imported and exported symbols, and then a lookup of what is found and where. Note that this output is consistent between all graph types, hence why the output format is not needed. text ^^^^ Text output is akin to console, but we are explicitly printing basic entities for a graph. We are generating the data as if we are writing nodes and relationships in a graph, and (from Python) there is even an optional boolean to indicate we want to see ELF and SYMBOL entries on their own (default is False). This means we will see what the binary of interest is linked to, and a logical relationship for symbols and libs - one library will export a symbol, and another will need it. .. code-block:: console $ elfcall gen data/libfoo.so --fmt text /home/vanessa/Desktop/Code/elfcall/data/libfoo.so LINKSWITH /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /home/vanessa/Desktop/Code/elfcall/data/libfoo.so LINKSWITH /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 LINKSWITH libm.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 LINKSWITH /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 LINKSWITH ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 LINKSWITH libgcc_s.so.1 /lib/x86_64-linux-gnu/libc.so.6 LINKSWITH ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 EXPORTS _ZNSt8ios_base4InitC1Ev /usr/lib/x86_64-linux-gnu/libstdc++.so.6 EXPORTS _ZNSt8ios_base4InitD1Ev /lib/x86_64-linux-gnu/libc.so.6 EXPORTS __cxa_finalize /lib/x86_64-linux-gnu/libc.so.6 EXPORTS __cxa_atexit /home/vanessa/Desktop/Code/elfcall/data/libfoo.so NEEDS __cxa_finalize /home/vanessa/Desktop/Code/elfcall/data/libfoo.so NEEDS __cxa_atexit /home/vanessa/Desktop/Code/elfcall/data/libfoo.so NEEDS _ZNSt8ios_base4InitC1Ev /home/vanessa/Desktop/Code/elfcall/data/libfoo.so NEEDS _ZNSt8ios_base4InitD1Ev For the above, we might be in trouble if the number of ``NEEDS`` didn't equal the number of ``EXPORTS`` as we would be missing a symbol. To pipe to file: .. code-block:: console $ elfcall gen data/libfoo.so --fmt text > data/examples/text/graph.txt From within Python you might do: .. code-block:: python from elfcall.main import BinaryInterface cli = BinaryInterface("data/libfoo.so") cli.gen(fmt="text") cypher ^^^^^^ Cypher is the query format for Neo4j, the graph database. .. code-block:: console $ elfcall gen data/libfoo.so --fmt cypher CREATE (omyaovuh:ELF {name: 'libfoo.so', label: 'libfoo.so'}), (ilfrbqrc:ELF {name: 'libstdc++.so.6', label: 'libstdc++.so.6'}), (vyiefgcr:ELF {name: 'libm.so.6', label: 'libm.so.6'}), (gnxoyhkm:ELF {name: 'libc.so.6', label: 'libc.so.6'}), (fvynaahi:ELF {name: 'ld-linux-x86-64.so.2', label: 'ld-linux-x86-64.so.2'}), (hsrlkhie:ELF {name: 'libgcc_s.so.1', label: 'libgcc_s.so.1'}), (kgyffmqn:SYMBOL {name: '__cxa_finalize', label: '__cxa_finalize', type: 'FUNC'}), (bieoloch:SYMBOL {name: '_ZNSt8ios_base4InitC1Ev', label: '_ZNSt8ios_base4InitC1Ev', type: 'FUNC'}), (owpwqsyl:SYMBOL {name: '__cxa_atexit', label: '__cxa_atexit', type: 'FUNC'}), (stndoxns:SYMBOL {name: '_ZNSt8ios_base4InitD1Ev', label: '_ZNSt8ios_base4InitD1Ev', type: 'FUNC'}), (omyaovuh)-[:LINKSWITH]->(ilfrbqrc), (omyaovuh)-[:LINKSWITH]->(lxtmuvsv), (ilfrbqrc)-[:LINKSWITH]->(vyiefgcr), (ilfrbqrc)-[:LINKSWITH]->(gnxoyhkm), (ilfrbqrc)-[:LINKSWITH]->(fvynaahi), (ilfrbqrc)-[:LINKSWITH]->(hsrlkhie), (lxtmuvsv)-[:LINKSWITH]->(fvynaahi), (ilfrbqrc)-[:EXPORTS]->(bieoloch), (ilfrbqrc)-[:EXPORTS]->(stndoxns), (lxtmuvsv)-[:EXPORTS]->(kgyffmqn), (lxtmuvsv)-[:EXPORTS]->(owpwqsyl), (omyaovuh)-[:NEEDS]->(kgyffmqn), (omyaovuh)-[:NEEDS]->(owpwqsyl), (omyaovuh)-[:NEEDS]->(bieoloch), (omyaovuh)-[:NEEDS]->(stndoxns); What you are seeing above is a definition of node and relationships. You can pipe to file: .. code-block:: console $ elfcall gen data/libfoo.so --fmt cypher > data/examples/cypher/graph.cypher $ elfcall gen /usr/bin/vim --fmt cypher > data/examples/cypher/graph-vim.cypher If you test the output in `the Neo4J sandbox `_ by first running the code to generate nodes and then doing: .. code-block:: console MATCH (n) RETURN (n) You should see: .. image:: https://raw.githubusercontent.com/vsoch/elfcall/main/data/examples/cypher/graph.png :alt: Cypher Graph From within Python you might do: .. code-block:: python from elfcall.main import BinaryInterface cli = BinaryInterface("data/libfoo.so") cli.gen(fmt="cypher") dot ^^^ Dot is probably one of my favorite because you can use the `dot `_ command line tool to make pretty beautiful plots! .. code-block:: console $ elfcall gen data/libfoo.so --fmt dot And here is how to generate a png or svg: .. code-block:: console $ elfcall gen data/libfoo.so --fmt dot > data/examples/dot/graph.dot $ dot -Tpng < data/examples/dot/graph.dot > data/examples/dot/graph.png $ dot -Tsvg < data/examples/dot/graph.dot > data/examples/dot/graph.svg That generates this beauty! .. image:: https://raw.githubusercontent.com/vsoch/elfcall/main/data/examples/dot/graph.png :alt: Dot Graph Note that this format isn't great for large graphs. Gexf (NetworkX) ^^^^^^^^^^^^^^^ If you want to use networkX or Gephi or `a viewer `_ you can generate output as follows: .. code-block:: console $ elfcall gen data/libfoo.so --fmt gexf $ elfcall gen data/libfoo.so --fmt gexf > data/examples/gexf/graph.xml To use the viewer, you'll first need to import into Gephi so the nodes have added spatial information. Without this information, you won't see them in the UI. You can then do the following: .. code-block:: console $ here=$PWD $ cd /tmp $ git clone https://github.com/raphv/gexf-js $ cd gexf-js # The file we generated above, we copy over the example so we don't have # to edit config.js $ cp $here/data/examples/gexf/graph.xml miserables.gexf And then run the server! .. code-block:: console $ python -m http.server 9999 As an alternative, `networkx `_ can also read in the gexf file: .. code-block:: python import matplotlib.pyplot as plt import networkx as nx graph = nx.read_gexf('data/examples/gexf/graph.xml') nx.draw(graph, with_labels=True, font_weight='bold') plt.show() Tree ---- Elfcall also lets you generate a tree of the library paths parsed: .. code-block:: console $ elfcall tree data/libfoo.so libstdc++.so.6 [x86_64-linux-gnu.conf] ld-linux-x86-64.so.2 [x86_64-linux-gnu.conf] libm.so.6 [x86_64-linux-gnu.conf] libgcc_s.so.1 [x86_64-linux-gnu.conf] libc.so.6 [x86_64-linux-gnu.conf] Notice the right column, the source of finding a file? This is the source lookup I was talking about in :ref:`getting_started-background`. You can verify needed using ``readelf``: .. code-block:: console $ readelf -a data/libfoo.so | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6] 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] and here is an example with vim: .. code-block:: console $ elfcall tree /usr/bin/vim libm.so.6 [x86_64-linux-gnu.conf] ld-linux-x86-64.so.2 [x86_64-linux-gnu.conf] libtinfo.so.6 [x86_64-linux-gnu.conf] libselinux.so.1 [x86_64-linux-gnu.conf] libpcre2-8.so.0 [x86_64-linux-gnu.conf] libcanberra.so.0 [x86_64-linux-gnu.conf] libvorbisfile.so.3 [x86_64-linux-gnu.conf] libvorbis.so.0 [x86_64-linux-gnu.conf] libogg.so.0 [x86_64-linux-gnu.conf] libtdb.so.1 [x86_64-linux-gnu.conf] libltdl.so.7 [x86_64-linux-gnu.conf] libacl.so.1 [x86_64-linux-gnu.conf] libgpm.so.2 [x86_64-linux-gnu.conf] libdl.so.2 [x86_64-linux-gnu.conf] libpython3.8.so.1.0 [x86_64-linux-gnu.conf] libexpat.so.1 [x86_64-linux-gnu.conf] libz.so.1 [x86_64-linux-gnu.conf] libutil.so.1 [x86_64-linux-gnu.conf] libpthread.so.0 [x86_64-linux-gnu.conf] libc.so.6 [x86_64-linux-gnu.conf] And the needed headers: .. code-block:: console $ readelf -a /usr/bin/vim | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] 0x0000000000000001 (NEEDED) Shared library: [libtinfo.so.6] 0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1] 0x0000000000000001 (NEEDED) Shared library: [libcanberra.so.0] 0x0000000000000001 (NEEDED) Shared library: [libacl.so.1] 0x0000000000000001 (NEEDED) Shared library: [libgpm.so.2] 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2] 0x0000000000000001 (NEEDED) Shared library: [libpython3.8.so.1.0] 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]