angr/tests/engines/test_java.py
pre-commit-ci[bot] d38cc5a019
[pre-commit.ci] pre-commit autoupdate (#6721)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.22 → v0.16.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.22...v0.16.0)

* Apply fixes

* Add values()

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Kevin Phoenix <kevin@kphoenix.us>
2026-07-29 13:46:11 -07:00

699 lines
27 KiB
Python
Executable file

#!/usr/bin/env python3
from __future__ import annotations
__package__ = __package__ or "tests.engines" # pylint:disable=redefined-builtin
import os
import unittest
import claripy
from archinfo.arch_amd64 import ArchAMD64
from archinfo.arch_soot import (
ArchSoot,
SootAddressDescriptor,
SootAddressTerminator,
SootArgument,
SootMethodDescriptor,
)
import angr
from angr.engines.soot.method_dispatcher import resolve_method
from angr.engines.soot.values import SimSootValue_ArrayRef, SimSootValue_ThisRef
from angr.storage.memory_mixins import DefaultMemory, JavaVmMemory, KeyValueMemory
try:
import pysoot
except ModuleNotFoundError:
pysoot = None
from tests.common import bin_location
class TestJava(unittest.TestCase):
test_location = os.path.join(bin_location, "tests", "java")
sdk_path = os.path.join(os.path.expanduser("~"), "Android", "Sdk", "platforms")
@unittest.skipUnless(pysoot, "pysoot not available")
def test_fauxware(self):
# create project
binary_path = os.path.join(self.test_location, "fauxware_java_jni", "fauxware.jar")
jni_options = {"jni_libs": ["libfauxware.so"]}
project = angr.Project(binary_path, main_opts=jni_options, auto_load_libs=True)
entry = project.factory.entry_state()
simgr = project.factory.simgr(entry)
# find path to `accepted()` method
accepted_method = SootMethodDescriptor.from_string("Fauxware.accepted()").address()
simgr.explore(find=lambda s: s.addr == accepted_method)
assert len(simgr.found) == 1
state = simgr.found[0]
# eval password
cmd_line_args = project.simos.get_cmd_line_args(state)
password = state.solver.eval(cmd_line_args[0])
assert password == "SOSNEAKY"
@unittest.skipUnless(os.path.exists(sdk_path), "Android SDK found")
def test_apk_loading(self):
loading_opts = {
"android_sdk": self.sdk_path,
"entry_point": "com.example.antoniob.android1.MainActivity.onCreate",
"entry_point_params": ("android.os.Bundle",),
}
project = angr.Project(
os.path.join(self.test_location, "android1.apk"), main_opts=loading_opts, auto_load_libs=False
)
blank_state = project.factory.blank_state()
a1 = SimSootValue_ThisRef.new_object(blank_state, "com.example.antoniob.android1.MainActivity")
a2 = SimSootValue_ThisRef.new_object(blank_state, "android.os.Bundle", symbolic=True)
args = [SootArgument(arg, arg.type) for arg in [a1, a2]]
entry = project.factory.entry_state(args=args)
simgr = project.factory.simgr(entry)
simgr.step()
simgr.step()
assert simgr.active[0].addr.block_idx == 0
assert simgr.active[0].addr.stmt_idx == 3
simgr.run()
assert len(simgr.deadended) == 1
assert type(simgr.deadended[0].addr) is SootAddressTerminator
#
# Command line arguments
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_cmd_line_args(self):
project = self.create_project("cmd_line_args", load_native_libs=False)
entry = project.factory.entry_state()
simgr = project.factory.simgr(entry)
simgr.run()
assert len(simgr.deadended) == 2
state1, state2 = tuple(simgr.deadended)
# get symbol of args[0] from memory
args = state1.globals["cmd_line_args"]
args0_arrref = SimSootValue_ArrayRef(args, 0)
args0_strref = state1.memory.load(args0_arrref)
args0_strval = state1.memory.load(args0_strref)
# eval args[0] on both states
str1 = state1.solver.eval(args0_strval)
str2 = state2.solver.eval(args0_strval)
assert "secret_value" in [str1, str2]
#
# JNI Version Information
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_jni_version_information(self):
project = self.create_project("jni_version_information")
self.run_method(project=project, method="MixedJava.test_jni_get_version", assert_locals={"i0": 0x10008})
#
# JNI Global and Local References
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_jni_global_and_local_refs(self):
project = self.create_project("jni_global_and_local_refs")
assertions = {"global refs dict": lambda state: state.jni_references.global_refs == {}}
self.run_method(
project=project, method="MixedJava.test_jni_global_refs", assert_locals={"i0": 0xA}, assertions=assertions
)
#
# JNI Object Operations
#
def test_jni_object_operations(self):
project = self.create_project("jni_object_operations")
self.run_method(project=project, method="MixedJava.test_jni_alloc_object", assert_locals={"i0": 0})
self.run_method(project=project, method="MixedJava.test_jni_new_object", assert_locals={"i0": 1})
self.run_method(project=project, method="MixedJava.test_jni_new_subclass_object", assert_locals={"i0": 2})
self.run_method(
project=project,
method="MixedJava.test_jni_isinstanceof",
assert_locals={"i0": 1, "i1": 1, "i2": 0, "i3": 1},
)
self.run_method(project=project, method="MixedJava.test_jni_issameobject", assert_locals={"i0": 0, "i1": 1})
#
# JNI String Operations
#
def test_jni_string_operations(self):
project = self.create_project("jni_string_operations")
assertions = {
"1st string": lambda state: state.solver.eval_one(self.load_string(state, "r0")) == "mum",
"2nd string": lambda state: state.solver.eval_one(self.load_string(state, "r1")) == "himum!",
}
self.run_method(
project=project,
method="MixedJava.test_jni_string_operations",
assert_locals={"i0": 0x3, "i1": 0x6},
assertions=assertions,
)
#
# JNI Field Access
#
def test_jni_field_access(self):
project = self.create_project("jni_field_access")
self.run_method(
project=project,
method="MixedJava.test_static_field_access_basic",
assert_locals={"i0": 0x0, "i1": 0x1, "i2": 0xA, "i3": 0xB, "i4": 0x7, "i5": 0xB, "i6": 0x0, "i7": 0x9},
)
self.run_method(
project=project, method="MixedJava.test_jni_static_field_access", assert_locals={"i0": 0, "i1": 5}
)
self.run_method(
project=project,
method="MixedJava.test_jni_static_field_access_subclass",
assert_locals={"i0": 1, "i1": 10, "i2": 30, "i3": 1},
)
self.run_method(
project=project,
method="MixedJava.test_instance_field_access_0",
assert_locals={"i0": 0, "i1": 10, "i2": 5, "i3": 5},
)
self.run_method(
project=project,
method="MixedJava.test_instance_field_access_1",
assert_locals={"i0": 0, "i1": 1, "i2": 10, "i3": 4, "i4": 4, "i5": 1},
)
#
# JNI Method Calls
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_jni_method_calls(self):
project = self.create_project("jni_method_calls")
self.run_method(
project=project, method="MixedJava.test_jni_non_virtual_instance_method_call", assert_locals={"i0": 5}
)
self.run_method(
project=project, method="MixedJava.test_jni_instance_method_calls_basic", assert_locals={"i0": 7, "i1": 7}
)
self.run_method(
project=project,
method="MixedJava.test_jni_instance_method_calls_subclass",
assert_locals={"i0": 2, "i1": 2},
)
self.run_method(
project=project,
method="MixedJava.test_jni_instance_method_calls_shared_method_id",
assert_locals={"i0": 8, "i1": 2},
)
self.run_method(
project=project, method="MixedJava.test_jni_instance_method_calls_args", assert_locals={"i0": 11}
)
self.run_method(project=project, method="MixedJava.test_jni_static_method_call", assert_locals={"i0": 10})
self.run_method(
project=project, method="MixedJava.test_jni_static_method_call_return_obj", assert_locals={"i0": 7}
)
#
# JNI Primitive Datatypes
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_jni_primitive_datatypes(self):
project = self.create_project("jni_primitive_datatypes")
self.run_method(
project=project,
method="MixedJava.test_boolean",
assert_locals={"z0": 1, "z1": 0, "z2": 1, "z3": 0, "z4": 1},
)
self.run_method(
project=project, method="MixedJava.test_byte", assert_locals={"b5": 30, "b8": 0xFFFFFF80, "b11": 0}
)
self.run_method(project=project, method="MixedJava.test_char", assert_locals={"c4": 21, "c6": 0, "c9": 1})
self.run_method(
project=project,
method="MixedJava.test_short",
assert_locals={"s3": 0x1000, "s5": 0xFFFFF000, "s0": 11, "s9": 0},
)
self.run_method(
project=project,
method="MixedJava.test_int",
assert_locals={"i1": 0xFFFFFFF6, "i3": 0, "i5": 0x80000001, "i7": 0x7FFFFFFF},
)
self.run_method(
project=project, method="MixedJava.test_long", assert_locals={"l1": 0xFFFFFFFFFFFFFFFF, "l3": 1}
)
@unittest.skipUnless(pysoot, "pysoot not available")
def test_jni_object_arrays(self):
project = self.create_project("jni_object_array_operations")
self.run_method(project=project, method="MixedJava.test_jni_access_object_array", assert_locals={"i0": 7})
self.run_method(project=project, method="MixedJava.test_jni_new_object_array", assert_locals={"i0": 10})
#
# JNI Array Operations
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_jni_array_operations(self):
project = self.create_project("jni_array_operations")
# test_jni_newarray
self.run_method(
project=project,
method="MixedJava.test_jni_newarray",
assert_locals={"i0": 0, "i1": 1, "i2": 2, "i3": 3, "i4": 4},
)
# test_jni_getarrayregion
state = self.run_method(project=project, method="MixedJava.test_jni_getarrayregion")
a = self.load_value_from_stack(state, "i1")
state.solver.add(a == 15)
idx = state.posix.stdin.content[0][0]
assert state.solver.eval_one(idx) == 7
# test_jni_setarrayregion1
self.run_method(
project=project,
method="MixedJava.test_jni_setarrayregion1",
assert_locals={"i0": 0, "i1": 3, "i2": 2, "i3": 1, "i4": 4},
)
# test_jni_setarrayregion2
state = self.run_method(project=project, method="MixedJava.test_jni_setarrayregion2")
a = self.load_value_from_stack(state, "i1")
state.solver.add(a == 2)
idx = state.posix.stdin.content[0][0]
idx_value = state.solver.eval_one(idx)
assert idx_value == 0
# test_jni_setarrayregion2
state = self.run_method(project=project, method="MixedJava.test_jni_setarrayregion2")
a = self.load_value_from_stack(state, "i1")
state.solver.add(a == 0)
idx = state.posix.stdin.content[0][0]
idx_value = state.solver.eval_exact(idx, 2)
assert 1 in idx_value
assert 2 in idx_value
assert 3 not in idx_value
# test_jni_getarrayelements_symbolic
winning_path = self.get_winning_path(
project=project, method_fullname="MixedJava.test_jni_getarrayelements_symbolic"
)
stdin_packets = winning_path.posix.stdin.content
idx = winning_path.solver.eval_one(stdin_packets[0][0])
min_length = winning_path.solver.min(stdin_packets[1][0])
assert idx == 223
assert min_length == 224
# test_jni_releasearrayelements
self.run_method(
project=project,
method="MixedJava.test_jni_releasearrayelments",
assert_locals={"i0": 4, "i1": 3, "i2": 2, "i3": 1, "i4": 0},
)
# test_jni_getarrayelements_and_releasearrayelements
self.run_method(
project=project,
method="MixedJava.test_jni_getarrayelements_and_releasearrayelements",
assert_locals={
"c9": 0xFFFF,
"c14": 0x0000,
"b5": 0x0000007F,
"b10": 0xFFFFFF80,
"s6": 0x00007FFF,
"s11": 0xFFFF8000,
"i7": 0x7FFFFFFF,
"i12": 0x80000000,
"l8": 0x7FFFFFFFFFFFFFFF,
"l13": 0x8000000000000000,
},
)
# test_jni_getarraylength
state = self.run_method(project=project, method="MixedJava.test_jni_getarraylength")
a = state.memory.stack.load("i3")
assert state.solver.eval(a) == 10
b = state.memory.stack.load("i4")
assert state.solver.min(b) == 0
assert state.solver.max(b) == 255
#
# Method Calls
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_method_calls(self):
project = self.create_project("method_calls", load_native_libs=False)
self.run_method(
project=project,
method="MixedJava.test_instance_method_calls",
assert_locals={"i0": 0, "i1": 1, "i2": 1, "i3": 2, "i4": 2, "i5": 2},
)
self.run_method(
project=project,
method="MixedJava.test_static_method_calls_0",
assert_locals={"i0": 0, "i1": 1, "i2": 2, "i3": 2},
)
self.run_method(
project=project,
method="MixedJava.test_static_method_calls_1",
assert_locals={"i0": 0, "i1": 0, "i2": 1, "i3": 2, "i4": 2, "i5": 2},
)
self.run_method(project=project, method="MixedJava.test_special_invoke_0", assert_locals={"i0": 3})
self.run_method(project=project, method="MixedJava.test_special_invoke_1", assert_locals={"i0": 4})
#
# Array Operations
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_array_operations(self):
project = self.create_project("array_operations", load_native_libs=False)
# test_basic_array_operations
self.run_method(
project=project,
method="MixedJava.test_basic_array_operations",
assert_locals={"i1": 0, "i2": 1, "i3": 2, "i4": 3, "i5": 4, "i6": 5, "i7": 2, "i8": 0},
)
# test_symbolic_array_read
winning_path = self.get_winning_path(project=project, method_fullname="MixedJava.test_symbolic_array_read")
stdin_packets = winning_path.posix.stdin.content
input_char, _ = stdin_packets[0]
solutions = winning_path.solver.eval_upto(input_char, 2)
assert ord("A") in solutions
assert ord("C") in solutions
# test_symbolic_array_write
winning_path = self.get_winning_path(project=project, method_fullname="MixedJava.test_symbolic_array_write")
stdin_packets = winning_path.posix.stdin.content
idx_symbol, _ = stdin_packets[0]
val_symbol, _ = stdin_packets[1]
winning_path.solver.add(val_symbol != 0) # exclude trivial solution
idx = winning_path.solver.eval(idx_symbol)
val = winning_path.solver.eval(val_symbol)
assert idx == 73
assert val == 53
# test_symbolic_array_length
winning_path = self.get_winning_path(project=project, method_fullname="MixedJava.test_symbolic_array_length")
stdin_packets = winning_path.posix.stdin.content
input_char, _ = stdin_packets[0]
solution = winning_path.solver.eval(input_char)
assert solution == ord("F")
# test_index_of_of_bound0
state = self.run_method(project=project, method="MixedJava.test_index_of_of_bound0")
array_len = self.load_value_from_stack(state, "i1")
assert state.solver.min(array_len) == 0
assert state.solver.max(array_len) == 255
# test_index_of_of_bound1
state = self.run_method(project=project, method="MixedJava.test_index_of_of_bound1")
array_len = self.load_value_from_stack(state, "i1")
assert state.solver.min(array_len) == 101
assert state.solver.max(array_len) == 255
# test_index_of_of_bound2
state = self.run_method(project=project, method="MixedJava.test_index_of_of_bound2")
assert self.load_value_from_stack(state, "i1") is not None
assert self.load_value_from_stack(state, "i2") is None
assert self.load_value_from_stack(state, "i3") is None
assert self.load_value_from_stack(state, "i4") is not None
assert self.load_value_from_stack(state, "i5") is None
# test_index_of_of_bound3
state = self.run_method(project=project, method="MixedJava.test_index_of_of_bound3")
assert self.load_value_from_stack(state, "i1") is not None
assert self.load_value_from_stack(state, "i2") is not None
assert self.load_value_from_stack(state, "i3") is None
assert self.load_value_from_stack(state, "i4") is not None
assert self.load_value_from_stack(state, "i5") is None
# test_index_of_of_bound4
state = self.run_method(project=project, method="MixedJava.test_index_of_of_bound4")
assert self.load_value_from_stack(state, "i1") is not None
assert self.load_value_from_stack(state, "i2") is not None
assert self.load_value_from_stack(state, "i3") is None
# test_index_of_of_bound5
state = self.run_method(project=project, method="MixedJava.test_index_of_of_bound5")
assert self.load_value_from_stack(state, "i1") is not None
assert self.load_value_from_stack(state, "i2") is not None
assert self.load_value_from_stack(state, "i3") is None
#
# MultiArray Operations
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_multiarray_operations(self):
project = self.create_project("multiarray_operations", load_native_libs=False)
self.run_method(project=project, method="MixedJava.basic_multiarray_ops", assert_locals={"d1": 4})
#
# Loading
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_loading(self):
# Test1: test loading with load path
native_libs_ld_path = os.path.join(self.test_location, "misc", "loading1", "libs")
jar_path = os.path.join(self.test_location, "misc", "loading1", "mixedjava.jar")
# define which libraries to load (+ the load path)
jni_options = {"jni_libs": ["libmixedjava.so"], "jni_libs_ld_path": native_libs_ld_path}
project = angr.Project(jar_path, main_opts=jni_options, auto_load_libs=True)
# check if libmixedjava.so was loaded
loaded_libs = [lib.provides for lib in project.loader.all_elf_objects]
assert "libmixedjava.so" in loaded_libs
# Test 2: test loading without load path
# => the folder of the JAR is implicitly used as an additional load path
binary_dir = os.path.join(self.test_location, "misc", "loading2")
project = self.create_project(binary_dir)
# check if libmixedjava.so was loaded
loaded_libs = [lib.provides for lib in project.loader.all_elf_objects]
assert "libmixedjava.so" in loaded_libs
#
# SimStates
#
@unittest.skipUnless(pysoot, "pysoot not available")
def test_toggling_of_simstate(self):
binary_dir = os.path.join(self.test_location, "misc", "simstates")
project = self.create_project(binary_dir)
state = project.factory.entry_state()
assert state.ip_is_soot_addr
assert isinstance(state.arch, ArchSoot)
assert isinstance(state.memory, JavaVmMemory)
assert isinstance(state.registers, KeyValueMemory)
state.regs.ip = 1
assert not state.ip_is_soot_addr
assert isinstance(state.arch, ArchAMD64)
assert isinstance(state.memory, DefaultMemory)
assert isinstance(state.registers, DefaultMemory)
state.regs._ip = project.entry
assert state.ip_is_soot_addr
assert isinstance(state.arch, ArchSoot)
assert isinstance(state.memory, JavaVmMemory)
assert isinstance(state.registers, KeyValueMemory)
state.ip = 1
assert not state.ip_is_soot_addr
assert isinstance(state.arch, ArchAMD64)
assert isinstance(state.memory, DefaultMemory)
assert isinstance(state.registers, DefaultMemory)
state_copy = state.copy()
assert not state_copy.ip_is_soot_addr
assert isinstance(state_copy.arch, ArchAMD64)
assert isinstance(state_copy.memory, DefaultMemory)
assert isinstance(state_copy.registers, DefaultMemory)
@unittest.skipUnless(pysoot, "pysoot not available")
def test_object_tracking(self):
binary_dir = os.path.join(self.test_location, "object_tracking")
project = self.create_project(binary_dir, load_native_libs=False)
bootstrap_state = project.factory.blank_state(addr=SootAddressTerminator())
mylib_object = SimSootValue_ThisRef.new_object(bootstrap_state, "MyLib", symbolic=True, init_object=False)
soot_method = resolve_method(
bootstrap_state, "testGetterAndSetterConcrete", "MixedJava", ("mylib.MyLib",), init_class=False
).address()
call_state = project.factory.call_state(
soot_method,
SootArgument(mylib_object, mylib_object.type, is_this_ref=False),
base_state=bootstrap_state,
ret_addr=SootAddressTerminator(),
)
call_state.options.add(angr.options.JAVA_IDENTIFY_GETTER_SETTER)
call_state.options.add(angr.options.JAVA_TRACK_ATTRIBUTES)
simgr = project.factory.simgr(call_state)
simgr.run()
assert len(simgr.deadended) == 1
final_state = simgr.deadended[0]
assert final_state.solver.eval(mylib_object.get_field(final_state, "myInt", "int")) == 1
assert final_state.solver.eval(mylib_object.get_field(final_state, "myShort", "short")) == 1
assert final_state.solver.eval(mylib_object.get_field(final_state, "myChar", "char")) == ord("c")
assert final_state.solver.eval(mylib_object.get_field(final_state, "myLong", "long")) == 2
assert final_state.solver.eval(mylib_object.get_field(final_state, "myFloat", "float")) == 1.5
assert final_state.solver.eval(mylib_object.get_field(final_state, "myDouble", "double")) == 1.5
string_ref = mylib_object.get_field(final_state, "myString", "java.lang.String")
assert final_state.solver.eval(final_state.memory.load(string_ref)) == "Hello!"
array_ref = mylib_object.get_field(final_state, "myArray", "int[]")
assert final_state.solver.eval(array_ref.size) == 3
object_ref = mylib_object.get_field(final_state, "myObject", "java.lang.Object")
assert final_state.solver.eval(object_ref.get_field(final_state, "a", "int")) == 1
assert ("myInt", "int") in mylib_object.attributes
assert ("myChar", "char") in mylib_object.attributes
assert ("myShort", "short") in mylib_object.attributes
assert ("myLong", "long") in mylib_object.attributes
assert ("myFloat", "float") in mylib_object.attributes
assert ("myDouble", "double") in mylib_object.attributes
assert ("myString", "java.lang.String") in mylib_object.attributes
assert ("myArray", "int[]") in mylib_object.attributes
assert ("myObject", "java.lang.Object") in mylib_object.attributes
#
# Helper
#
def run_method(self, project, method, assert_locals=None, assertions=None):
end_state = self.get_last_state_of_method(project, method)
# print_java_memory(end_state)
if assert_locals:
for symbol_name, assert_value in assert_locals.items():
symbol = self.load_value_from_stack(end_state, symbol_name)
val = end_state.solver.eval(symbol)
assert val == assert_value
if assertions:
for test in assertions.values():
assert test(end_state)
return end_state
@unittest.skipUnless(pysoot, "pysoot not available")
def create_project(self, binary_dir, load_native_libs=True):
jar_path = os.path.join(self.test_location, binary_dir, "mixedjava.jar")
if load_native_libs:
jni_options = {"jni_libs": ["libmixedjava.so"]}
project = angr.Project(jar_path, main_opts=jni_options, auto_load_libs=True)
else:
project = angr.Project(jar_path, auto_load_libs=True)
return project
def load_string(self, state, local_name):
str_ref = self.load_value_from_stack(state, local_name)
return state.memory.load(str_ref)
def load_value_from_stack(self, state, symbol_name):
try:
return state.memory.stack.load(symbol_name)
except KeyError:
return None
def get_entry_state_of_method(self, project, method_fullname):
# get SootAddressDescriptor of method entry
soot_method = project.loader.main_object.get_soot_method(method_fullname)
method = SootMethodDescriptor.from_soot_method(soot_method)
addr = SootAddressDescriptor(method, 0, 0)
# create call state
return project.factory.blank_state(addr=addr, add_options={angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY})
def get_last_state_of_method(self, project, method_fullname):
state = self.get_entry_state_of_method(project, method_fullname)
# run until no successors exists
# Note: this does not work if conditional branches are present
states = [state]
succ = states[-1].step()
while len(succ.successors) == 1:
states += succ
succ = states[-1].step()
# last state is the 'Terminator' state
# => return the state before
return states[-2]
def get_winning_paths(self, project, method_fullname):
state = self.get_entry_state_of_method(project, method_fullname)
simgr = project.factory.simgr(state)
simgr.run()
paths = simgr.deadended
# winning paths output a single 'W' on stdout
winnning_paths = []
for pp in paths:
stdout_packets = pp.posix.stdout.content
read_byte, _ = stdout_packets[0]
# a winning path is printing 'W'
pp.solver.add(read_byte == claripy.BVV(ord("W"), 8))
if pp.satisfiable():
winnning_paths.append(pp)
return winnning_paths
def get_winning_path(self, project, method_fullname):
winning_paths = self.get_winning_paths(project, method_fullname)
assert len(winning_paths) != 0
assert len(winning_paths) == 1
return winning_paths[0]
if __name__ == "__main__":
unittest.main()