Skip to content

01 Angr Avoid

One down! Time to look at 01_angr_avoid.

Analyzing the Binary

As before, we are going to start with the binary in a decompiler. I had trouble opening this up in any decompiler as the file was just too large. I finally got it open in both Cutter and Ghidra by tweaking a setting. I changed the Max Instructions per Function setting to something larger. You can find this by going to Edit > Tool Options > Decompiler

Ghidra Setting Max Instructions per Function

In Binary Ninja I just forced the analyze function.

As with the first one, we are looking for an eight character input.

Eight Characters

It then loops the input and calls complex_function on each character.

Complex Function

It then compares the output using a bunch of if statements. A lot of them result in the avoid_me function being called.

Avoid Function

If we are able to navigate the branches we can arrive at a maybe_good function. As shown it does a couple of more checks and then will print Good Job. If successful.

Maybe Good Function

I think this does a great job describing the need for an automated way to reverse binaries. There is no way I am going through all of those branches to find the winning password. Luckily I don't have to!

Building the Script

The script starts out as before, by defining the initial state and then creating a simulation manager.

path_to_binary = "./01_angr_avoid"
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state(
    add_options={
        angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
        angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
    },
)
simulation = project.factory.simgr(initial_state)

This time, we want the simulation manager to end any paths that reach the avoid_me function. This is important to keep the number of paths from exploding. Anytime the simulation manager reaches this point, we want it to discard the path.

Avoid Address

In addition, we want to tell the simulation manager how to tell if its found the "good" function.

Win Address

We set these and then let it go forth and pillage.

print_good_address = 0x080485E5
will_not_succeed_address = 0x080485AB
simulation.explore(find=print_good_address, avoid=will_not_succeed_address)

That is it. On to the next one.

Final Script

import sys

import angr
import pwn


def run_binary(solution, path_to_binary):
    if type(solution) == str:
        solution = bytes(solution, "utf-8")
    print(f"[+] Solution found: {solution.decode()}")
    print("    [|] Running binary")
    elf = pwn.ELF(path_to_binary, checksec=False)
    pty = pwn.process.PTY
    io = elf.process(stdin=pty, stdout=pty, level="warn")
    io.recvuntil(b":")
    io.sendline(solution)
    output = io.recvline().decode().splitlines()[0].strip()
    print(f"    [+] Output: {output}")


def main():
    path_to_binary = "./01_angr_avoid"
    project = angr.Project(path_to_binary)
    initial_state = project.factory.entry_state(
        add_options={
            angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
            angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
        },
    )
    simulation = project.factory.simgr(initial_state)
    print_good_address = 0x080485E5
    will_not_succeed_address = 0x080485AB
    simulation.explore(find=print_good_address, avoid=will_not_succeed_address)

    if simulation.found:
        solution_state = simulation.found[0]
        solution = solution_state.posix.dumps(sys.stdin.fileno()).decode()
        run_binary(solution, path_to_binary)
    else:
        raise Exception("Could not find the solution")


if __name__ == "__main__":
    main()
Back to top