I am trying to get all the library function calls that a binary performs in a preorder-DFT traversal of the CFG. I'm able to get the CFG like:

import sys, angr
import networkx as nx
proj = angr.Project(sys.argv[1],auto_load_libs=False)
cfg = proj.analyses.CFG().graph

I was able to get the CFG and I can even traverse it like this (Suppose I'm getting the correct main function's node):

s = nx.dfs_preorder_nodes(cfg,mainFuncNode)
nodes = []
    while True:

However I don't know how to get the function calls from the nodes (if they are actually doing it). I read some documentation and all I could come up with was:

for n in nodes:
    if n.is_simprocedure:
          print n.to_codenode().function

The output is all None and I'm sure that's wrong because the binary Is doing some I/O operations. So I expect to see something like:

  • libc_puts
  • libc_gets
  • ...

I would appreciate if you could give me some better pointers.

  • 3
    Hmm, on this SE site we have specialists of IDApro, Radare2 and others, but I did not see a lot of people from Angr... It would be nice to try to attract some community from them! :-) @anon : Thanks for your question about Angr! – perror Aug 16 '17 at 8:06
  • I know OP was already there but, just for others who browse here, there is a Slack channel with automatic invites generated. Hopefully the angr tag picks up here though. – sraboy Aug 16 '17 at 13:07

However I don't know how to get the function calls from the nodes

Are you saying you want to know which function a node belongs to, or which function a node is calling?

For the former, each block has a corresponding CFGNode object that are in the graph. Each CFGNode has a .function_address member, which tells you the address of the function that the node belongs to.

For the latter, every edge in the graph is labeled with properties, and we use 'jumpkind' to mark the type of an edge. An Ijk_Call jumpkind means that edge is a call from a block (or a node) to a function.

By the way, angr's CFG class is more than just the .graph member (which is a networkx.DiGraph instance). You might find it easier sometimes to directly work on CFG, instead of manually traversing the graph.

In addition, once a CFG is generated, you can access all functions by accessing CFG.functions. Each Function instance has two intra-function graphs associated with it: a .graph and a .transition_graph. You may find it easier to work with than traversing the CFG of the whole binary.

In the end, if you like GUI, and you have a lot of patience, you might want to give angr Management a try.

  • Thank you for the pointers. Yes I meant the latter. I also want to know. How does the CFG represent a shared library function call. Does it have a jump edge to that shared lib's location(@ptr actually) with the respective symbol name? – anon Aug 16 '17 at 15:09
  • Thanks for the GUI I was able to get the functions I expected like memset, memcpy, _controlfp, etc... However I need to know if I can find these in the CFG nodes. – anon Aug 16 '17 at 15:44
  • It does have edges pointing to functions in shared libraries, even if the library is not loaded with the project. – Fish Aug 16 '17 at 20:27
  • Thank you! Do you know how I can see if that node is a shared lib function? – anon Aug 16 '17 at 21:09
  • First get the address of the node. If it is a function, you can get the Function instance by cfg.functions[addr], and then access .binary and .binary_name. If it is not a function, you can call project.loader.whats_at(addr). – Fish Aug 16 '17 at 22:43

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.