mirror of
https://github.com/angr/angr
synced 2026-06-08 13:09:39 +00:00
563fb5f862
* [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.14.11 → v0.14.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.11...v0.14.13) - [github.com/psf/black-pre-commit-mirror: 25.12.0 → 26.1.0](https://github.com/psf/black-pre-commit-mirror/compare/25.12.0...26.1.0) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
297 lines
13 KiB
Python
Executable File
297 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# pylint: disable=missing-class-docstring,no-self-use,line-too-long
|
|
from __future__ import annotations
|
|
|
|
__package__ = __package__ or "tests.analyses" # pylint:disable=redefined-builtin
|
|
|
|
import sys
|
|
import platform
|
|
import os
|
|
import tempfile
|
|
import subprocess
|
|
import shutil
|
|
import unittest
|
|
|
|
import angr
|
|
|
|
from tests.common import bin_location, has_32_bit_compiler_support
|
|
|
|
test_location = os.path.join(bin_location, "tests")
|
|
|
|
# Note: Reassembler is intensively tested by Patcherex test cases on CGC binaries.
|
|
|
|
|
|
def is_linux_x64():
|
|
return sys.platform.startswith("linux") and platform.machine().endswith("64")
|
|
|
|
|
|
def is_linux():
|
|
return sys.platform.startswith("linux")
|
|
|
|
|
|
class TestReassembler(unittest.TestCase):
|
|
def test_data_reference_collection_in_add(self):
|
|
# Issue reported and test binary provided by Antonio F. Montoya
|
|
# Fixed in https://github.com/angr/pyvex/pull/192
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "df_gcc_-O1"), auto_load_libs=False)
|
|
vexblock_opt0 = p.factory.block(0x402431, opt_level=0).vex
|
|
vexblock_opt1 = p.factory.block(0x402431, opt_level=1).vex
|
|
vexblock_opt1_nostmt = p.factory.block(0x402431, opt_level=1, collect_data_refs=True).vex_nostmt
|
|
|
|
cfg = p.analyses.CFG()
|
|
|
|
cfg._model.memory_data = {}
|
|
cfg._collect_data_references(vexblock_opt0, 0x402431)
|
|
memory_data_opt0 = cfg._model.memory_data
|
|
|
|
cfg._model.memory_data = {}
|
|
# bypass the IRSB unoptimization step
|
|
cfg._collect_data_references_by_scanning_stmts(vexblock_opt1, 0x402431)
|
|
memory_data_opt1 = cfg._model.memory_data
|
|
|
|
cfg._model.memory_data = {}
|
|
cfg._collect_data_references(vexblock_opt1_nostmt, 0x402431)
|
|
memory_data_opt1_nostmt = cfg._model.memory_data
|
|
|
|
assert memory_data_opt0.keys() == memory_data_opt1.keys()
|
|
assert memory_data_opt0.keys() == memory_data_opt1_nostmt.keys()
|
|
|
|
def test_ln_gcc_O2(self):
|
|
# Issue reported and test binary provided by Antonio F. Montoya
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "ln_gcc_-O2"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
# There should be two symbols with the same name: file_name. Reassembler renames the second one to file_name_0.
|
|
# Test their existence.
|
|
assert "\nfile_name:" in assembly and "\nfile_name_0:" in assembly
|
|
|
|
if is_linux_x64():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "ln_gcc-O2.s"
|
|
bin_filename = "ln_gcc-O2"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["gcc", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file, and it should not crash (which is a pretty basic requirement, I know)
|
|
subprocess.check_call([bin_filepath, "--help"], stdout=subprocess.DEVNULL)
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
def test_chmod_gcc_O1(self):
|
|
# Issue reported and test binary provided by Antonio F. Montoya
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "chmod_gcc_-O1"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
if is_linux_x64():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "chmod_gcc-O1.s"
|
|
bin_filename = "chmod_gcc-O1"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["gcc", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file, and it should not crash (which is a pretty basic requirement, I know)
|
|
subprocess.check_call([bin_filepath, "--help"], stdout=subprocess.DEVNULL)
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
def test_ex_gpp(self):
|
|
# Issue reported and test binary provided by Antonio F. Montoya
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "ex_g++"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
if is_linux_x64():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "ex_g++.s"
|
|
bin_filename = "ex_g++"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["g++", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file and check the output
|
|
output = subprocess.check_output([bin_filepath])
|
|
assert output == b"A1\nA2\n"
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
def test_df_gcc_O1(self):
|
|
# Issue reported and test binary provided by Antonio F. Montoya
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "df_gcc_-O1"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
if is_linux_x64():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "df_gcc-O1.s"
|
|
bin_filename = "df_gcc-O1"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["gcc", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file, and it should not crash (which is a pretty basic requirement, I know)
|
|
subprocess.check_call([bin_filepath, "--help"], stdout=subprocess.DEVNULL)
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
def test_dir_gcc_O0(self):
|
|
# Issue reported and test binary provided by Antonio F. Montoya
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "dir_gcc_-O0"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
if is_linux_x64():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "dir_gcc-O0.s"
|
|
bin_filename = "dir_gcc-O0"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["gcc", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file, and it should not crash (which is a pretty basic requirement, I know)
|
|
subprocess.check_call([bin_filepath, "--help"], stdout=subprocess.DEVNULL)
|
|
subprocess.check_call([bin_filepath, "-la", "/"], stdout=subprocess.DEVNULL)
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
def test_helloworld(self):
|
|
# Reassembler complains about TYPE_OTHER symbols, which is because it's trying to classify bytes inside the ELF
|
|
# header as pointers. We identify the ELF header in CFGFast to workaround this problem.
|
|
# https://github.com/angr/angr/issues/1630
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "hello_world"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
_ = r.assembly(comments=True, symbolized=True)
|
|
|
|
# No exception should have been raised
|
|
|
|
def test_helloworld_gcc9(self):
|
|
# New versions of GCC changed the names of init and fini sections.
|
|
# https://github.com/angr/patcherex/issues/39
|
|
|
|
p = angr.Project(os.path.join(test_location, "x86_64", "hello_gcc9_reassembler"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
if is_linux_x64():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "hello.s"
|
|
bin_filename = "hello"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["gcc", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file, and it should not crash
|
|
subprocess.check_call([bin_filepath], stdout=subprocess.DEVNULL)
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
def test_partial_pie_ls_x86(self):
|
|
# https://github.com/angr/patcherex/issues/39
|
|
# a GCC-generated X86 binary with a few functions somehow being PIE
|
|
|
|
p = angr.Project(os.path.join(test_location, "i386", "ls_gcc_7.5_reassembler"), auto_load_libs=False)
|
|
r = p.analyses.Reassembler(syntax="at&t")
|
|
r.symbolize()
|
|
r.remove_unnecessary_stuff()
|
|
assembly = r.assembly(comments=True, symbolized=True)
|
|
|
|
if is_linux() and has_32_bit_compiler_support():
|
|
# we should be able to compile it and run it ... if we are running on x64 Linux
|
|
tempdir = tempfile.mkdtemp(prefix="angr_test_reassembler_")
|
|
asm_filename = "ls.s"
|
|
bin_filename = "ls"
|
|
asm_filepath = os.path.join(tempdir, asm_filename)
|
|
bin_filepath = os.path.join(tempdir, bin_filename)
|
|
with open(asm_filepath, "w", encoding="ascii") as f:
|
|
f.write(assembly)
|
|
# Call out to GCC, and it should return 0. Otherwise check_call() will raise an exception.
|
|
subprocess.check_call(
|
|
["gcc", "-m32", "-no-pie", asm_filepath, "-o", bin_filepath],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
# Run the generated binary file, and it should not crash
|
|
subprocess.check_call([bin_filepath], stdout=subprocess.DEVNULL)
|
|
# We can also run it with "-h"
|
|
o = subprocess.check_output([bin_filepath, "--version"])
|
|
assert (
|
|
o == b"ls (GNU coreutils) 8.30\n"
|
|
b"Copyright (C) 2018 Free Software Foundation, Inc.\n"
|
|
b"License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n"
|
|
b"This is free software: you are free to change and redistribute it.\n"
|
|
b"There is NO WARRANTY, to the extent permitted by law.\n\n"
|
|
b"Written by Richard M. Stallman and David MacKenzie.\n"
|
|
)
|
|
# Pick up after ourselves
|
|
shutil.rmtree(tempdir)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|