# -*- coding: utf-8 -*- # Copyright (C) 2020 FireEye, Inc. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: [package root]/LICENSE.txt # Unless required by applicable law or agreed to in writing, software distributed under the License # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and limitations under the License. import os import os.path import binascii import contextlib import collections from functools import lru_cache import pytest import capa.main import capa.features.file import capa.features.insn import capa.features.common import capa.features.basicblock from capa.features.common import ARCH_X32, ARCH_X64 CD = os.path.dirname(__file__) @contextlib.contextmanager def xfail(condition, reason=None): """ context manager that wraps a block that is expected to fail in some cases. when it does fail (and is expected), then mark this as pytest.xfail. if its unexpected, raise an exception, so the test fails. example:: # this test: # - passes on Linux if foo() works # - fails on Linux if foo() fails # - xfails on Windows if foo() fails # - fails on Windows if foo() works with xfail(sys.platform == "win32", reason="doesn't work on Windows"): foo() """ try: # do the block yield except: if condition: # we expected the test to fail, so raise and register this via pytest pytest.xfail(reason) else: # we don't expect an exception, so the test should fail raise else: if not condition: # here we expect the block to run successfully, # and we've received no exception, # so this is good pass else: # we expected an exception, but didn't find one. that's an error. raise RuntimeError("expected to fail, but didn't") # need to limit cache size so GitHub Actions doesn't run out of memory, see #545 @lru_cache(maxsize=1) def get_viv_extractor(path): import capa.main import capa.features.extractors.viv.extractor sigpaths = [ os.path.join(CD, "..", "sigs", "test_aulldiv.pat"), os.path.join(CD, "..", "sigs", "test_aullrem.pat.gz"), os.path.join(CD, "..", "sigs", "flare_common_libs.sig"), os.path.join(CD, "..", "sigs", "flare_msvc_atlmfc_32_64.sig"), os.path.join(CD, "..", "sigs", "flare_msvc_rtf_32_64.sig"), ] if "raw32" in path: vw = capa.main.get_workspace(path, "sc32", sigpaths=sigpaths) elif "raw64" in path: vw = capa.main.get_workspace(path, "sc64", sigpaths=sigpaths) else: vw = capa.main.get_workspace(path, "auto", sigpaths=sigpaths) extractor = capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path) fixup_viv(path, extractor) return extractor def fixup_viv(path, extractor): """ vivisect fixups to overcome differences between backends """ if "3b13b" in path: # vivisect only recognizes calling thunk function at 0x10001573 extractor.vw.makeFunction(0x10006860) @lru_cache() def get_smda_extractor(path): from smda.SmdaConfig import SmdaConfig from smda.Disassembler import Disassembler import capa.features.extractors.smda.extractor config = SmdaConfig() config.STORE_BUFFER = True disasm = Disassembler(config) report = disasm.disassembleFile(path) return capa.features.extractors.smda.extractor.SmdaFeatureExtractor(report, path) @lru_cache(maxsize=1) def get_pefile_extractor(path): import capa.features.extractors.pefile return capa.features.extractors.pefile.PefileFeatureExtractor(path) @lru_cache() def extract_file_features(extractor): features = collections.defaultdict(set) for feature, va in extractor.extract_file_features(): features[feature].add(va) return features # f may not be hashable (e.g. ida func_t) so cannot @lru_cache this def extract_function_features(extractor, f): features = collections.defaultdict(set) for bb in extractor.get_basic_blocks(f): for insn in extractor.get_instructions(f, bb): for feature, va in extractor.extract_insn_features(f, bb, insn): features[feature].add(va) for feature, va in extractor.extract_basic_block_features(f, bb): features[feature].add(va) for feature, va in extractor.extract_function_features(f): features[feature].add(va) return features # f may not be hashable (e.g. ida func_t) so cannot @lru_cache this def extract_basic_block_features(extractor, f, bb): features = collections.defaultdict(set) for insn in extractor.get_instructions(f, bb): for feature, va in extractor.extract_insn_features(f, bb, insn): features[feature].add(va) for feature, va in extractor.extract_basic_block_features(f, bb): features[feature].add(va) return features # note: too reduce the testing time it's recommended to reuse already existing test samples, if possible def get_data_path_by_name(name): if name == "mimikatz": return os.path.join(CD, "data", "mimikatz.exe_") elif name == "kernel32": return os.path.join(CD, "data", "kernel32.dll_") elif name == "kernel32-64": return os.path.join(CD, "data", "kernel32-64.dll_") elif name == "pma12-04": return os.path.join(CD, "data", "Practical Malware Analysis Lab 12-04.exe_") elif name == "pma16-01": return os.path.join(CD, "data", "Practical Malware Analysis Lab 16-01.exe_") elif name == "pma21-01": return os.path.join(CD, "data", "Practical Malware Analysis Lab 21-01.exe_") elif name == "al-khaser x86": return os.path.join(CD, "data", "al-khaser_x86.exe_") elif name == "al-khaser x64": return os.path.join(CD, "data", "al-khaser_x64.exe_") elif name.startswith("39c05"): return os.path.join(CD, "data", "39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.dll_") elif name.startswith("499c2"): return os.path.join(CD, "data", "499c2a85f6e8142c3f48d4251c9c7cd6.raw32") elif name.startswith("9324d"): return os.path.join(CD, "data", "9324d1a8ae37a36ae560c37448c9705a.exe_") elif name.startswith("a1982"): return os.path.join(CD, "data", "a198216798ca38f280dc413f8c57f2c2.exe_") elif name.startswith("a933a"): return os.path.join(CD, "data", "a933a1a402775cfa94b6bee0963f4b46.dll_") elif name.startswith("bfb9b"): return os.path.join(CD, "data", "bfb9b5391a13d0afd787e87ab90f14f5.dll_") elif name.startswith("c9188"): return os.path.join(CD, "data", "c91887d861d9bd4a5872249b641bc9f9.exe_") elif name.startswith("64d9f"): return os.path.join(CD, "data", "64d9f7d96b99467f36e22fada623c3bb.dll_") elif name.startswith("82bf6"): return os.path.join(CD, "data", "82BF6347ACF15E5D883715DC289D8A2B.exe_") elif name.startswith("pingtaest"): return os.path.join(CD, "data", "ping_täst.exe_") elif name.startswith("77329"): return os.path.join(CD, "data", "773290480d5445f11d3dc1b800728966.exe_") elif name.startswith("3b13b"): return os.path.join(CD, "data", "3b13b6f1d7cd14dc4a097a12e2e505c0a4cff495262261e2bfc991df238b9b04.dll_") else: raise ValueError("unexpected sample fixture: %s" % name) def get_sample_md5_by_name(name): """used by IDA tests to ensure the correct IDB is loaded""" if name == "mimikatz": return "5f66b82558ca92e54e77f216ef4c066c" elif name == "kernel32": return "e80758cf485db142fca1ee03a34ead05" elif name == "kernel32-64": return "a8565440629ac87f6fef7d588fe3ff0f" elif name == "pma12-04": return "56bed8249e7c2982a90e54e1e55391a2" elif name == "pma16-01": return "7faafc7e4a5c736ebfee6abbbc812d80" elif name == "pma21-01": return "c8403fb05244e23a7931c766409b5e22" elif name == "al-khaser x86": return "db648cd247281954344f1d810c6fd590" elif name == "al-khaser x64": return "3cb21ae76ff3da4b7e02d77ff76e82be" elif name.startswith("39c05"): return "b7841b9d5dc1f511a93cc7576672ec0c" elif name.startswith("499c2"): return "499c2a85f6e8142c3f48d4251c9c7cd6" elif name.startswith("9324d"): return "9324d1a8ae37a36ae560c37448c9705a" elif name.startswith("a1982"): return "a198216798ca38f280dc413f8c57f2c2" elif name.startswith("a933a"): return "a933a1a402775cfa94b6bee0963f4b46" elif name.startswith("bfb9b"): return "bfb9b5391a13d0afd787e87ab90f14f5" elif name.startswith("c9188"): return "c91887d861d9bd4a5872249b641bc9f9" elif name.startswith("64d9f"): return "64d9f7d96b99467f36e22fada623c3bb" elif name.startswith("82bf6"): return "82bf6347acf15e5d883715dc289d8a2b" elif name.startswith("77329"): return "773290480d5445f11d3dc1b800728966" elif name.startswith("3b13b"): # file name is SHA256 hash return "56a6ffe6a02941028cc8235204eef31d" else: raise ValueError("unexpected sample fixture: %s" % name) def resolve_sample(sample): return get_data_path_by_name(sample) @pytest.fixture def sample(request): return resolve_sample(request.param) def get_function(extractor, fva): for f in extractor.get_functions(): if int(f) == fva: return f raise ValueError("function not found") def get_basic_block(extractor, f, va): for bb in extractor.get_basic_blocks(f): if int(bb) == va: return bb raise ValueError("basic block not found") def resolve_scope(scope): if scope == "file": def inner_file(extractor): return extract_file_features(extractor) inner_file.__name__ = scope return inner_file elif "bb=" in scope: # like `function=0x401000,bb=0x40100A` fspec, _, bbspec = scope.partition(",") fva = int(fspec.partition("=")[2], 0x10) bbva = int(bbspec.partition("=")[2], 0x10) def inner_bb(extractor): f = get_function(extractor, fva) bb = get_basic_block(extractor, f, bbva) return extract_basic_block_features(extractor, f, bb) inner_bb.__name__ = scope return inner_bb elif scope.startswith("function"): # like `function=0x401000` va = int(scope.partition("=")[2], 0x10) def inner_function(extractor): f = get_function(extractor, va) return extract_function_features(extractor, f) inner_function.__name__ = scope return inner_function else: raise ValueError("unexpected scope fixture") @pytest.fixture def scope(request): return resolve_scope(request.param) def make_test_id(values): return "-".join(map(str, values)) def parametrize(params, values, **kwargs): """ extend `pytest.mark.parametrize` to pretty-print features. by default, it renders objects as an opaque value. ref: https://docs.pytest.org/en/2.9.0/example/parametrize.html#different-options-for-test-ids rendered ID might look something like: mimikatz-function=0x403BAC-api(CryptDestroyKey)-True """ ids = list(map(make_test_id, values)) return pytest.mark.parametrize(params, values, ids=ids, **kwargs) FEATURE_PRESENCE_TESTS = sorted( [ # file/characteristic("embedded pe") ("pma12-04", "file", capa.features.common.Characteristic("embedded pe"), True), # file/string ("mimikatz", "file", capa.features.common.String("SCardControl"), True), ("mimikatz", "file", capa.features.common.String("SCardTransmit"), True), ("mimikatz", "file", capa.features.common.String("ACR > "), True), ("mimikatz", "file", capa.features.common.String("nope"), False), # file/sections ("mimikatz", "file", capa.features.file.Section(".text"), True), ("mimikatz", "file", capa.features.file.Section(".nope"), False), # IDA doesn't extract unmapped sections by default # ("mimikatz", "file", capa.features.file.Section(".rsrc"), True), # file/exports ("kernel32", "file", capa.features.file.Export("BaseThreadInitThunk"), True), ("kernel32", "file", capa.features.file.Export("lstrlenW"), True), ("kernel32", "file", capa.features.file.Export("nope"), False), # file/imports ("mimikatz", "file", capa.features.file.Import("advapi32.CryptSetHashParam"), True), ("mimikatz", "file", capa.features.file.Import("CryptSetHashParam"), True), ("mimikatz", "file", capa.features.file.Import("kernel32.IsWow64Process"), True), ("mimikatz", "file", capa.features.file.Import("msvcrt.exit"), True), ("mimikatz", "file", capa.features.file.Import("cabinet.#11"), True), ("mimikatz", "file", capa.features.file.Import("#11"), False), ("mimikatz", "file", capa.features.file.Import("#nope"), False), ("mimikatz", "file", capa.features.file.Import("nope"), False), ("mimikatz", "file", capa.features.file.Import("advapi32.CryptAcquireContextW"), True), ("mimikatz", "file", capa.features.file.Import("advapi32.CryptAcquireContext"), True), ("mimikatz", "file", capa.features.file.Import("CryptAcquireContextW"), True), ("mimikatz", "file", capa.features.file.Import("CryptAcquireContext"), True), # function/characteristic(loop) ("mimikatz", "function=0x401517", capa.features.common.Characteristic("loop"), True), ("mimikatz", "function=0x401000", capa.features.common.Characteristic("loop"), False), # bb/characteristic(tight loop) ("mimikatz", "function=0x402EC4", capa.features.common.Characteristic("tight loop"), True), ("mimikatz", "function=0x401000", capa.features.common.Characteristic("tight loop"), False), # bb/characteristic(stack string) ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("stack string"), True), ("mimikatz", "function=0x401000", capa.features.common.Characteristic("stack string"), False), # bb/characteristic(tight loop) ("mimikatz", "function=0x402EC4,bb=0x402F8E", capa.features.common.Characteristic("tight loop"), True), ("mimikatz", "function=0x401000,bb=0x401000", capa.features.common.Characteristic("tight loop"), False), # insn/mnemonic ("mimikatz", "function=0x40105D", capa.features.insn.Mnemonic("push"), True), ("mimikatz", "function=0x40105D", capa.features.insn.Mnemonic("movzx"), True), ("mimikatz", "function=0x40105D", capa.features.insn.Mnemonic("xor"), True), ("mimikatz", "function=0x40105D", capa.features.insn.Mnemonic("in"), False), ("mimikatz", "function=0x40105D", capa.features.insn.Mnemonic("out"), False), # insn/number ("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF), True), ("mimikatz", "function=0x40105D", capa.features.insn.Number(0x3136B0), True), # insn/number: stack adjustments ("mimikatz", "function=0x40105D", capa.features.insn.Number(0xC), False), ("mimikatz", "function=0x40105D", capa.features.insn.Number(0x10), False), # insn/number: arch flavors ("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF), True), ("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF, arch=ARCH_X32), True), ("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF, arch=ARCH_X64), False), # insn/offset ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x0), True), ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x4), True), ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0xC), True), # insn/offset, issue #276 ("64d9f", "function=0x10001510,bb=0x100015B0", capa.features.insn.Offset(0x4000), True), # insn/offset: stack references ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x8), False), ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x10), False), # insn/offset: negative ("mimikatz", "function=0x4011FB", capa.features.insn.Offset(-0x1), True), ("mimikatz", "function=0x4011FB", capa.features.insn.Offset(-0x2), True), # insn/offset: arch flavors ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x0), True), ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x0, arch=ARCH_X32), True), ("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x0, arch=ARCH_X64), False), # insn/api ("mimikatz", "function=0x403BAC", capa.features.insn.API("advapi32.CryptAcquireContextW"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("advapi32.CryptAcquireContext"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("advapi32.CryptGenKey"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("advapi32.CryptImportKey"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("advapi32.CryptDestroyKey"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("CryptAcquireContextW"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("CryptAcquireContext"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("CryptGenKey"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("CryptImportKey"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("CryptDestroyKey"), True), ("mimikatz", "function=0x403BAC", capa.features.insn.API("Nope"), False), ("mimikatz", "function=0x403BAC", capa.features.insn.API("advapi32.Nope"), False), # insn/api: thunk ("mimikatz", "function=0x4556E5", capa.features.insn.API("advapi32.LsaQueryInformationPolicy"), True), ("mimikatz", "function=0x4556E5", capa.features.insn.API("LsaQueryInformationPolicy"), True), # insn/api: x64 ( "kernel32-64", "function=0x180001010", capa.features.insn.API("RtlVirtualUnwind"), True, ), ("kernel32-64", "function=0x180001010", capa.features.insn.API("RtlVirtualUnwind"), True), # insn/api: x64 thunk ( "kernel32-64", "function=0x1800202B0", capa.features.insn.API("RtlCaptureContext"), True, ), ("kernel32-64", "function=0x1800202B0", capa.features.insn.API("RtlCaptureContext"), True), # insn/api: x64 nested thunk ("al-khaser x64", "function=0x14004B4F0", capa.features.insn.API("__vcrt_GetModuleHandle"), True), # insn/api: call via jmp ("mimikatz", "function=0x40B3C6", capa.features.insn.API("LocalFree"), True), ("c91887...", "function=0x40156F", capa.features.insn.API("CloseClipboard"), True), # TODO ignore thunk functions that call via jmp? # insn/api: resolve indirect calls ("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.CreatePipe"), True), ("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.SetHandleInformation"), True), ("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.CloseHandle"), True), ("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.WriteFile"), True), # insn/string ("mimikatz", "function=0x40105D", capa.features.common.String("SCardControl"), True), ("mimikatz", "function=0x40105D", capa.features.common.String("SCardTransmit"), True), ("mimikatz", "function=0x40105D", capa.features.common.String("ACR > "), True), ("mimikatz", "function=0x40105D", capa.features.common.String("nope"), False), ("773290...", "function=0x140001140", capa.features.common.String(r"%s:\\OfficePackagesForWDAG"), True), # insn/regex, issue #262 ("pma16-01", "function=0x4021B0", capa.features.common.Regex("HTTP/1.0"), True), ("pma16-01", "function=0x4021B0", capa.features.common.Regex("www.practicalmalwareanalysis.com"), False), # insn/string, pointer to string ("mimikatz", "function=0x44EDEF", capa.features.common.String("INPUTEVENT"), True), # insn/string, direct memory reference ("mimikatz", "function=0x46D6CE", capa.features.common.String("(null)"), True), # insn/bytes ("mimikatz", "function=0x40105D", capa.features.common.Bytes("SCardControl".encode("utf-16le")), True), ("mimikatz", "function=0x40105D", capa.features.common.Bytes("SCardTransmit".encode("utf-16le")), True), ("mimikatz", "function=0x40105D", capa.features.common.Bytes("ACR > ".encode("utf-16le")), True), ("mimikatz", "function=0x40105D", capa.features.common.Bytes("nope".encode("ascii")), False), # IDA features included byte sequences read from invalid memory, fixed in #409 ("mimikatz", "function=0x44570F", capa.features.common.Bytes(binascii.unhexlify("FF" * 256)), False), # insn/bytes, pointer to bytes ("mimikatz", "function=0x44EDEF", capa.features.common.Bytes("INPUTEVENT".encode("utf-16le")), True), # insn/characteristic(nzxor) ("mimikatz", "function=0x410DFC", capa.features.common.Characteristic("nzxor"), True), ("mimikatz", "function=0x40105D", capa.features.common.Characteristic("nzxor"), False), # insn/characteristic(nzxor): no security cookies ("mimikatz", "function=0x46D534", capa.features.common.Characteristic("nzxor"), False), # insn/characteristic(nzxor): xorps # viv needs fixup to recognize function, see above ("3b13b...", "function=0x10006860", capa.features.common.Characteristic("nzxor"), True), # insn/characteristic(peb access) ("kernel32-64", "function=0x1800017D0", capa.features.common.Characteristic("peb access"), True), ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("peb access"), False), # insn/characteristic(gs access) ("kernel32-64", "function=0x180001068", capa.features.common.Characteristic("gs access"), True), ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("gs access"), False), # insn/characteristic(cross section flow) ("a1982...", "function=0x4014D0", capa.features.common.Characteristic("cross section flow"), True), # insn/characteristic(cross section flow): imports don't count ("kernel32-64", "function=0x180001068", capa.features.common.Characteristic("cross section flow"), False), ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("cross section flow"), False), # insn/characteristic(recursive call) ("mimikatz", "function=0x40640e", capa.features.common.Characteristic("recursive call"), True), # before this we used ambiguous (0x4556E5, False), which has a data reference / indirect recursive call, see #386 ("mimikatz", "function=0x4175FF", capa.features.common.Characteristic("recursive call"), False), # insn/characteristic(indirect call) ("mimikatz", "function=0x4175FF", capa.features.common.Characteristic("indirect call"), True), ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("indirect call"), False), # insn/characteristic(calls from) ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("calls from"), True), ("mimikatz", "function=0x4702FD", capa.features.common.Characteristic("calls from"), False), # function/characteristic(calls to) ("mimikatz", "function=0x40105D", capa.features.common.Characteristic("calls to"), True), # before this we used ambiguous (0x4556E5, False), which has a data reference / indirect recursive call, see #386 ("mimikatz", "function=0x456BB9", capa.features.common.Characteristic("calls to"), False), # file/function-name ("pma16-01", "file", capa.features.file.FunctionName("__aulldiv"), True), ], # order tests by (file, item) # so that our LRU cache is most effective. key=lambda t: (t[0], t[1]), ) FEATURE_PRESENCE_TESTS_IDA = [ # file/imports # IDA can recover more names of APIs imported by ordinal ("mimikatz", "file", capa.features.file.Import("cabinet.FCIAddFile"), True), ] FEATURE_COUNT_TESTS = [ ("mimikatz", "function=0x40E5C2", capa.features.basicblock.BasicBlock(), 7), ("mimikatz", "function=0x4702FD", capa.features.common.Characteristic("calls from"), 0), ("mimikatz", "function=0x40E5C2", capa.features.common.Characteristic("calls from"), 3), ("mimikatz", "function=0x4556E5", capa.features.common.Characteristic("calls to"), 0), ("mimikatz", "function=0x40B1F1", capa.features.common.Characteristic("calls to"), 3), ] def do_test_feature_presence(get_extractor, sample, scope, feature, expected): extractor = get_extractor(sample) features = scope(extractor) if expected: msg = "%s should be found in %s" % (str(feature), scope.__name__) else: msg = "%s should not be found in %s" % (str(feature), scope.__name__) assert feature.evaluate(features) == expected, msg def do_test_feature_count(get_extractor, sample, scope, feature, expected): extractor = get_extractor(sample) features = scope(extractor) msg = "%s should be found %d times in %s, found: %d" % ( str(feature), expected, scope.__name__, len(features[feature]), ) assert len(features[feature]) == expected, msg def get_extractor(path): extractor = get_viv_extractor(path) # overload the extractor so that the fixture exposes `extractor.path` setattr(extractor, "path", path) return extractor @pytest.fixture def mimikatz_extractor(): return get_extractor(get_data_path_by_name("mimikatz")) @pytest.fixture def a933a_extractor(): return get_extractor(get_data_path_by_name("a933a...")) @pytest.fixture def kernel32_extractor(): return get_extractor(get_data_path_by_name("kernel32")) @pytest.fixture def a1982_extractor(): return get_extractor(get_data_path_by_name("a1982...")) @pytest.fixture def z9324d_extractor(): return get_extractor(get_data_path_by_name("9324d...")) @pytest.fixture def pma12_04_extractor(): return get_extractor(get_data_path_by_name("pma12-04")) @pytest.fixture def pma16_01_extractor(): return get_extractor(get_data_path_by_name("pma16-01")) @pytest.fixture def bfb9b_extractor(): return get_extractor(get_data_path_by_name("bfb9b...")) @pytest.fixture def pma21_01_extractor(): return get_extractor(get_data_path_by_name("pma21-01")) @pytest.fixture def c9188_extractor(): return get_extractor(get_data_path_by_name("c9188...")) @pytest.fixture def z39c05_extractor(): return get_extractor(get_data_path_by_name("39c05...")) @pytest.fixture def z499c2_extractor(): return get_extractor(get_data_path_by_name("499c2...")) @pytest.fixture def al_khaser_x86_extractor(): return get_extractor(get_data_path_by_name("al-khaser x86")) @pytest.fixture def pingtaest_extractor(): return get_extractor(get_data_path_by_name("pingtaest"))