angr_file.ipynb

General definitions

I recommend using a pypy virtual environment to run angr.

# Imports
import angr
import claripy
import logging
logging.getLogger('angr').setLevel('INFO')

# Parameters
binary_path = './Exploit_Me(if_you_can)'

useVeritesting = False # Can speed up the analysis when there is a lot of branching, but causes instability
useUnicorn = True      # Unicorn engine is faster than the default engine, but can cause instability

# (OPTIONAL) address parameters
base_addr = 0x00
state_addr = 0x00

# (OPTIONAL) input parameters, when the input length is known
max_input_len = 0x40

# (OPTIONAL) goal address parameters
success_addr = 0x00 
fail_addr = 0x00

# (OPTIONAL) goal strings in stdout
success_stdout = [b'Well']
fail_stdout = [b'Try again!']


def is_successful(state):
    stdout_output = state.posix.dumps(1)
    return any(s in stdout_output for s in success_stdout)

def is_failed(state):
    stdout_output = state.posix.dumps(1)
    return any(f in stdout_output for f in fail_stdout)


# Create the project
if base_addr:
    p = angr.Project(binary_path, main_opts={'base_addr': base_addr})
else:
    p = angr.Project(binary_path)

# (OPTIONAL) Hook ptrace to return 0. Avoid detection by anti-debugging techniques with ptrace.
#p.hook_symbol('ptrace', angr.SIM_PROCEDURES['stubs']['ReturnUnconstrained'](return_value=0))

Create the simulation manager and explore

emulated_filename = ".m.key"
file_size = 0x14
file_content = [claripy.BVS('file_%d' % i, 8) for i in range(file_size)]
sf = angr.SimFile(emulated_filename, size=0x14, content=claripy.Concat(*file_content))

# Create the simulation manager
state = p.factory.entry_state(args=[binary_path], stdin="", add_options=({angr.options.UNICORN} if useUnicorn else {}))
state.fs.insert(emulated_filename, sf)

# (OPTIONAL) Conditions on the file_content:
# for c in file_content:
#     state.add_constraints(c >= ord(' '))
#     state.add_constraints(c <= ord('~'))

sm = p.factory.simulation_manager(state, veritesting=useVeritesting)
# Explore the binary
if success_addr and fail_addr:
    sm.explore(find=success_addr, avoid=fail_addr)
else:
    sm.explore(find=is_successful, avoid=is_failed)
sm

Parse the results

if sm.found:
    found = sm.found[0]
    data, actual_size, new_pos = sm.found[0].fs.get(emulated_filename).read(0, 0x14)
    output = sm.found[0].solver.eval(data, cast_to=bytes)
    print("FOUND")
    print("In:", found.posix.dumps(0))
    print("Out:", found.posix.dumps(1))
    print("File content:", output)
    print()

if sm.deadended:
    print("DEADENDED")
    for deadended in sm.deadended.state:
        print("In:", deadended.posix.dumps(0))
        print("Out:", deadended.posix.dumps(1))
        print()

if sm.errored:
    print("ERRORED")
    for errored in sm.errored.state:
        print(sm.errored)
        print("In:", errored.posix.dumps(0))
        print("Out:", errored.posix.dumps(1))
        print()


# Save the results to a local file
# with open("emulated_filename", 'wb') as f:
#     f.write(output)