I am using latest version of angr (9,0,'gitrollling'). [I get the same behavior with angr version (9, 0, 4663)].
Using gcc 9.3.0 I created an ELF binary for this simple C program:
float func3(float y) {
float temp = 5.5; // expected angr to find this constant
return y + temp;
}
int main(int argc, char *argv[]) {
float ans;
ans = func3(2.2); // expected angr to find this constant
}
I then used angr to extract the constants in my functions (namely 'func3' and 'main') as well as number of arguments for the functions. Unfortunately the answers I get back for constants ("const" in output below) or for "argc" make no sense. I get:
name main const [8, 32, 8, 32, 18446744073709551596, 18446744073709551584, 0, 4202504, 4202504,
8, 4198767, 128, 4198697, 18446744073709551612, 0, 8, 8, 128] argc -1
name func3 const [8, 18446744073709551596, 4202500, 4202500, 18446744073709551612,
18446744073709551596, 0, 18446744073709551612, 8, 8, 128] argc -1
My angr code:
#!/usr/bin/env python3
import angr
from angrutils import *
def get_attributes(cfg, addr):
if addr in cfg.kb.functions:
func = cfg.kb.functions.get_by_addr(addr)
if func:
name = func.demangled_name
if name != 'main' and name != 'func3':
return # only care about these 2 funcs
const = func.code_constants
argc = len(func.arguments) if func.arguments else -1
print(' name %s const %s argc %s ' % (name, const, argc))
return
proj = angr.Project('simple', main_opts={'backend': 'elf'}, load_options={'auto_load_libs':False})
main = proj.loader.main_object.get_symbol('main')
start_state = proj.factory.blank_state(addr=main.rebased_addr)
start_state.stack_push(0x0)
with hook0(proj):
cfg = proj.analyses.CFGFast() # using CFGEmulated() also does not change the answer!
#cfg = proj.analyses.CFGEmulated(fail_fast=False, starts=[main.rebased_addr], context_sensitivity_level=1, enable_function_hints=False, keep_state=True, enable_advanced_backward_slicing=False, enable_symbolic_back_traversal=False,normalize=True)
d=dict()
for src, dst in cfg.kb.functions.callgraph.edges():
if not d.get(src): # only need to do this once.
src_attr = get_attributes(cfg, src)
d[src] = True # mark completed
if not d.get(dst): # only need to do this once.
dst_attr = get_attributes(cfg, dst)
d[dst] = True # mark completed
Where am I going wrong?
I have no experience with angr, but based on inspecting the assembly generated for your program, I have some hypotheses for what went wrong:
func3
has no side effects andmain
does not use the value ofans
, so the compiler can eliminate the call tofunc3
entirely, e.g. on x86-64 I get this formain
:So the constant 2.2 may well not be in the executable at all.
Floating point constants usually have to be emitted into memory and loaded by reference, e.g. on x86-64 I get this assembly for
func3
:In a fully linked executable the cross-reference
.LC0
becomes a relative offset:It is possible that angr does not recognize this offset as a constant to be extracted, or that it can only extract this offset and not the value in .rodata that it refers to. And even if it could pull out the value in .rodata, the only way it could know that the value should be interpreted as a single-precision float rather than an integer, is if it decoded the instruction that uses the value.