Debug variable resolution#

angr now support resolve source level variable (debug variable) in binary with debug information. This article will introduce you how to use it.

Setting up#

To use it you need binary that is compiled with dwarf debuging information (ex: gcc -g) and load in angr with the option load_debug_info. After that you need to run project.kb.dvars.load_from_dwarf() to set up the feature and we’re set.

Overall it looks like this:

# compile your binary with debug information
gcc -g -o debug_var debug_var.c
>>> import angr
>>> project = angr.Project('./examples/debug_var/simple_var', load_debug_info = True)
>>> project.kb.dvars.load_from_dwarf()

Core feature#

With things now set up you can view the value in the angr memory view of the debug variable within a state with: state.dvars['variable_name'].mem or the value that it point to if it is a pointer with: state.dvars['pointer_name'].deref.mem. Here are some example:

Given the source code in examples/debug_var/simple_var.c

#include<stdio.h>

int global_var = 100;
int main(void){
   int a = 10;
   int* b = &a;
   printf("%d\n", *b);
   {
      int a = 24;
      *b = *b + a;
      int c[] = {5, 6, 7, 8};
      printf("%d\n", a);
   }
   return 0;
}
# Get a state before executing printf(%d\n", *b) (line 7)
# the addr to line 7 is 0x401193 you can search for it with
>>> project.loader.main_object.addr_to_line
{...}
>>> addr = 0x401193
# Create an simulation manager and run to that addr
>>> simgr = project.factory.simgr()
>>> simgr.explore(find = addr)
<SimulationManager with 1 found>
>>> state = simgr.found[0]
# Resolve 'a' in state
>>> state.dvars['a'].mem
<int (32 bits) <BV32 0xa> at 0x7fffffffffeff30>
# Dereference pointer b
>>> state.dvars['b'].deref.mem
<int (32 bits) <BV32 0xa> at 0x7fffffffffeff30>
# It works as expected when resolving the value of b gives the address of a
>>> state.dvars['b'].mem
<reg64_t <BV64 0x7fffffffffeff30> at 0x7fffffffffeff38>

Side-note: For string type you can use .string instead of .mem to resolve it. For struct type you can resolve its member by .member("member_name").mem. For array type you can use .array(index).mem to access the element in array.

Variable visibility#

If you have many variable with the same name but in different scope, calling state.dvars['var_name'] would resolve the variable with the nearest scope.

Example:

# Find the addr before executing printf("%d\n", a) (line 12)
# with the same method to find addr
>>> addr = 0x4011e0
# Explore until find state
>>> simgr.move(from_stash='found', to_stash='active')
<SimulationManager with 1 active>
>>> simgr.explore(find = addr)
<SimulationManager with 1 found>
>>> state = simgr.found[0]
# Resolve 'a' in state before execute line 10
>>> state.dvars['a'].mem
<int (32 bits) <BV32 0x18> at 0x7fffffffffeff34>

Congratulation, you’ve now know how to resolve debug variable using angr, for more info check out the api-doc.