mirror of
https://github.com/LLVMParty/striga
synced 2026-06-21 13:43:42 +00:00
160 lines
5.0 KiB
Python
160 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
# ruff: noqa: E402
|
|
|
|
import sys
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import cast
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from llvm import create_context
|
|
|
|
from striga import BoundaryResult, Interpreter, PtrVal
|
|
from striga.domains.concrete import (
|
|
ConcreteDomain,
|
|
ConcreteMemory,
|
|
ConcreteRegisters,
|
|
ConcreteValue,
|
|
)
|
|
from striga.domains.taint import TaintDomain, TaintMemory, TaintRegisters, Tainted
|
|
from striga.semantics import Semantics
|
|
|
|
HANDLER_BYTES = [
|
|
b"\x48\x8b\x06", # mov rax, qword ptr [rsi]
|
|
b"\x48\x31\xd8", # xor rax, rbx
|
|
b"\x48\x89\x44\x24\x08", # mov qword ptr [rsp + 8], rax
|
|
b"\x48\x8b\x4c\x24\x08", # mov rcx, qword ptr [rsp + 8]
|
|
b"\xc3", # ret
|
|
]
|
|
|
|
|
|
@dataclass
|
|
class HandlerProfile:
|
|
instructions: int = 0
|
|
stores: int = 0
|
|
boundaries: list[str] = field(default_factory=list)
|
|
|
|
def pre_instruction(self, inst) -> None:
|
|
self.instructions += 1
|
|
|
|
def post_store(
|
|
self, inst, value: ConcreteValue, ptr: PtrVal[ConcreteValue]
|
|
) -> ConcreteValue:
|
|
self.stores += 1
|
|
return value
|
|
|
|
def post_boundary(
|
|
self, inst, name, target: ConcreteValue, target_arg, target_ptr
|
|
) -> ConcreteValue:
|
|
self.boundaries.append(name)
|
|
return target
|
|
|
|
|
|
def _run_handler(sem: Semantics, interp: Interpreter, start: int = 0x1000):
|
|
rip = start
|
|
result = None
|
|
for code in HANDLER_BYTES:
|
|
sem.lift_instruction(sem.cs_disasm(rip, code))
|
|
result = interp.execute_block(sem.insn_blocks[rip])
|
|
if not isinstance(result, int):
|
|
return result
|
|
rip = result
|
|
return result
|
|
|
|
|
|
def test_concrete_handler_like_sequence_has_meaningful_state_and_boundary() -> None:
|
|
with create_context() as context:
|
|
with context.create_module("concrete_handler") as module:
|
|
sem = Semantics(module)
|
|
sem.begin(0x1000)
|
|
regs = ConcreteRegisters(
|
|
sem.reg_sizes,
|
|
{"rsi": 0x5000, "rbx": 0x1111, "rsp": 0x8000},
|
|
)
|
|
memory = ConcreteMemory(bytearray())
|
|
domain = ConcreteDomain()
|
|
memory.write(domain.constant(0x5000, 64), domain.constant(0x2222, 64), 64)
|
|
memory.write(domain.constant(0x8000, 64), domain.constant(0x9000, 64), 64)
|
|
profile = HandlerProfile()
|
|
interp = Interpreter(
|
|
domain,
|
|
regs,
|
|
memory,
|
|
sem.reg_sizes,
|
|
sem.state_ty,
|
|
sem.reg_indices,
|
|
hooks=profile,
|
|
)
|
|
|
|
result = _run_handler(sem, interp)
|
|
|
|
assert isinstance(result, BoundaryResult)
|
|
boundary = cast("BoundaryResult[ConcreteValue]", result)
|
|
assert boundary.name == "__striga_ret"
|
|
assert boundary.target == ConcreteValue(0x9000, 64)
|
|
assert regs.read("rcx") == ConcreteValue(0x3333, 64)
|
|
assert memory.read(domain.constant(0x8008, 64), 64) == ConcreteValue(
|
|
0x3333, 64
|
|
)
|
|
assert profile.boundaries == ["__striga_ret"]
|
|
assert profile.stores >= 1
|
|
assert profile.instructions > 0
|
|
|
|
|
|
def test_taint_handler_like_sequence_tracks_loaded_bytecode_to_output_register() -> (
|
|
None
|
|
):
|
|
with create_context() as context:
|
|
with context.create_module("taint_handler") as module:
|
|
sem = Semantics(module)
|
|
sem.begin(0x1000)
|
|
regs = TaintRegisters(
|
|
sem.reg_sizes,
|
|
{
|
|
"rsi": Tainted(0x5000, 64, frozenset()),
|
|
"rbx": Tainted(0x1111, 64, frozenset({"key"})),
|
|
"rsp": 0x8000,
|
|
},
|
|
)
|
|
memory = TaintMemory(bytearray())
|
|
memory.write(
|
|
Tainted(0x5000, 64, frozenset()),
|
|
Tainted(0x2222, 64, frozenset({"bytecode"})),
|
|
64,
|
|
)
|
|
memory.write(
|
|
Tainted(0x8000, 64, frozenset()), Tainted(0x9000, 64, frozenset()), 64
|
|
)
|
|
interp = Interpreter(
|
|
TaintDomain(),
|
|
regs,
|
|
memory,
|
|
sem.reg_sizes,
|
|
sem.state_ty,
|
|
sem.reg_indices,
|
|
)
|
|
|
|
result = _run_handler(sem, interp)
|
|
|
|
assert isinstance(result, BoundaryResult)
|
|
assert regs.read("rcx") == Tainted(
|
|
0x3333, 64, frozenset({"bytecode", "key"})
|
|
)
|
|
assert memory.read(Tainted(0x8008, 64, frozenset()), 64) == Tainted(
|
|
0x3333, 64, frozenset({"bytecode", "key"})
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
test_concrete_handler_like_sequence_has_meaningful_state_and_boundary()
|
|
test_taint_handler_like_sequence_tracks_loaded_bytecode_to_output_register()
|
|
print("PASS interpreter handler example tests")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|