Understanding CFGs and Decompilation with angr


Intro to angr.


Challenges

Level 0: Generate CFG & List Functions

Load the binary with angr.Project and generate a CFG using project.analyses.CFGFast().

Your task is to:

  1. Generate CFG for the binary.
  2. Count the total number of functions discovered by the CFG
  3. Count the number of PLT stubs among those functions

Submit both numbers as a JSON list: [total_functions, plt_count].

Interesting APIs

  • project.analyses.CFGFast()
  • project.kb.functions - the knowledge base of discovered functions
  • func.is_plt - whether a function is a PLT stub

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 1: Count CFG Nodes

Load the binary and generate a CFG.

Your task is to count the total number of nodes in the CFG graph.

Submit the count as a single integer.

Interesting APIs

  • cfg.graph - the full CFG as a NetworkX DiGraph
  • cfg.graph.nodes() - all nodes in the CFG

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 2: Function Graph - Max In-Degree and Out-Degree

Load the binary and generate a CFG. Then access the function graph for state_machine via func.graph.

The function implements a state machine inside a while(1) loop. Each basic block in the function graph has in-degree and out-degree edges.

Your task is to find:

  1. The hex address of the node with the highest out-degree
  2. The hex address of the node with the highest in-degree

Submit both as a JSON list: [hex_max_out_degree, hex_max_in_degree].

Interesting APIs

  • project.kb.functions - look up functions by name
  • func.graph - the function's CFG as a NetworkX DiGraph
  • func.graph.out_degree(node) - out-degree of a node
  • func.graph.in_degree(node) - in-degree of a node

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 3: Callers of a Function

Load the binary and generate a CFG. Use the callgraph to find all user-defined functions that directly call the function printer.

Submit a sorted JSON list of the caller function names.

Interesting APIs

  • project.kb.callgraph - the call graph as a NetworkX DiGraph
  • Use NetworkX predecessors() to find callers

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 4: Finding Callers via CFG Edges

Load the binary and generate a CFG.

Your task is to find the instruction addresses of every call to the printer function by querying the CFG graph edges.

Submit the list of instruction addresses.

Interesting APIs

  • cfg.model.get_any_node(addr) - get the CFG node for a given address
  • cfg.graph.in_edges(node, data=True) - get all incoming edges to a node with edge data

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 5: Strings Referenced in a Function

Load the binary and generate a CFG with cross_references.

Your task is to find all constant strings referenced by the main function that are at least 8 bytes long. Submit them as a sorted JSON list.

Interesting APIs

  • project.analyses.CFGFast()
  • func.string_references() - get string references from a function

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 6: Instruction Addresses Referencing a String

Load the binary and generate a CFG with cross_references=True.

Your task is to find all instruction addresses that reference a string which contains "angr_is_best". Search through cfg.model.memory_data to locate the string, then use cross-references to find which instructions access it.

Submit a sorted JSON list of instruction addresses (as integers).

If the string appears at multiple memory locations, collect xrefs for all of them.

Interesting APIs

  • cfg.model.memory_data - dictionary of memory addresses to MemoryData objects
  • from angr.knowledge_plugins.cfg import MemoryDataSort
  • md.sort == MemoryDataSort.String - check if memory data is a string
  • md.content - the string content (as bytes)
  • project.kb.xrefs.get_xrefs_by_dst(addr) - cross-references to an address
  • xref.ins_addr - the instruction address of the xref

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 7: Calling Convention Analysis - Argument Count

Load the binary and generate a CFG. Then run CompleteCallingConventions analysis with recover_variables=True to recover function prototypes.

Your task is to determine how many arguments the function compute_result takes.

Submit the argument count as a single integer.

Interesting APIs

  • project.analyses.CompleteCallingConventions()
  • func.prototype - the recovered function prototype
  • func.prototype.args - list of recovered arguments

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 8: Count Global Variables (Decompiler)

Load the binary, generate a CFG, and decompile the main function. Make sure you normalize the cfg before decompiling it.

Your task is to count the number of global variables (external symbols) referenced in the main function.

Submit the count as a single integer.

Interesting APIs

  • project.analyses.CFGFast()
  • project.analyses.Decompiler(func, cfg=cfg.model) - decompile a function
  • dec.codegen.cexterns - list of external (global) variables used in the function

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 9: Count Local Variables (Decompiler)

Load the binary, generate a CFG, and decompile the main function.

Your task is to count the number of local variables in the decompiled main function.

Submit the count as a single integer.

Interesting APIs

  • project.analyses.CFGFast(normalize=True)
  • project.analyses.Decompiler(func, cfg=cfg.model)
  • dec.codegen.cfunc - the decompiled C function object
  • dec.codegen.cfunc.unified_local_vars - list of unified local variables

Variable unification is a decompiler process that reduces the number of variables to produce more readable output.

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

Level 10: Decompile & Count write() Calls

Load the binary, generate a CFG, and decompile the function logging_function.

Your task is to count how many times write() is called with file descriptor 4 as the first argument. Parse the decompiled text output to find these calls.

Submit the count as a single integer.

Interesting APIs

  • dec.codegen.text - the decompiled C code as a string

Connect with SSH

Link your SSH key, then connect with: ssh hacker@dojo.emotionlabs.io

30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score