mirror of
https://github.com/angr/angr
synced 2026-08-17 12:23:11 -04:00
164 lines
6.8 KiB
Python
Executable file
164 lines
6.8 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# pylint:disable=missing-class-docstring,no-self-use
|
|
from __future__ import annotations
|
|
|
|
__package__ = __package__ or "tests.analyses.reaching_definitions" # pylint:disable=redefined-builtin
|
|
|
|
import os
|
|
from types import SimpleNamespace
|
|
from typing import TYPE_CHECKING
|
|
from unittest import TestCase, main
|
|
|
|
import archinfo
|
|
import claripy
|
|
|
|
import angr
|
|
from angr.analyses.reaching_definitions import FunctionHandler
|
|
from angr.calling_conventions import SimCCCdecl, SimCCMicrosoftAMD64, SimCCSystemVAMD64
|
|
from angr.errors import SimMemoryMissingError
|
|
from angr.knowledge_plugins.key_definitions.atoms import Register
|
|
from angr.sim_type import SimStruct, SimTypeFunction, SimTypeLongLong
|
|
from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
|
|
|
|
if TYPE_CHECKING:
|
|
from angr.analyses.reaching_definitions import FunctionCallData
|
|
from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
|
|
|
|
from tests.common import bin_location
|
|
|
|
TESTS_LOCATION = os.path.join(bin_location, "tests")
|
|
|
|
|
|
def load_cstring_from_loader_memory(project, addr: int, as_str: bool = False):
|
|
# This function loads a null-terminated string from the static memory region
|
|
s = b""
|
|
while True:
|
|
char_addr = addr + len(s)
|
|
try:
|
|
v = project.loader.memory.load(char_addr, 1)
|
|
except KeyError:
|
|
break
|
|
if v == b"\x00":
|
|
break
|
|
s += v
|
|
return s.decode("utf-8") if as_str else (s + b"\x00")
|
|
|
|
|
|
def load_cstring_from_memory_definitions(ld, addr: int, as_str: bool = False):
|
|
# This function loads a null-terminated string from the memory definitions
|
|
s = b""
|
|
while True:
|
|
char_addr = addr + len(s)
|
|
try:
|
|
v = ld.memory.load(char_addr, 1).one_value().concrete_value
|
|
except SimMemoryMissingError:
|
|
break
|
|
if v == 0:
|
|
break
|
|
s += v.to_bytes(1, "little")
|
|
return s.decode("utf-8") if as_str else (s + b"\x00")
|
|
|
|
|
|
class CustomFunctionHandler(FunctionHandler):
|
|
def __init__(self, project):
|
|
self.project = project
|
|
self.system_cmd = ""
|
|
self.strcpy_addr = None
|
|
self.sscanf_str_addr = None
|
|
self.sscanf_str = None
|
|
self.sscanf_fmtstr_addr = None
|
|
self.sscanf_fmtstr = None
|
|
self.sscanf_out_value = None
|
|
self.malloc_sizes = []
|
|
|
|
def handle_impl_malloc(self, state: ReachingDefinitionsState, data: FunctionCallData):
|
|
assert data.args_atoms
|
|
((src_atom,),) = data.args_atoms
|
|
|
|
src_value = state.get_values(src_atom).one_value().concrete_value
|
|
self.malloc_sizes.append(src_value)
|
|
data.depends(next(iter(data.ret_atoms)), value=MultiValues(claripy.BVV(0x12345678, 64)))
|
|
|
|
def handle_impl___isoc99_sscanf(self, state: ReachingDefinitionsState, data: FunctionCallData):
|
|
assert data.args_atoms
|
|
(str_atom,), (fmtstr_atom,), (out_atom,) = data.args_atoms[:3]
|
|
|
|
# string
|
|
str_addr = state.get_values(str_atom).one_value().concrete_value
|
|
self.sscanf_str_addr = str_addr
|
|
str_ = load_cstring_from_loader_memory(self.project, str_addr)
|
|
self.sscanf_str = str_
|
|
|
|
# format string: "%d"
|
|
fmtstr_addr = state.get_values(fmtstr_atom).one_value().concrete_value
|
|
self.sscanf_fmtstr_addr = fmtstr_addr
|
|
fmtstr = load_cstring_from_loader_memory(self.project, str_addr)
|
|
self.sscanf_fmtstr = fmtstr
|
|
|
|
# out pointer
|
|
(dst,) = state.deref(out_atom, size=4, endness=state.arch.memory_endness)
|
|
(src,) = state.deref(str_atom, size=len(str_), endness="Iend_BE")
|
|
self.sscanf_out_value = int(str_.strip(b"\x00"))
|
|
data.depends(dst, src, value=MultiValues(claripy.BVV(self.sscanf_out_value, 32)))
|
|
|
|
def handle_impl_strcpy(self, state: ReachingDefinitionsState, data: FunctionCallData):
|
|
assert data.args_atoms
|
|
(dst_atom,), (src_atom,) = data.args_atoms
|
|
|
|
# Assume source is a constant string
|
|
src_addr = state.get_values(src_atom).one_value().concrete_value
|
|
self.strcpy_addr = src_addr
|
|
src_str = load_cstring_from_loader_memory(self.project, src_addr)
|
|
src_str_size = len(src_str)
|
|
|
|
(dst,) = state.deref(dst_atom, size=src_str_size, endness="Iend_BE")
|
|
(src,) = state.deref(src_atom, size=src_str_size, endness="Iend_BE")
|
|
|
|
data.depends(dst, src, value=MultiValues(claripy.BVV(src_str)))
|
|
|
|
def handle_impl_system(self, state: ReachingDefinitionsState, data: FunctionCallData):
|
|
assert data.args_atoms
|
|
(cmd_atom,) = data.args_atoms[0]
|
|
cmd_addr = state.get_values(cmd_atom).one_value().concrete_value
|
|
self.system_cmd = load_cstring_from_memory_definitions(state.live_definitions, cmd_addr, as_str=True)
|
|
|
|
|
|
class TestFunctionHandler(TestCase):
|
|
def test_function_handler_depends_has_endness(self):
|
|
filename = os.path.join(TESTS_LOCATION, "x86_64", "rda_function_handler")
|
|
project = angr.Project(filename, auto_load_libs=False)
|
|
_ = project.analyses.CFGFast()
|
|
handler = CustomFunctionHandler(project)
|
|
_ = project.analyses.ReachingDefinitions("main", function_handler=handler)
|
|
|
|
assert handler.system_cmd == "ABCDEFGH"
|
|
assert handler.sscanf_str == b"12345678\x00"
|
|
assert handler.sscanf_out_value == 12345678
|
|
assert handler.malloc_sizes == [20, 12345678]
|
|
|
|
def test_c_return_as_atoms_implicit_outparam(self):
|
|
# a prototype returning a large struct through an implicit out-parameter must not crash
|
|
# c_return_as_atoms; the return atom is the register holding the returned pointer (issue #6536)
|
|
arch = archinfo.ArchAMD64()
|
|
state = SimpleNamespace(arch=arch)
|
|
retty = SimStruct({"a": SimTypeLongLong(), "b": SimTypeLongLong()}, name="big").with_arch(arch)
|
|
proto = SimTypeFunction([], retty).with_arch(arch)
|
|
|
|
atoms = FunctionHandler.c_return_as_atoms(state, SimCCMicrosoftAMD64(arch), proto)
|
|
assert atoms == {Register(*arch.registers["rax"], arch=arch)}
|
|
|
|
# 16-byte structs are returned in rax:rdx on SysV; this behavior must be unchanged
|
|
atoms = FunctionHandler.c_return_as_atoms(state, SimCCSystemVAMD64(arch), proto)
|
|
assert atoms == {Register(*arch.registers["rax"], arch=arch), Register(*arch.registers["rdx"], arch=arch)}
|
|
|
|
# on x86 cdecl, large structs are also returned through an implicit out-parameter
|
|
arch_x86 = archinfo.ArchX86()
|
|
state_x86 = SimpleNamespace(arch=arch_x86)
|
|
retty_x86 = SimStruct({"a": SimTypeLongLong(), "b": SimTypeLongLong()}, name="big").with_arch(arch_x86)
|
|
proto_x86 = SimTypeFunction([], retty_x86).with_arch(arch_x86)
|
|
atoms = FunctionHandler.c_return_as_atoms(state_x86, SimCCCdecl(arch_x86), proto_x86)
|
|
assert atoms == {Register(*arch_x86.registers["eax"], arch=arch_x86)}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|