angr/tests/sim/test_emulator.py
Kevin Phoenix e0a6a82282
Add Emulator and ConcreteEngine; Adapt IcicleEngine to ConcreteEngine (#5529)
* Add Emulator and ConcreteEngine; Adapt IcicleEngine to ConcreteEngine

* Remove redundant assertions

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Move test_emulator.py to tests/sim/

* Fix missed rename

* Add HeavyConcreteState to __all__

* Fix lint

* Put it in the right place

* Move it to the top level

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-13 12:46:02 -07:00

171 lines
6.4 KiB
Python

"""
test_emulator.py - Unit tests for the Emulator class in angr.
These tests mirror TestFauxware and TestBreakpoints from test_icicle.py, but use the Emulator interface.
"""
from __future__ import annotations
import os
from unittest import TestCase
import angr
from angr import sim_options as o
from angr.emulator import Emulator, EmulatorStopReason
from angr.engines.icicle import UberIcicleEngine
from tests.common import bin_location
# pylint: disable=no-self-use
class TestFauxware(TestCase):
"""Integration tests executing the fauxware binary using the Emulator class."""
def _run_fauxware(self, arch):
project = angr.Project(os.path.join(bin_location, "tests", arch, "fauxware"), auto_load_libs=False)
init_state = project.factory.entry_state(
stdin=b"username\nSOSNEAKY\n",
args=["fauxware"],
remove_options={*o.symbolic},
add_options={o.ZERO_FILL_UNCONSTRAINED_MEMORY, o.ZERO_FILL_UNCONSTRAINED_REGISTERS},
)
engine = UberIcicleEngine(project)
emulator = Emulator(engine, init_state.copy())
stop_reason = emulator.run()
assert stop_reason == EmulatorStopReason.EXIT
# Check that the program has printed the expected output
assert b"Welcome to the admin console, trusted user!\n" in emulator.state.posix.dumps(1)
def test_fauxware_i386(self):
self._run_fauxware("i386")
def test_fauxware_x86_64(self):
self._run_fauxware("x86_64")
def test_fauxware_armel(self):
self._run_fauxware("armel")
def test_fauxware_armhf(self):
self._run_fauxware("armhf")
def test_fauxware_mips(self):
self._run_fauxware("mips")
def test_fauxware_mipsel(self):
self._run_fauxware("mipsel")
class TestBreakpoints(TestCase):
"""Unit tests for breakpoint functionality in the Emulator class."""
def test_add_breakpoint(self):
"""Test adding and hitting a breakpoint."""
shellcode = "mov x0, 0x1; mov x1, 0x2; add x2, x0, x1; mov x3, 0x3"
project = angr.load_shellcode(shellcode, "aarch64")
engine = UberIcicleEngine(project)
init_state = project.factory.blank_state(
remove_options={*o.symbolic},
add_options={o.ZERO_FILL_UNCONSTRAINED_MEMORY, o.ZERO_FILL_UNCONSTRAINED_REGISTERS},
)
emulator = Emulator(engine, init_state.copy())
# Add breakpoint at the third instruction (add x2, x0, x1)
breakpoint_addr = project.entry + 8
emulator.add_breakpoint(breakpoint_addr)
# Run until breakpoint
stop_reason = emulator.run()
assert stop_reason == EmulatorStopReason.BREAKPOINT
assert emulator.state.addr == breakpoint_addr
assert emulator.state.regs.x0.concrete_value == 1
assert emulator.state.regs.x1.concrete_value == 2
# Continue execution with instruction limit
stop_reason = emulator.run(num_inst=2)
assert stop_reason == EmulatorStopReason.INSTRUCTION_LIMIT
assert emulator.state.regs.x2.concrete_value == 3
assert emulator.state.regs.x3.concrete_value == 3
def test_remove_breakpoint(self):
"""Test removing a breakpoint."""
shellcode = "mov x0, 0x1; mov x1, 0x2; add x2, x0, x1; mov x3, 0x3"
project = angr.load_shellcode(shellcode, "aarch64")
engine = UberIcicleEngine(project)
init_state = project.factory.blank_state(
remove_options={*o.symbolic},
add_options={o.ZERO_FILL_UNCONSTRAINED_MEMORY, o.ZERO_FILL_UNCONSTRAINED_REGISTERS},
)
emulator = Emulator(engine, init_state.copy())
breakpoint_addr = project.entry + 8 # add x2, x0, x1
emulator.add_breakpoint(breakpoint_addr)
emulator.remove_breakpoint(breakpoint_addr)
# Run all instructions, breakpoint should not be hit
stop_reason = emulator.run(num_inst=4)
assert stop_reason == EmulatorStopReason.INSTRUCTION_LIMIT
assert emulator.state.regs.x2.concrete_value == 3
assert emulator.state.regs.x3.concrete_value == 3
def test_multiple_breakpoints(self):
"""Test multiple breakpoints."""
shellcode = "mov x0, 0x1; mov x1, 0x2; add x2, x0, x1; mov x3, 0x3; sub x4, x3, x0"
project = angr.load_shellcode(shellcode, "aarch64")
engine = UberIcicleEngine(project)
init_state = project.factory.blank_state(
remove_options={*o.symbolic},
add_options={o.ZERO_FILL_UNCONSTRAINED_MEMORY, o.ZERO_FILL_UNCONSTRAINED_REGISTERS},
)
emulator = Emulator(engine, init_state.copy())
bp1_addr = project.entry + 4 # mov x1, 0x2
bp2_addr = project.entry + 12 # mov x3, 0x3
emulator.add_breakpoint(bp1_addr)
emulator.add_breakpoint(bp2_addr)
# Run to first breakpoint
stop_reason = emulator.run()
assert stop_reason == EmulatorStopReason.BREAKPOINT
assert emulator.state.addr == bp1_addr
assert emulator.state.regs.x0.concrete_value == 1
# Run to second breakpoint
stop_reason = emulator.run()
assert stop_reason == EmulatorStopReason.BREAKPOINT
assert emulator.state.addr == bp2_addr
assert emulator.state.regs.x1.concrete_value == 2
assert emulator.state.regs.x2.concrete_value == 3
# Run to end
stop_reason = emulator.run(num_inst=2)
assert stop_reason == EmulatorStopReason.INSTRUCTION_LIMIT
assert emulator.state.regs.x3.concrete_value == 3
assert emulator.state.regs.x4.concrete_value == 2 # 3 - 1
def test_breakpoint_at_start(self):
"""Test that a breakpoint at the very first instruction is ignored (execution resumes immediately)."""
shellcode = "mov x0, 0x1; mov x1, 0x2"
project = angr.load_shellcode(shellcode, "aarch64")
engine = UberIcicleEngine(project)
init_state = project.factory.blank_state(
remove_options={*o.symbolic},
add_options={o.ZERO_FILL_UNCONSTRAINED_MEMORY, o.ZERO_FILL_UNCONSTRAINED_REGISTERS},
)
emulator = Emulator(engine, init_state.copy())
emulator.add_breakpoint(project.entry)
# The breakpoint at the entry should be ignored, so execution should proceed as normal
stop_reason = emulator.run(num_inst=2)
assert stop_reason == EmulatorStopReason.INSTRUCTION_LIMIT
assert emulator.state.regs.x0.concrete_value == 1
assert emulator.state.regs.x1.concrete_value == 2