#! /usr/bin/env python2 from __future__ import print_function import argparse import os import platform import subprocess import sys import tempfile import time import unittest from builtins import map from builtins import range from parameterized import parameterized from utils import cosmetics, multithread from utils.test import Test from utils.testset import TestSet is_win = platform.system() == "Windows" is_64bit = platform.architecture()[0] == "64bit" testset = TestSet("../") TAGS = {"regression": "REGRESSION", # Regression tests "example": "EXAMPLE", # Examples "long": "LONG", # Very time consumming tests "llvm": "LLVM", # LLVM dependency is required "gcc": "GCC", # GCC based tests "python": "PYTHON", # Python jitted tests "z3": "Z3", # Z3 dependency is needed "qemu": "QEMU", # QEMU tests (several tests) "cparser": "CPARSER", # pycparser is needed "linux": "LINUX", # Test must be run on a Linux } # Regression tests class RegressionTest(Test): """Regression tests specificities: - @base_dir: test/@base_dir - @tags: TAGS["regression"]""" sample_dir = os.path.join("..", "samples") def __init__(self, *args, **kwargs): super(RegressionTest, self).__init__(*args, **kwargs) self.base_dir = os.path.join("test", self.base_dir) self.tags.append(TAGS["regression"]) @classmethod def get_sample(cls, sample_name): "Return the relative path of @sample_name" return os.path.join(cls.sample_dir, sample_name) ## Architecture testset += RegressionTest(["x86/arch.py"], base_dir="arch", products=["x86_speed_reg_test.bin", "regression_test16_ia32.bin", "regression_test32_ia32.bin", "regression_test64_ia32.bin"]) testset += RegressionTest(["arm/arch.py"], base_dir="arch") testset += RegressionTest(["aarch64/arch.py"], base_dir="arch") testset += RegressionTest(["sh4/arch.py"], base_dir="arch") testset += RegressionTest(["msp430/arch.py"], base_dir="arch") testset += RegressionTest(["mips32/arch.py"], base_dir="arch") testset += RegressionTest(["ppc32/arch.py"], base_dir="arch") ### ArchUnit regression tests class ArchUnitTest(RegressionTest): """Test against arch unit regression tests""" jitter_engines = ["llvm", "gcc", "python"] def __init__(self, script, jitter ,*args, **kwargs): super(ArchUnitTest, self).__init__([script, jitter], *args, **kwargs) # script -> blacklisted jitter blacklist = { "x86/unit/mn_float.py": ["python", "llvm"], } for script in ["x86/sem.py", "x86/unit/mn_strings.py", "x86/unit/mn_float.py", "x86/unit/mn_stack.py", "x86/unit/mn_daa.py", "x86/unit/mn_das.py", "x86/unit/mn_int.py", "x86/unit/mn_pshufb.py", "x86/unit/mn_psrl_psll.py", "x86/unit/mn_pmaxu.py", "x86/unit/mn_pminu.py", "x86/unit/mn_pcmpeq.py", "x86/unit/mn_punpck.py", "x86/unit/mn_pinsr.py", "x86/unit/mn_pextr.py", "x86/unit/mn_pmovmskb.py", "x86/unit/mn_pushpop.py", "x86/unit/mn_cdq.py", "x86/unit/mn_seh.py", "x86/unit/mn_cpuid.py", "x86/unit/mn_div.py", "x86/unit/test_asm_x86_64.py", "x86/unit/mn_getset128.py", "x86/unit/mn_cmov.py", "x86/unit/mn_rotsh.py", "arm/sem.py", "aarch64/unit/mn_ubfm.py", "msp430/sem.py", "mips32/unit/mn_bcc.py", "ppc32/sem.py", ]: for jitter in ArchUnitTest.jitter_engines: if jitter in blacklist.get(script, []): continue tags = [TAGS[jitter]] if jitter in TAGS else [] if (not is_64bit) and script.endswith("mn_div.py"): continue testset += ArchUnitTest(script, jitter, base_dir="arch", tags=tags) testset += ArchUnitTest("x86/unit/access_xmm.py", "python", base_dir="arch", tags=[TAGS["python"]]) ### QEMU regression tests class QEMUTest(RegressionTest): """Test against QEMU regression tests An expected output is provided, computed on a x86 host""" SCRIPT_NAME = "testqemu.py" SAMPLE_NAME = "test-i386" EXPECTED_PATH = "expected" jitter_engines = ["llvm", "python", "gcc"] def __init__(self, name, jitter, *args, **kwargs): super(QEMUTest, self).__init__([self.SCRIPT_NAME], *args, **kwargs) self.base_dir = os.path.join(self.base_dir, "arch", "x86", "qemu") test_name = "test_%s" % name expected_output = os.path.join(self.EXPECTED_PATH, test_name) + ".exp" self.command_line += [self.SAMPLE_NAME, test_name, expected_output, "--jitter", jitter, ] self.tags.append(TAGS["qemu"]) # Test name -> supported jitter engines QEMU_TESTS = [ # Operations "btr", "bts", "bt", "shrd", "shld", "rcl", "rcr", "ror", "rol", "sar", "shr", "shl", "not", "neg", "dec", "inc", "sbb", "adc", "cmp", "or", "and", "xor", "sub", "add", # Specifics "bsx", "mul", "jcc", "loop", "lea", "self_modifying_code", "conv", "bcd", "xchg", "string", "misc", 'sse', # Unsupported # "floats", "segs", "code16", "exceptions", "single_step" ] for test_name in QEMU_TESTS: for jitter in QEMUTest.jitter_engines: if (test_name, jitter) in [("sse", "python")]: # SKIP unsupported continue tags = [TAGS[jitter]] if jitter in TAGS else [] testset += QEMUTest(test_name, jitter, tags=tags) class QEMUTestx86_64(QEMUTest): SAMPLE_NAME = "test-x86_64" SCRIPT_NAME = "testqemu64.py" EXPECTED_PATH = "expected_x86_64" # Test name -> supported jitter engines QEMU_TESTS_x86_64 = [ "adc", "add", "and", "btc", "bt", "btr", "bts", "cmp", "conv", "dec", "inc", "jcc", "lea", "misc", "mul", "neg", "not", "or", "rcl", "rcr", "rol", "ror", "sar", "sbb", "shld", "shl", "shrd", "shr", "string", "sub", "xor", # Unsupported # "sse", "floats", "loop", "xchg", "fxsave" ] for test_name in QEMU_TESTS_x86_64: for jitter in QEMUTestx86_64.jitter_engines: if is_win and jitter == "llvm" and test_name in [ "mul", "rcl", "rcr" ]: continue tags = [TAGS[jitter]] if jitter in TAGS else [] testset += QEMUTestx86_64(test_name, jitter, tags=tags) ## Semantic class SemanticTestAsm(RegressionTest): """Assemble an asm file""" shellcode_script = os.path.join("..", "example", "asm", "shellcode.py") container_dct = {"PE": "--PE"} def __init__(self, arch, container, *args, **kwargs): super(SemanticTestAsm, self).__init__(*args, **kwargs) sample_dir = os.path.join("samples", arch) base_filename = os.path.join(sample_dir, self.command_line[0]) input_filename = base_filename + ".S" output_filename = base_filename + ".bin" self.command_line = [self.shellcode_script, arch, input_filename, output_filename] if container in self.container_dct: self.command_line.append(self.container_dct[container]) self.products = [output_filename, "graph.dot"] class SemanticTestExec(RegressionTest): """Execute a binary file""" launcher_dct = {("PE", "x86_64"): "sandbox_pe_x86_64.py", ("PE", "x86_32"): "sandbox_pe_x86_32.py", } launcher_base = os.path.join("..", "example", "jitter") def __init__(self, arch, container, address, *args, **kwargs): super(SemanticTestExec, self).__init__(*args, **kwargs) sample_dir = os.path.join("samples", arch) base_filename = os.path.join(sample_dir, self.command_line[0]) input_filename = base_filename + ".bin" launcher = os.path.join(self.launcher_base, self.launcher_dct[(container, arch)]) self.command_line = [launcher, input_filename, "-a", hex(address)] self.products = [] self.tags.append(TAGS["gcc"]) test_x86_64_mul_div = SemanticTestAsm("x86_64", "PE", ["mul_div"]) test_x86_32_bsr_bsf = SemanticTestAsm("x86_32", "PE", ["bsr_bsf"]) testset += test_x86_64_mul_div testset += test_x86_32_bsr_bsf testset += SemanticTestExec("x86_64", "PE", 0x401000, ["mul_div"], depends=[test_x86_64_mul_div]) testset += SemanticTestExec("x86_32", "PE", 0x401000, ["bsr_bsf"], depends=[test_x86_32_bsr_bsf]) ## Core for script in ["modint.py", "interval.py", "graph.py", "parse_asm.py", "utils.py", "sembuilder.py", "locationdb.py", "test_types.py", ]: testset += RegressionTest([script], base_dir="core") testset += RegressionTest(["asmblock.py"], base_dir="core", products=["graph.dot", "graph2.dot", "graph3.dot", "graph4.dot"]) ## Expression for script in ["expression.py", "stp.py", "simplifications.py", "expression_helper.py", "expr_pickle.py", "parser.py", "expr_cmp.py", ]: testset += RegressionTest([script], base_dir="expression") testset += RegressionTest(["simplifications.py", "--z3"], base_dir="expression", tags=[TAGS["z3"]]) testset += RegressionTest(["z3_div.py"], base_dir="expression", tags=[TAGS["z3"]]) ## ObjC/CHandler testset += RegressionTest(["test_chandler.py"], base_dir="expr_type", tags=[TAGS["cparser"]]) ## IR for script in ["symbexec.py", "ir.py", "reduce_graph.py" ]: testset += RegressionTest([script], base_dir="ir") testset += RegressionTest(["z3_ir.py"], base_dir="ir/translators", tags=[TAGS["z3"]]) testset += RegressionTest(["smt2.py"], base_dir="ir/translators", tags=[TAGS["z3"]]) ## OS_DEP for script in ["common.py", "win_api_x86_32.py", os.path.join("linux", "stdlib.py"), ]: testset += RegressionTest([script], base_dir="os_dep", tags=[TAGS['gcc']]) for arch in ["x86_32", "x86_64", "arml", "aarch64l"]: testset += RegressionTest(["test_env.py", arch, "test_env.%s" % arch, "-c", "arg1", "-c", "arg2", "--environment-vars", "TEST=TOTO", "--mimic-env"], base_dir="os_dep/linux", tags=[TAGS['gcc']]) ## Analysis testset += RegressionTest(["depgraph.py"], base_dir="analysis", products=[fname for fnames in ( ["graph_test_%02d_00.dot" % test_nb, "graph_%02d.dot" % test_nb] for test_nb in range(1, 18)) for fname in fnames] + ["graph_test_%02d_%02d.dot" % (test_nb, res_nb) for (test_nb, res_nb) in ((3, 1), (5, 1), (8, 1), (9, 1), (10, 1), (12, 1), (12, 2), (13, 1), (13, 2), (14, 1), (14, 2), (15, 1)) ]) testset += RegressionTest(["modularintervals.py"], base_dir="analysis") for jitter in ArchUnitTest.jitter_engines: if jitter in blacklist.get(script, []): continue tags = [TAGS[jitter]] if jitter in TAGS else [] testset += RegressionTest(["dse.py", jitter], base_dir="analysis", tags=tags) testset += RegressionTest(["range.py"], base_dir="analysis", tags=[TAGS["z3"]]) testset += RegressionTest(["data_flow.py"], base_dir="analysis", products=[fname for fnames in ( ["simp_graph_%02d.dot" % test_nb, "graph_%02d.dot" % test_nb] for test_nb in range(1, 18)) for fname in fnames]) testset += RegressionTest(["unssa.py"], base_dir="analysis") for i in range(1, 21): input_name = "cst_propag/x86_32_sc_%d" % i bin_name = "samples/x86_32/%s.bin" % input_name test_x86_32_cst = SemanticTestAsm("x86_32", None, [input_name]) testset+= test_x86_32_cst testset += RegressionTest(["../example/expression/constant_propagation.py", "-s", bin_name, "0"], depends=[test_x86_32_cst], products=["%s.propag.dot" % bin_name]) ## Degraph class TestDepgraph(RegressionTest): """Dependency graph test""" example_depgraph = os.path.join("..", "..", "example", "symbol_exec", "depgraph.py") launcher = "dg_check.py" def __init__(self, test_nb, implicit, base_addr, target_addr, elements, nb_sol, *args, **kwargs): super(TestDepgraph, self).__init__([self.launcher], *args, **kwargs) self.base_dir = os.path.join(self.base_dir, "analysis") self.products = ["sol_%d.dot" % i for i in range(nb_sol)] if implicit: expected_fname = "dg_test_%.2d_implicit_expected.json" self.tags.append(TAGS["z3"]) else: expected_fname = "dg_test_%.2d_expected.json" self.command_line += [ expected_fname % test_nb, self.example_depgraph, "-m", "x86_32", "--json", self.get_sample(os.path.join("x86_32", "dg_test_%.2d.bin" % test_nb)), hex(base_addr), hex(target_addr)] + elements if implicit: self.command_line.append("-i") # Depgraph emulation regression test test_args = [(0x401000, 0x40100d, ["EAX"], 1), (0x401000, 0x401011, ["EAX"], 1), (0x401000, 0x401018, ["EAX"], 2), (0x401000, 0x401011, ["EAX"], 2), (0x401000, 0x401011, ["EAX"], 1), (0x401000, 0x401016, ["EAX"], 1), (0x401000, 0x401017, ["EAX"], 2), (0x401000, 0x401012, ["EAX", "ECX"], 1), (0x401000, 0x401012, ["ECX"], 1), (0x401000, 0x40101f, ["EAX", "EBX"], 2), (0x401000, 0x401025, ["EAX", "EBX"], 4), (0x401000, 0x401007, ["EBX"], 3), ] for i, test_args in enumerate(test_args): test_dg = SemanticTestAsm("x86_32", "PE", ["dg_test_%.2d" % i]) testset += test_dg testset += TestDepgraph(i, False, *test_args, depends=[test_dg]) testset += TestDepgraph(i, True, *test_args, depends=[test_dg]) ## Jitter for script in ["jitload.py", "vm_mngr.py", "jit_options.py", "jitcore.py", "test_post_instr.py", "bad_block.py", "jmp_out_mem.py", "mem_breakpoint.py", ]: for engine in ArchUnitTest.jitter_engines: testset += RegressionTest([script, engine], base_dir="jitter", tags=[TAGS.get(engine,None)]) # Examples class Example(Test): """Examples specificities: - @base_dir: example/@base_dir - @tags: TAGS["example"]""" # Directory containing samples for examples sample_dir = os.path.join("..", "samples") example_dir = "" def __init__(self, *args, **kwargs): if not self.example_dir: raise NotImplementedError("ExampleDir should be inherited") super(Example, self).__init__(*args, **kwargs) self.base_dir = os.path.join(self.base_dir, "example", self.example_dir) self.tags.append(TAGS["example"]) @classmethod def get_sample(cls, sample_name): "Return the relative path of @sample_name" return os.path.join(cls.sample_dir, sample_name) ## Assembler class ExampleAssembler(Example): """Assembler examples specificities: - script path begins with "asm/" """ example_dir = "asm" testset += ExampleAssembler(["simple.py"]) class ExampleShellcode(ExampleAssembler): """Specificities: - script: asm/shellcode.py - @products: graph.dot + 3rd arg - apply get_sample on each products (!= graph.dot) - apply get_sample on the 2nd and 3rd arg (source, output) """ def __init__(self, *args, **kwargs): super(ExampleShellcode, self).__init__(*args, **kwargs) self.command_line = ["shellcode.py", self.command_line[0]] + \ list(map(Example.get_sample, self.command_line[1:3])) + \ self.command_line[3:] self.products = [self.command_line[3], "graph.dot"] testset += ExampleShellcode(['x86_32', 'x86_32_manip_ptr.S', "demo_x86_32.bin"]) test_box = {} test_box_names = ["mod", "mod_self", "repmod", "simple", "enc", "pop_esp", "automod"] for source in test_box_names: sample_base = "x86_32_" + source args = ["x86_32", sample_base + ".S", sample_base + ".bin", "--PE"] if source == "enc": args += ["--encrypt","msgbox_encrypted_start", "msgbox_encrypted_stop"] test_box[source] = ExampleShellcode(args) testset += test_box[source] test_armb = ExampleShellcode(["armb", "arm_simple.S", "demo_arm_b.bin"]) test_arml = ExampleShellcode(["arml", "arm_simple.S", "demo_arm_l.bin"]) test_aarch64b = ExampleShellcode(["aarch64b", "aarch64_simple.S", "demo_aarch64_b.bin"]) test_aarch64l = ExampleShellcode(["aarch64l", "aarch64_simple.S", "demo_aarch64_l.bin"]) test_armb_sc = ExampleShellcode(["armb", "arm_sc.S", "demo_arm2_b.bin"]) test_arml_sc = ExampleShellcode(["arml", "arm_sc.S", "demo_arm2_l.bin"]) test_armtb = ExampleShellcode(["armtb", "armt.S", "demo_armt_b.bin"]) test_armtl = ExampleShellcode(["armtl", "armt.S", "demo_armt_l.bin"]) test_msp430 = ExampleShellcode(["msp430", "msp430.S", "msp430_sc.bin"]) test_mips32b = ExampleShellcode(["mips32b", "mips32.S", "mips32_sc_b.bin"]) test_mips32l = ExampleShellcode(["mips32l", "mips32.S", "mips32_sc_l.bin"]) test_x86_64 = ExampleShellcode(["x86_64", "x86_64.S", "demo_x86_64.bin", "--PE"]) test_x86_32_if_reg = ExampleShellcode(['x86_32', 'x86_32_if_reg.S', "x86_32_if_reg.bin"]) test_x86_32_seh = ExampleShellcode(["x86_32", "x86_32_seh.S", "x86_32_seh.bin", "--PE"]) test_x86_32_dead = ExampleShellcode(['x86_32', 'x86_32_dead.S', "x86_32_dead.bin"]) test_x86_32_automod_2 = ExampleShellcode( [ 'x86_32', 'x86_32_automod_2.S', "x86_32_automod_2.bin", "--PE" ] ) test_x86_32_dis = ExampleShellcode( [ "x86_32", "test_x86_32_dis.S", "test_x86_32_dis.bin", "--PE" ] ) test_human = ExampleShellcode(["x86_64", "human.S", "human.bin"]) testset += test_armb testset += test_arml testset += test_aarch64b testset += test_aarch64l testset += test_armb_sc testset += test_arml_sc testset += test_armtb testset += test_armtl testset += test_msp430 testset += test_mips32b testset += test_mips32l testset += test_x86_64 testset += test_x86_32_if_reg testset += test_x86_32_seh testset += test_x86_32_dead testset += test_human testset += test_x86_32_dis testset += test_x86_32_automod_2 class ExampleDisassembler(Example): """Disassembler examples specificities: - script path begins with "disasm/" """ example_dir = "disasm" for script, prods, depends in [ (["single_instr.py"], [], []), (["callback.py"], [], []), (["dis_x86_string.py"], ["str_cfg.dot"], []), (["dis_binary.py", Example.get_sample("test_x86_32_dis.bin"), ], ["bin_cfg.dot"], [test_x86_32_dis]), (["dis_binary_lift.py", Example.get_sample("test_x86_32_dis.bin"), ], ["bin_ir_cfg.dot"], [test_x86_32_dis]), (["dis_binary_lift_model_call.py", Example.get_sample("test_x86_32_dis.bin"), ], ["bin_lifter_model_call_cfg.dot"], [test_x86_32_dis]), (["full.py", Example.get_sample("box_upx.exe")], ["graph_execflow.dot", "lines.dot"], []), ]: testset += ExampleDisassembler(script, products=prods, depends=depends) class ExampleDisasmFull(ExampleDisassembler): """DisasmFull specificities: - script: disasm/full.py - flags: -g -s - @products: graph_execflow.dot, graph_irflow.dot, graph_irflow_raw.dot, lines.dot, out.dot """ def __init__(self, *args, **kwargs): super(ExampleDisasmFull, self).__init__(*args, **kwargs) self.command_line = ["full.py", "-g", "-ss", "-d", "-p", "-x", "-m"] + self.command_line self.products += ["graph_defuse.dot", "graph_execflow.dot", "ssa.dot", "graph_irflow.dot", "graph_irflow_raw.dot", "lines.dot", "graph_irflow_reduced.dot"] testset += ExampleDisasmFull(["arml", Example.get_sample("demo_arm_l.bin"), "0x2c", "-z"], depends=[test_arml]) testset += ExampleDisasmFull(["armb", Example.get_sample("demo_arm_b.bin"), "0x2c", "-z"], depends=[test_armb]) testset += ExampleDisasmFull(["arml", Example.get_sample("demo_arm2_l.bin"), "0x0", "-z"], depends=[test_arml_sc]) testset += ExampleDisasmFull(["armb", Example.get_sample("demo_arm2_b.bin"), "0x0", "-z"], depends=[test_armb_sc]) testset += ExampleDisasmFull(["armtl", Example.get_sample("demo_armt_l.bin"), "0x2c", "-z"], depends=[test_armtl]) testset += ExampleDisasmFull(["armtb", Example.get_sample("demo_armt_b.bin"), "0x2c", "-z"], depends=[test_armtb]) testset += ExampleDisasmFull(["aarch64l", Example.get_sample("demo_aarch64_l.bin"), "0"], depends=[test_aarch64l]) testset += ExampleDisasmFull(["aarch64b", Example.get_sample("demo_aarch64_b.bin"), "0"], depends=[test_aarch64b]) testset += ExampleDisasmFull(["x86_32", Example.get_sample("x86_32_simple.bin"), "0x401000"], depends=[test_box["simple"]]) testset += ExampleDisasmFull(["x86_32", Example.get_sample("x86_32_if_reg.bin"), "0x0"], depends=[test_x86_32_if_reg]) testset += ExampleDisasmFull(["msp430", Example.get_sample("msp430_sc.bin"), "0"], depends=[test_msp430]) testset += ExampleDisasmFull(["mips32l", Example.get_sample("mips32_sc_l.bin"), "0"], depends=[test_mips32l]) testset += ExampleDisasmFull(["mips32b", Example.get_sample("mips32_sc_b.bin"), "0"], depends=[test_mips32b]) testset += ExampleDisasmFull(["x86_64", Example.get_sample("demo_x86_64.bin"), "0x401000"], depends=[test_x86_64]) testset += ExampleDisasmFull(["aarch64l", Example.get_sample("md5_aarch64l"), "0x400A00"], depends=[test_aarch64l]) testset += ExampleDisasmFull(["ppc32b", Example.get_sample("md5_ppc32b"), "0x1000087C"]) testset += ExampleDisasmFull(["x86_32", os.path.join("..", "..", "test", "arch", "x86", "qemu", "test-i386"), "func_iret"]) testset += ExampleDisasmFull(["x86_32", Example.get_sample("x86_32_dead.bin"), "0"], depends=[test_x86_32_dead]) ## Expression class ExampleExpression(Example): """Expression examples specificities: - script path begins with "expression/" """ example_dir = "expression" for args in [[], ["--symb"]]: testset += ExampleExpression(["graph_dataflow.py", Example.get_sample("simple_test.bin"), "0x0"] + args, products=["data.dot"]) testset += ExampleExpression(["asm_to_ir.py"], products=["graph.dot", "graph2.dot"]) testset += ExampleExpression(["get_read_write.py"], products=["graph_instr.dot"]) testset += ExampleExpression(["solve_condition_stp.py", Example.get_sample("simple_test.bin")], products=["graph_instr.dot", "out.dot"]) testset += ExampleExpression(["access_c.py", Example.get_sample("human.bin")], depends=[test_human], products=["graph_irflow.dot"], tags=[TAGS["cparser"]]) testset += ExampleExpression(["expr_c.py"], tags=[TAGS["cparser"]]) testset += ExampleExpression(["constant_propagation.py", Example.get_sample("simple_test.bin"), "-s", "0"], products=["%s.propag.dot" % Example.get_sample("simple_test.bin")]) testset += ExampleExpression(["export_llvm.py", "-a", "x86_32", Example.get_sample("simple_test.bin"), "0"], products=["out.ll"]) for script in [["basic_op.py"], ["basic_simplification.py"], ["simplification_tools.py"], ["expr_grapher.py"], ["simplification_add.py"], ["expr_random.py"], ["expr_translate.py"], ["expr_reduce.py"], ["interfer.py"], ]: testset += ExampleExpression(script) ## Loader class ExampleLoader(Example): """Loader examples specificities: - script path begins with "loader/" """ example_dir = "loader" testset += ExampleLoader(["build_pe.py"], products=["fresh_pe.exe"]) # A sample is required, so "minidump_to_pe.py" is disabled for now ## Symbolic Execution class ExampleSymbolExec(Example): """Symbol Exec examples specificities: - script path begins with "symbol_exec/" """ example_dir = "symbol_exec" testset += ExampleSymbolExec(["single_instr.py"]) testset += ExampleSymbolExec(["symbol_exec.py", "--steps", Example.get_sample("box_upx.exe")]) for options, nb_sol, tag in [([], 8, []), (["-i", "--rename-args"], 12, [TAGS["z3"]])]: testset += ExampleSymbolExec(["depgraph.py", Example.get_sample("simple_test.bin"), "-m", "x86_32", "0x0", "0x8b", "EAX"] + options, products=["sol_%d.dot" % nb for nb in range(nb_sol)], tags=tag) for options, nb_sol, tag in [([], 4, []), (["-i", "--rename-args"], 4, [TAGS["z3"]])]: testset += ExampleSymbolExec(["depgraph.py", Example.get_sample("x86_32_if_reg.bin"), "-m", "x86_32", "0x0", "0x19", "EAX"] + options, products=["sol_%d.dot" % nb for nb in range(nb_sol)], depends=[test_x86_32_if_reg], tags=tag) dse_crackme_out = Example.get_sample("dse_crackme") for strategy in ["code-cov", "branch-cov", "path-cov"]: testset += ExampleSymbolExec(["dse_crackme.py", dse_crackme_out, "--strategy", strategy], tags=[TAGS["z3"]]) testset += ExampleSymbolExec(["dse_strategies.py", Example.get_sample("simple_test.bin"), strategy], tags=[TAGS["z3"]]) ## Jitter class ExampleJitter(Example): """Jitter examples specificities: - script path begins with "jitter/" """ example_dir = "jitter" jitter_engines = ["llvm", "python", "gcc"] class ExampleJitterNoPython(ExampleJitter): """Jitter examples specificities: - script path begins with "jitter/" Run jitting script without python support """ jitter_engines = ["llvm", "gcc"] for jitter in ExampleJitter.jitter_engines: # Take 5 min on a Core i5 tags = {"python": [TAGS["long"], TAGS["python"]], "llvm": [TAGS["llvm"]], "gcc": [TAGS["gcc"]], } testset += ExampleJitter(["unpack_upx.py", Example.get_sample("box_upx.exe")] + ["--jitter", jitter], products=[Example.get_sample("box_upx_exe_unupx.bin")], tags=tags.get(jitter, [])) testset += ExampleJitter(["unpack_generic.py", Example.get_sample("box_upx.exe")] + ["--jitter", jitter, "-o"], products=[Example.get_sample("box_upx.exe.dump")], tags=tags.get(jitter, [])) testset += ExampleJitter(["memory_breakpoint.py", Example.get_sample("box_upx.exe")] + ["--jitter", jitter] + ["-o", "0x401130", "0x100", "--access", "rw"], tags=tags.get(jitter, [])) for script, dep in [(["x86_32.py", Example.get_sample("x86_32_sc.bin")], []), (["arm.py", Example.get_sample("md5_arm"), "--mimic-env"], []), (["sandbox_elf_aarch64l.py", Example.get_sample("md5_aarch64l"), "--mimic-env"], []), (["sandbox_elf_ppc32.py", Example.get_sample("md5_ppc32b"), "--mimic-env"], []), (["msp430.py", Example.get_sample("msp430_sc.bin"), "0"], [test_msp430]), (["mips32.py", Example.get_sample("mips32_sc_l.bin"), "0"], [test_mips32l]), (["arm_sc.py", "0", Example.get_sample("demo_arm_b.bin"), "b", "-a", "0"], [test_armb]), (["arm_sc.py", "0", Example.get_sample("demo_arm_l.bin"), "l", "-a", "0"], [test_arml]), (["sandbox_call.py", Example.get_sample("md5_arm")], []), (["sandbox_pe_x86_32.py", Example.get_sample("x86_32_automod_2.bin")], [test_x86_32_automod_2]) ] + [(["sandbox_pe_x86_32.py", Example.get_sample("x86_32_" + name + ".bin")], [test_box[name]]) for name in test_box_names]: for jitter in ExampleJitter.jitter_engines: tags = [TAGS[jitter]] if jitter in TAGS else [] testset += ExampleJitter(script + ["--jitter", jitter], depends=dep, tags=tags) for jitter in ExampleJitterNoPython.jitter_engines: tags = [TAGS[jitter]] if jitter in TAGS else [] testset += ExampleJitterNoPython(["test_x86_32_seh.py", Example.get_sample("x86_32_seh.bin")] + ["--jitter", jitter], depends=[test_x86_32_seh], tags=tags) testset += ExampleJitter(["example_types.py"]) testset += ExampleJitter(["trace.py", Example.get_sample("md5_arm"), "-a", "0xA684"]) ## Toshiba MeP testset += RegressionTest(["launch.py"], base_dir="arch/mep/asm") testset += RegressionTest(["launch.py"], base_dir="arch/mep/ir") testset += RegressionTest(["launch.py"], base_dir="arch/mep/jit", tags=[TAGS["gcc"]]) # region Unittest compatibility class TestSequence(unittest.TestCase): # Compatibility layer for Python's unittest module # Instead of calling the '__main__' defined below, we parameterize a single test with all the tests selected in # testset, and run them as we would have. tests = testset.tests tests_without_shellcodes = (t for t in tests if "shellcode.py" not in t.command_line[0]) @staticmethod def run_process(t): """ @type t: Test """ print("Base dir:", t.base_dir) print("Command: ", t.command_line) print("Depends: ", [t.command_line for t in t.depends]) print("Tags: ", t.tags) print("Products:", t.products) executable = t.executable if t.executable else sys.executable print("Exec: ", executable, "(explicit)" if t.executable else "(default)") for t in t.depends: assert "shellcode.py" in t.command_line[0], "At the moment, only dependencies on 'shellcode.py' are handled" subprocess.check_call( [executable] + t.command_line, cwd=testset.base_dir + t.base_dir, ) print("Done") @classmethod def setUpClass(cls): for t in testset.tests: if "shellcode.py" in t.command_line[0]: print("\n*** Shellcode generation ***") cls.run_process(t) @parameterized.expand(("_".join(test.command_line), test) for test in tests_without_shellcodes) def test(self, name, t): print("***", name, "***") TestSequence.run_process(t) # endregion if __name__ == "__main__": # Argument parsing parser = argparse.ArgumentParser(description="Miasm2 testing tool") parser.add_argument("-m", "--mono", help="Force monothreading", action="store_true") parser.add_argument("-c", "--coverage", help="Include code coverage", action="store_true") parser.add_argument("-t", "--omit-tags", help="Omit tests based on tags \ (tag1,tag2). Available tags are %s. \ By default, no tag is omitted." % ", ".join(list(TAGS)), default="") parser.add_argument("-o", "--only-tags", help="Restrict to tests based on tags \ (tag1,tag2). Available tags are %s. \ By default, all tag are considered." % ", ".join(list(TAGS)), default="") parser.add_argument("-n", "--do-not-clean", help="Do not clean tests products", action="store_true") args = parser.parse_args() ## Parse multiproc argument multiproc = True if args.mono is True or args.coverage is True: multiproc = False ## Parse omit-tags and only-tags argument exclude_tags = [] include_tags = [] for dest, src in ((exclude_tags, args.omit_tags), (include_tags, args.only_tags)): for tag in src.split(","): if not tag: continue if tag not in TAGS: print("%(red)s[TAG]%(end)s" % cosmetics.colors, \ "Unknown tag '%s'" % tag) exit(-1) dest.append(TAGS[tag]) if exclude_tags and include_tags: print("%(red)s[TAG]%(end)s" % cosmetics.colors, \ "Omit and Only used together: whitelist mode") # Handle coverage coveragerc = None if args.coverage is True: try: import coverage except ImportError: print("%(red)s[Coverage]%(end)s " % cosmetics.colors + \ "Python 'coverage' module is required") exit(-1) # Create directory suffix = "_" + str(int(time.time())) cov_dir = tempfile.mkdtemp(suffix, "m2_coverage_") # Create configuration file coveragerc = os.path.join(cov_dir, ".coveragerc") coverage = os.path.join(cov_dir, ".coverage") from configparser import ConfigParser from os.path import expanduser config = ConfigParser() config.read(['/etc/coveragerc', expanduser('~/.coveragerc')]) if not config.has_section('run'): config.add_section('run') config.set('run', 'data_file', coverage) config.write(open(coveragerc, 'w')) # Add arguments to tests command line testset.add_additional_args(["-m", "coverage", "run", "--rcfile", coveragerc, "-a"]) # Inform the user d = {"blue": cosmetics.colors['blue'], "end": cosmetics.colors['end'], "cov_dir": cov_dir} print("[%(blue)sCoverage%(end)s] Report will be written in %(cov_dir)s" % d) # Handle llvm modularity llvm = True try: import llvmlite except ImportError: llvm = False if llvm is False: print("%(red)s[LLVM]%(end)s Python" % cosmetics.colors + \ "'llvmlite' module is required for llvm tests") # Remove llvm tests if TAGS["llvm"] not in exclude_tags: exclude_tags.append(TAGS["llvm"]) # Handle Z3 dependency try: import z3 except: print("%(red)s[Z3]%(end)s " % cosmetics.colors + \ "Z3 and its python binding are necessary for TranslatorZ3.") if TAGS["z3"] not in exclude_tags: exclude_tags.append(TAGS["z3"]) # Handle pycparser dependency try: import pycparser except ImportError: print("%(red)s[PYCPARSER]%(end)s " % cosmetics.colors + \ "pycparser are necessary for Objc.") if TAGS["cparser"] not in exclude_tags: exclude_tags.append(TAGS["cparser"]) # Handle Linux tests if platform.system() != "Linux": if TAGS["linux"] not in exclude_tags: exclude_tags.append(TAGS["linux"]) test_ko = [] test_ok = [] # Set callbacks if multiproc is False: testset.set_cpu_numbers(1) testset.set_callback(task_done=lambda test, error:multithread.task_done(test, error, test_ok, test_ko), task_new=multithread.task_new) # Filter testset according to tags testset.filter_tags(exclude_tags=exclude_tags, include_tags=include_tags) # Run tests testset.run() # Finalize testset.end(clean=not args.do_not_clean) print() print (cosmetics.colors["green"] + "Result: %d/%d pass" % (len(test_ok), len(test_ok) + len(test_ko)) + cosmetics.colors["end"]) for test, error in test_ko: command_line = " ".join(test.command_line) print(cosmetics.colors["red"] + 'ERROR', cosmetics.colors["lightcyan"] + command_line + cosmetics.colors["end"]) print(error) # Exit with an error if at least a test failed exit(testset.tests_passed())