x86_64: Phase 4

In [1]:
# Imports

import angr
import claripy

We push the input values to the stack and then pop them right before the function exits. Since no value is pushed onto the stack during the execution of the function, we can safely manipulate the stack in the way we planned.

Load the binary.

In [2]:
project = angr.Project('bomb64')

Define the address for the start of the execution path.

In [3]:
# The address where the symbolic execution shall begin. It is the beginning of the func4 function.
addr_start = 0x400FCE

# The address of the return of func4.
addr_target = 0x401007

# The address of the first instruction of the explode_bomb function, which is to be avoided.
addr_bomb = 0x40143A

Define a blank state for the simulation, a state with most of it's data uninitialized. Pass the address where the state is initialized, along with the user input to be given via standard input.

In [4]:
state = project.factory.blank_state(addr=addr_start)

Move a symbolic value into the relevant register.

In [5]:
num_1 = claripy.BVS("num_1", 32)

Setup the state of the registers as they were right before the function call.

In [6]:
state.regs.edx = 0xE
state.regs.esi = 0
state.regs.edi = num_1

Create a simulation manager with this blank state that would help us manage the symbolic execution.

In [7]:
simgr = project.factory.simulation_manager(state)

We call the explore method of the simulation manager, tasked with finding an execution path that reaches the target address and avoids the address which explodes the bomb.

In [8]:
simgr.explore(find=addr_target, avoid=addr_bomb, enable_veritesting=True)
WARNING | 2020-06-15 04:17:44,537 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2020-06-15 04:17:44,538 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2020-06-15 04:17:44,539 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
WARNING | 2020-06-15 04:17:44,540 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2020-06-15 04:17:44,542 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages.
WARNING | 2020-06-15 04:17:44,544 | angr.state_plugins.symbolic_memory | Filling register 36 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:44,549 | angr.state_plugins.symbolic_memory | Filling register 68 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:44,558 | angr.state_plugins.symbolic_memory | Filling register 76 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
Out[8]:
<SimulationManager with 3 active, 1 found>

We dereference the execution path "found" by the simulation manager and dump the stack.

In [9]:
found = simgr.found[0]
In [10]:
found.solver.eval(num_1)
Out[10]:
7

This is the first number. Since we already know the second number is 0, the user input that passes the fourth phase is 7 0

Let's progressively add constraints to reach different possible end values. Add the constraint for the new value to be smaller than the one in previous iteration.

In the form: state.solver.add(num_1 < x), where x is the integer value discovered in the previous iteration.

Let's put it in a loop to get the all the values.

In [11]:
x = 10
while x > 0:
    
    # Load the binary.
    project = angr.Project('bomb64')

    # Init the addresses.
    addr_start = 0x400FCE
    addr_target = 0x401007
    addr_bomb = 0x40143A

    
    # Init a blank state.
    state = project.factory.blank_state(addr=addr_start)

    # Define the symbolic value.
    num_1 = claripy.BVS("num_1", 32)

    # Setup the registers.
    state.regs.edx = 0xE
    state.regs.esi = 0
    state.regs.edi = num_1
    
    # Add the constraint.
    state.solver.add(num_1<x)
    
    # Init the simulation manager.
    simgr = project.factory.simulation_manager(state)

    # Let Angr explore.
    simgr.explore(find=addr_target, avoid=addr_bomb, enable_veritesting=True)

    # Get the answer from the found state.
    found = simgr.found[0]
    x = found.solver.eval(num_1)
    print(x, "0")
    
WARNING | 2020-06-15 04:17:46,821 | angr.state_plugins.symbolic_memory | Filling register 36 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:46,825 | angr.state_plugins.symbolic_memory | Filling register 68 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:46,836 | angr.state_plugins.symbolic_memory | Filling register 76 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
7 0
WARNING | 2020-06-15 04:17:48,801 | angr.state_plugins.symbolic_memory | Filling register 36 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:48,802 | angr.state_plugins.symbolic_memory | Filling register 68 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:48,813 | angr.state_plugins.symbolic_memory | Filling register 76 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
3 0
WARNING | 2020-06-15 04:17:51,163 | angr.state_plugins.symbolic_memory | Filling register 36 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:51,164 | angr.state_plugins.symbolic_memory | Filling register 68 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:51,170 | angr.state_plugins.symbolic_memory | Filling register 76 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
1 0
WARNING | 2020-06-15 04:17:53,566 | angr.state_plugins.symbolic_memory | Filling register 36 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:53,567 | angr.state_plugins.symbolic_memory | Filling register 68 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
WARNING | 2020-06-15 04:17:53,572 | angr.state_plugins.symbolic_memory | Filling register 76 with 4 unconstrained bytes referenced from 0x400fce (func4+0x0 in bomb64 (0x400fce))
0 0

And there we go, more possible end values.