mirror of
https://github.com/angr/angr
synced 2026-08-17 12:23:11 -04:00
260 lines
10 KiB
Python
Executable file
260 lines
10 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# pylint: disable=missing-class-docstring,no-self-use
|
|
from __future__ import annotations
|
|
|
|
__package__ = __package__ or "tests" # pylint:disable=redefined-builtin
|
|
|
|
import os
|
|
import struct
|
|
from unittest import TestCase, main
|
|
|
|
import archinfo
|
|
|
|
from angr import Project, load_shellcode, types
|
|
from angr.calling_conventions import (
|
|
SimCCMicrosoftAMD64,
|
|
SimCCMicrosoftFastcall,
|
|
SimCCRISCV64,
|
|
SimCCSystemVAMD64,
|
|
SimReferenceArgument,
|
|
SimRegArg,
|
|
SimStackArg,
|
|
SimTypeFixedSizeArray,
|
|
SimTypeFunction,
|
|
SimTypeInt,
|
|
default_cc,
|
|
)
|
|
from angr.sim_type import (
|
|
SimCppClass,
|
|
SimStructValue,
|
|
SimTypeChar,
|
|
SimTypeDouble,
|
|
SimTypeLongLong,
|
|
SimTypeRef,
|
|
parse_file,
|
|
)
|
|
|
|
from .common import bin_location
|
|
|
|
test_location = os.path.join(bin_location, "tests")
|
|
|
|
|
|
class TestCallingConvention(TestCase):
|
|
def test_SystemVAMD64_flatten_int(self):
|
|
arch = archinfo.arch_from_id("amd64")
|
|
cc = SimCCSystemVAMD64(arch)
|
|
|
|
int_type = SimTypeInt().with_arch(arch)
|
|
flattened_int = cc._flatten(int_type)
|
|
self.assertTrue(all(isinstance(key, int) for key in flattened_int))
|
|
self.assertTrue(all(isinstance(value, list) for value in flattened_int.values()))
|
|
for v in flattened_int.values():
|
|
for subtype in v:
|
|
self.assertIsInstance(subtype, SimTypeInt)
|
|
|
|
def test_SystemVAMD64_flatten_array(self):
|
|
arch = archinfo.arch_from_id("amd64")
|
|
cc = SimCCSystemVAMD64(arch)
|
|
|
|
int_type = SimTypeInt().with_arch(arch)
|
|
array_type = SimTypeFixedSizeArray(int_type, 20).with_arch(arch)
|
|
flattened_array = cc._flatten(array_type)
|
|
self.assertTrue(all(isinstance(key, int) for key in flattened_array))
|
|
self.assertTrue(all(isinstance(value, list) for value in flattened_array.values()))
|
|
for v in flattened_array.values():
|
|
for subtype in v:
|
|
self.assertIsInstance(subtype, SimTypeInt)
|
|
|
|
def test_arg_locs_array(self):
|
|
arch = archinfo.arch_from_id("amd64")
|
|
cc = SimCCSystemVAMD64(arch)
|
|
proto = SimTypeFunction([SimTypeFixedSizeArray(SimTypeInt().with_arch(arch), 2).with_arch(arch)], None)
|
|
|
|
# It should not raise any exception!
|
|
cc.arg_locs(proto)
|
|
|
|
def test_microsoft_fastcall_large_arg(self):
|
|
# Regression test: a >DWORD argument (e.g. __int64/double) landing on a register position
|
|
# must NOT raise "doesn't know how to store large types". Per the __fastcall ABI such
|
|
# arguments are passed on the stack and do not consume an ECX/EDX slot.
|
|
arch = archinfo.arch_from_id("x86")
|
|
cc = SimCCMicrosoftFastcall(arch)
|
|
|
|
def footprints(proto):
|
|
return [list(loc.get_footprint()) for loc in cc.arg_locs(proto.with_arch(arch))]
|
|
|
|
# __int64 first arg -> stack (two words); the following int still gets ECX.
|
|
assert footprints(SimTypeFunction([SimTypeLongLong(), SimTypeInt()], SimTypeInt())) == [
|
|
[SimStackArg(0x4, 4), SimStackArg(0x8, 4)],
|
|
[SimRegArg("ecx", 4)],
|
|
]
|
|
# Two small ints fill ECX/EDX, the __int64 spills to the stack.
|
|
assert footprints(SimTypeFunction([SimTypeInt(), SimTypeInt(), SimTypeLongLong()], SimTypeInt())) == [
|
|
[SimRegArg("ecx", 4)],
|
|
[SimRegArg("edx", 4)],
|
|
[SimStackArg(0x4, 4), SimStackArg(0x8, 4)],
|
|
]
|
|
# An __int64 between two ints: it skips the registers; the trailing int still gets EDX.
|
|
assert footprints(SimTypeFunction([SimTypeInt(), SimTypeLongLong(), SimTypeInt()], SimTypeInt())) == [
|
|
[SimRegArg("ecx", 4)],
|
|
[SimStackArg(0x4, 4), SimStackArg(0x8, 4)],
|
|
[SimRegArg("edx", 4)],
|
|
]
|
|
# Doubles are passed on the stack too and do not consume a register.
|
|
assert footprints(SimTypeFunction([SimTypeDouble(), SimTypeInt()], SimTypeInt())) == [
|
|
[SimStackArg(0x4, 4), SimStackArg(0x8, 4)],
|
|
[SimRegArg("ecx", 4)],
|
|
]
|
|
# A sub-DWORD integer still uses a register, refined to its size.
|
|
char_locs = cc.arg_locs(SimTypeFunction([SimTypeChar(), SimTypeInt()], SimTypeInt()).with_arch(arch))
|
|
assert isinstance(char_locs[0], SimRegArg) and char_locs[0].reg_name == "ecx" and char_locs[0].size == 1
|
|
assert char_locs[1] == SimRegArg("edx", 4)
|
|
|
|
def test_struct_ffi(self):
|
|
with open(os.path.join(test_location, "../tests_src/test_structs.c"), encoding="utf-8") as fp:
|
|
decls = parse_file(fp.read())
|
|
|
|
p = Project(os.path.join(test_location, "x86_64/test_structs.o"), auto_load_libs=False)
|
|
|
|
def make_callable(name):
|
|
return p.factory.callable(p.loader.find_symbol(name).rebased_addr, decls[0][name])
|
|
|
|
test_small_struct_return = make_callable("test_small_struct_return")
|
|
result = test_small_struct_return()
|
|
self.assertIsInstance(result, SimStructValue)
|
|
self.assertTrue((result.a == 1).is_true())
|
|
self.assertTrue((result.b == 2).is_true())
|
|
|
|
def test_array_ffi(self):
|
|
# NOTE: if this test is failing and you think it is wrong, you might be right :)
|
|
p = load_shellcode(b"\xc3", arch="amd64")
|
|
s = p.factory.blank_state()
|
|
s.regs.rdi = 123
|
|
s.regs.rsi = 456
|
|
s.regs.rdx = 789
|
|
execve = parse_file("int execve(const char *pathname, char *const argv[], char *const envp[]);")[0]["execve"]
|
|
cc = p.factory.cc()
|
|
assert all((x == y).is_true() for x, y in zip(cc.get_args(s, execve), (123, 456, 789)))
|
|
# however, this is definitely right
|
|
assert [list(loc.get_footprint()) for loc in cc.arg_locs(execve)] == [
|
|
[SimRegArg("rdi", 8)],
|
|
[SimRegArg("rsi", 8)],
|
|
[SimRegArg("rdx", 8)],
|
|
]
|
|
|
|
def test_microsoft_amd64(self):
|
|
arch = archinfo.ArchAMD64()
|
|
cc = SimCCMicrosoftAMD64(arch)
|
|
ty1 = parse_file("struct foo { int x; int y; };", arch=arch)[1]["struct foo"]
|
|
loc1 = cc.return_val(ty1, perspective_returned=True)
|
|
assert loc1 is not None
|
|
assert loc1.get_footprint() == {SimRegArg("rax", 8)}
|
|
loc2 = cc.return_val(ty1, perspective_returned=False)
|
|
assert loc2 is not None
|
|
assert loc2.get_footprint() == {SimRegArg("rax", 8)}
|
|
|
|
ty3 = parse_file("struct foo { short x; int y; short z; };", arch=arch)[1]["struct foo"]
|
|
loc3 = cc.return_val(ty3, perspective_returned=True)
|
|
assert isinstance(loc3, SimReferenceArgument)
|
|
assert loc3.ptr_loc == SimRegArg("rax", 8)
|
|
assert loc3.main_loc.get_footprint() == {SimStackArg(0, 2), SimStackArg(4, 4), SimStackArg(8, 2)}
|
|
loc4 = cc.return_val(ty3, perspective_returned=False)
|
|
assert isinstance(loc4, SimReferenceArgument)
|
|
assert loc4.ptr_loc == SimRegArg("rcx", 8)
|
|
assert loc4.main_loc.get_footprint() == {SimStackArg(0, 2), SimStackArg(4, 4), SimStackArg(8, 2)}
|
|
|
|
def test_riscv64_args_actual_values(self):
|
|
bin_path = os.path.join(test_location, "riscv64", "sim_args_riscv64.so")
|
|
src_location = os.path.join(bin_location, "tests_src")
|
|
|
|
proj = Project(bin_path, auto_load_libs=False)
|
|
|
|
symbol = proj.loader.find_symbol("complex_func")
|
|
func_addr = symbol.rebased_addr
|
|
cc = SimCCRISCV64(proj.arch)
|
|
|
|
c_decl = os.path.join(src_location, "arch", "riscv", "sim_args_riscv64.c")
|
|
with open(c_decl, encoding="utf-8") as f:
|
|
raw_content = f.read()
|
|
defns, _ = types.parse_file(raw_content)
|
|
proto = defns["complex_func"].with_arch(proj.arch)
|
|
|
|
args = [100, {"f": 1.0, "i": 2}, 3.0, {"x": 10.0, "y": 20.0, "z": 30.0}, 4, 5, 6, 7, 8, 9.0, 10, 11, 12.0]
|
|
|
|
state = proj.factory.call_state(func_addr, *args, cc=cc, prototype=proto)
|
|
|
|
assert state.solver.eval(state.regs.a0) == 100
|
|
|
|
fa0_val = state.solver.eval(state.regs.fa0[31:0].raw_to_fp())
|
|
a1_val = state.solver.eval(state.regs.a1[31:0])
|
|
assert fa0_val == 1.0
|
|
assert a1_val == 2
|
|
|
|
fa1_val = state.solver.eval(state.regs.fa1.raw_to_fp())
|
|
assert fa1_val == 3.0
|
|
|
|
s2_ptr = state.solver.eval(state.regs.a2)
|
|
s2_x = state.solver.eval(state.memory.load(s2_ptr, 8, endness="Iend_LE").raw_to_fp())
|
|
assert s2_x == 10.0
|
|
|
|
sp_val = state.solver.eval(state.regs.sp)
|
|
r9_on_stack = state.solver.eval(state.memory.load(sp_val, 8, endness="Iend_LE"))
|
|
assert r9_on_stack == 10
|
|
|
|
fa3_val = state.solver.eval(state.regs.fa3[31:0].raw_to_fp())
|
|
assert fa3_val == 12.0
|
|
|
|
def test_riscv64_args_flatten_actual_values(self):
|
|
bin_path = os.path.join(test_location, "riscv64", "sim_args_flatten_riscv64.so")
|
|
src_location = os.path.join(bin_location, "tests_src")
|
|
|
|
proj = Project(bin_path, auto_load_libs=False)
|
|
|
|
symbol = proj.loader.find_symbol("complex_func")
|
|
func_addr = symbol.rebased_addr
|
|
|
|
cc = SimCCRISCV64(proj.arch)
|
|
|
|
c_decl = os.path.join(src_location, "arch", "riscv", "sim_args_flatten_riscv64.c")
|
|
with open(c_decl, encoding="utf-8") as f:
|
|
raw_content = f.read()
|
|
defns, _ = types.parse_file(raw_content)
|
|
proto = defns["complex_func"].with_arch(proj.arch)
|
|
|
|
args = [{"f": 1.0, "i": 2}, {"x": 10, "y": 20}, {"a": 101.3, "c": 102.3, "d": 60}]
|
|
state = proj.factory.call_state(func_addr, *args, cc=cc, prototype=proto)
|
|
|
|
fa0_val = state.solver.eval(state.regs.fa0[31:0].raw_to_fp())
|
|
a0_val = state.solver.eval(state.regs.a0[31:0])
|
|
assert fa0_val == 1.0
|
|
assert a0_val == 2
|
|
|
|
a1_val = state.solver.eval(state.regs.a1)
|
|
assert (a1_val & 0xFFFFFFFF) == 10
|
|
assert (a1_val >> 32) == 20
|
|
|
|
a2_bits = state.solver.eval(state.regs.a2)
|
|
a3_val = state.solver.eval(state.regs.a3)
|
|
|
|
a2_float = struct.unpack("<d", struct.pack("<Q", a2_bits))[0]
|
|
assert abs(a2_float - 101.3) < 0.00001
|
|
|
|
c_bits = a3_val & 0xFFFFFFFF
|
|
c_float = struct.unpack("<f", struct.pack("<I", c_bits))[0]
|
|
assert abs(c_float - 102.3) < 0.00001
|
|
assert (a3_val >> 32) == 60
|
|
|
|
def test_simcc_arg_locs_returnty_unresolved_simtyperef(self):
|
|
func_proto = SimTypeFunction([], SimTypeRef("std::wstring_t", SimCppClass))
|
|
|
|
for arch in [archinfo.ArchAMD64, archinfo.ArchX86, archinfo.ArchARM]:
|
|
proto = func_proto.with_arch(arch())
|
|
cc = default_cc(arch.name)(arch())
|
|
|
|
# It should not raise any exception!
|
|
arg_locs = list(cc.arg_locs(proto))
|
|
assert arg_locs is not None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|