From 5a3775455bf0ac62762bb0c24208a01e351e532f Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 10:30:43 +0200 Subject: [PATCH 01/34] main: allow to specify --backend=pefile --- capa/main.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/capa/main.py b/capa/main.py index cb7f0f4c..a7be6cc9 100644 --- a/capa/main.py +++ b/capa/main.py @@ -85,6 +85,7 @@ SIGNATURES_PATH_DEFAULT_STRING = "(embedded signatures)" BACKEND_VIV = "vivisect" BACKEND_DOTNET = "dotnet" BACKEND_BINJA = "binja" +BACKEND_PEFILE = "pefile" E_MISSING_RULES = 10 E_MISSING_FILE = 11 @@ -567,8 +568,12 @@ def get_extractor( return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) - # default to use vivisect backend - else: + elif backend == BACKEND_PEFILE: + import capa.features.extractors.pefile + + return capa.features.extractors.pefile.PefileFeatureExtractor(path) + + elif backend == BACKEND_VIV: import capa.features.extractors.viv.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): @@ -586,6 +591,9 @@ def get_extractor( return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) + else: + raise ValueError("unexpected backend: " + backend) + def get_file_extractors(sample: Path, format_: str) -> List[FeatureExtractor]: file_extractors: List[FeatureExtractor] = [] @@ -911,7 +919,7 @@ def install_common_args(parser, wanted=None): "--backend", type=str, help="select the backend to use", - choices=(BACKEND_VIV, BACKEND_BINJA), + choices=(BACKEND_VIV, BACKEND_BINJA, BACKEND_PEFILE), default=BACKEND_VIV, ) From 7898ac24d5ecc79b9d635a7349912850078a9ebf Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 10:31:28 +0200 Subject: [PATCH 02/34] show-features: support showing pefile features --- scripts/show-features.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/show-features.py b/scripts/show-features.py index 25f1662b..9437caa5 100644 --- a/scripts/show-features.py +++ b/scripts/show-features.py @@ -68,6 +68,7 @@ import os import sys import logging import argparse +from typing import Tuple from pathlib import Path import capa.main @@ -80,8 +81,10 @@ import capa.render.verbose as v import capa.features.common import capa.features.freeze import capa.features.address +import capa.features.extractors.pefile import capa.features.extractors.base_extractor from capa.helpers import log_unsupported_runtime_error +from capa.features.extractors.base_extractor import FunctionHandle logger = logging.getLogger("capa.show-features") @@ -101,6 +104,10 @@ def main(argv=None): args = parser.parse_args(args=argv) capa.main.handle_common_args(args) + if args.function and args.backend == "pefile": + print("pefile backend does not support extracting function features") + return -1 + try: taste = capa.helpers.get_file_taste(Path(args.sample)) except IOError as e: @@ -137,7 +144,12 @@ def main(argv=None): for feature, addr in extractor.extract_file_features(): print(f"file: {format_address(addr)}: {feature}") - function_handles = tuple(extractor.get_functions()) + function_handles: Tuple[FunctionHandle, ...] + if isinstance(extractor, capa.features.extractors.pefile.PefileFeatureExtractor): + # pefile extractor doesn't extract function features + function_handles = () + else: + function_handles = tuple(extractor.get_functions()) if args.function: if args.format == "freeze": From 21b2aac8b5f87e74529ca0913a1d4b548c74657c Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 10:31:52 +0200 Subject: [PATCH 03/34] fixtures: add test cases for forwarded exports --- tests/fixtures.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/fixtures.py b/tests/fixtures.py index 2eaf86ae..291ba1a8 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -306,6 +306,8 @@ def get_data_path_by_name(name) -> Path: return CD / "data" / "294b8db1f2702b60fb2e42fdc50c2cee6a5046112da9a5703a548a4fa50477bc.elf_" elif name.startswith("2bf18d"): return CD / "data" / "2bf18d0403677378adad9001b1243211.elf_" + elif name.startswith("ea2876"): + return CD / "data" / "ea2876e9175410b6f6719f80ee44b9553960758c7d0f7bed73c0fe9a78d8e669.dll_" else: raise ValueError(f"unexpected sample fixture: {name}") @@ -366,6 +368,8 @@ def get_sample_md5_by_name(name): return "3db3e55b16a7b1b1afb970d5e77c5d98" elif name.startswith("2bf18d"): return "2bf18d0403677378adad9001b1243211" + elif name.startswith("ea2876"): + return "76fa734236daa023444dec26863401dc" else: raise ValueError(f"unexpected sample fixture: {name}") @@ -529,6 +533,8 @@ FEATURE_PRESENCE_TESTS = sorted( ("kernel32", "file", capa.features.file.Export("BaseThreadInitThunk"), True), ("kernel32", "file", capa.features.file.Export("lstrlenW"), True), ("kernel32", "file", capa.features.file.Export("nope"), False), + # forwarded export + ("ea2876", "file", capa.features.file.Export("vresion.GetFileVersionInfoA"), True), # file/imports ("mimikatz", "file", capa.features.file.Import("advapi32.CryptSetHashParam"), True), ("mimikatz", "file", capa.features.file.Import("CryptSetHashParam"), True), @@ -715,6 +721,8 @@ FEATURE_PRESENCE_TESTS = sorted( ("mimikatz", "function=0x4702FD", capa.features.common.Characteristic("calls from"), False), # function/characteristic(calls to) ("mimikatz", "function=0x40105D", capa.features.common.Characteristic("calls to"), True), + # function/characteristic(forwarded export) + ("ea2876", "file", capa.features.common.Characteristic("forwarded export"), 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 From 6b6d7eb4946dc3d9978caab839ca331ee5ead7ad Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 10:32:27 +0200 Subject: [PATCH 04/34] pefile: extract forwarded exports --- capa/features/extractors/pefile.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/capa/features/extractors/pefile.py b/capa/features/extractors/pefile.py index a820e7b8..9a3d6b4f 100644 --- a/capa/features/extractors/pefile.py +++ b/capa/features/extractors/pefile.py @@ -40,8 +40,23 @@ def extract_file_export_names(pe, **kwargs): name = export.name.partition(b"\x00")[0].decode("ascii") except UnicodeDecodeError: continue - va = base_address + export.address - yield Export(name), AbsoluteVirtualAddress(va) + + if export.forwarder is None: + va = base_address + export.address + yield Export(name), AbsoluteVirtualAddress(va) + + else: + try: + forwarded_name = export.forwarder.partition(b"\x00")[0].decode("ascii") + except UnicodeDecodeError: + continue + + forwarded_dll, _, forwarded_symbol = forwarded_name.partition(".") + forwarded_dll = forwarded_dll.lower() + + va = base_address + export.address + yield Export(f"{forwarded_dll}.{forwarded_symbol}"), AbsoluteVirtualAddress(va) + yield Characteristic("forwarded export"), AbsoluteVirtualAddress(va) def extract_file_import_names(pe, **kwargs): From 9e9f120c80291ae7990f97aacd1ba5f5a32a6050 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 10:51:28 +0200 Subject: [PATCH 05/34] pefile: better handle forwarded exports with specific paths --- capa/features/extractors/pefile.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/capa/features/extractors/pefile.py b/capa/features/extractors/pefile.py index 9a3d6b4f..51cdf539 100644 --- a/capa/features/extractors/pefile.py +++ b/capa/features/extractors/pefile.py @@ -51,7 +51,12 @@ def extract_file_export_names(pe, **kwargs): except UnicodeDecodeError: continue - forwarded_dll, _, forwarded_symbol = forwarded_name.partition(".") + # use rpartition so we can split on separator between dll and name. + # the dll name can be a full path, like in the case of + # ef64d6d7c34250af8e21a10feb931c9b + # which i assume means the path can have embedded periods. + # so we don't want the first period, we want the last. + forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") forwarded_dll = forwarded_dll.lower() va = base_address + export.address From 6b81c77d2297b883bd603bd9354e1bf17720664d Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 11:45:53 +0200 Subject: [PATCH 06/34] profile-time: workaround for flake8-encodings bug https://github.com/python-formate/flake8-encodings/issues/35 --- scripts/profile-time.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/scripts/profile-time.py b/scripts/profile-time.py index b6c48683..9acd60ff 100644 --- a/scripts/profile-time.py +++ b/scripts/profile-time.py @@ -58,22 +58,16 @@ import capa.features.freeze logger = logging.getLogger("capa.profile") +def subshell(cmd): + return subprocess.run(cmd, shell=True, capture_output=True, text=True).stdout.strip() + + def main(argv=None): if argv is None: argv = sys.argv[1:] - label = subprocess.run( - "git show --pretty=oneline --abbrev-commit | head -n 1", shell=True, capture_output=True, text=True - ).stdout.strip() - is_dirty = ( - subprocess.run( - "git status | grep 'modified: ' | grep -v 'rules' | grep -v 'tests/data'", - shell=True, - capture_output=True, - text=True, - ).stdout - != "" - ) + label = subshell("git show --pretty=oneline --abbrev-commit | head -n 1").strip() + is_dirty = subshell("git status | grep 'modified: ' | grep -v 'rules' | grep -v 'tests/data'") != "" if is_dirty: label += " (dirty)" From 330f2a6b9b79d83e5e6b9b37928e51aa8583c15c Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 11:47:32 +0200 Subject: [PATCH 07/34] viv: emit forwarded export features ref #1592 --- capa/features/extractors/viv/file.py | 35 +++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/capa/features/extractors/viv/file.py b/capa/features/extractors/viv/file.py index cc078016..12258356 100644 --- a/capa/features/extractors/viv/file.py +++ b/capa/features/extractors/viv/file.py @@ -8,6 +8,7 @@ from typing import Tuple, Iterator import PE.carve as pe_carve # vivisect PE +import vivisect import viv_utils import viv_utils.flirt @@ -25,10 +26,42 @@ def extract_file_embedded_pe(buf, **kwargs) -> Iterator[Tuple[Feature, Address]] yield Characteristic("embedded pe"), FileOffsetAddress(offset) -def extract_file_export_names(vw, **kwargs) -> Iterator[Tuple[Feature, Address]]: +def get_first_vw_filename(vw: vivisect.VivWorkspace): + # vivisect associates metadata with each file that its loaded into the workspace. + # capa only loads a single file into each workspace. + # so to access the metadata for the file in question, we can just take the first one. + # otherwise, we'd have to pass around the module name of the file we're analyzing, + # which is a pain. + # + # so this is a simplifying assumption. + return next(iter(vw.filemeta.keys())) + + +def extract_file_export_names(vw: vivisect.VivWorkspace, **kwargs) -> Iterator[Tuple[Feature, Address]]: for va, _, name, _ in vw.getExports(): yield Export(name), AbsoluteVirtualAddress(va) + if vw.getMeta("Format") == "pe": + pe = vw.parsedbin + baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase + for rva, _, forwarded_name in vw.getFileMeta(get_first_vw_filename(vw), "forwarders"): + try: + forwarded_name = forwarded_name.partition(b"\x00")[0].decode("ascii") + except UnicodeDecodeError: + continue + + # use rpartition so we can split on separator between dll and name. + # the dll name can be a full path, like in the case of + # ef64d6d7c34250af8e21a10feb931c9b + # which i assume means the path can have embedded periods. + # so we don't want the first period, we want the last. + forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") + forwarded_dll = forwarded_dll.lower() + + va = baseaddr + rva + yield Export(f"{forwarded_dll}.{forwarded_symbol}"), AbsoluteVirtualAddress(va) + yield Characteristic("forwarded export"), AbsoluteVirtualAddress(va) + def extract_file_import_names(vw, **kwargs) -> Iterator[Tuple[Feature, Address]]: """ From 74924990a2865847a0e017306b3847a9f46282bd Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 11:50:56 +0200 Subject: [PATCH 08/34] changelog --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ccfcbeb..5d35632b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,7 @@ - Utility script to detect feature overlap between new and existing CAPA rules [#1451](https://github.com/mandiant/capa/issues/1451) [@Aayush-Goel-04](https://github.com/aayush-goel-04) - use fancy box drawing characters for default output #1586 @williballenthin - use [pre-commit](https://pre-commit.com/) to invoke linters #1579 @williballenthin -- publish via PyPI trusted publishing #1491 @williballenthin -- migrate to pyproject.toml #1301 @williballenthin - +- extract forwarded exports from PE files #1624 @williballenthin ### Breaking Changes - Update Metadata type in capa main [#1411](https://github.com/mandiant/capa/issues/1411) [@Aayush-Goel-04](https://github.com/aayush-goel-04) @manasghandat - Python 3.8 is now the minimum supported Python version #1578 @williballenthin @@ -67,6 +65,9 @@ - update ATT&CK/MBC data for linting #1568 @mr-tz - log time taken to analyze each function #1290 @williballenthin - tests: make fixture available via conftest.py #1592 @williballenthin +- publish via PyPI trusted publishing #1491 @williballenthin +- migrate to pyproject.toml #1301 @williballenthin + ### Raw diffs - [capa v5.1.0...master](https://github.com/mandiant/capa/compare/v5.1.0...master) From bb6557ea0a308f819842b9a2d8c8b1eebb3fbe38 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 12:18:57 +0200 Subject: [PATCH 09/34] ida: extract forwarded export features --- capa/features/extractors/ida/file.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/capa/features/extractors/ida/file.py b/capa/features/extractors/ida/file.py index 051ecafd..c18dc557 100644 --- a/capa/features/extractors/ida/file.py +++ b/capa/features/extractors/ida/file.py @@ -12,6 +12,7 @@ from typing import Tuple, Iterator import idc import idaapi import idautils +import ida_entry import capa.features.extractors.common import capa.features.extractors.helpers @@ -83,8 +84,21 @@ def extract_file_embedded_pe() -> Iterator[Tuple[Feature, Address]]: def extract_file_export_names() -> Iterator[Tuple[Feature, Address]]: """extract function exports""" - for _, _, ea, name in idautils.Entries(): - yield Export(name), AbsoluteVirtualAddress(ea) + for _, ordinal, ea, name in idautils.Entries(): + forwarded_name = ida_entry.get_entry_forwarder(ordinal) + if forwarded_name is None: + yield Export(name), AbsoluteVirtualAddress(ea) + else: + # use rpartition so we can split on separator between dll and name. + # the dll name can be a full path, like in the case of + # ef64d6d7c34250af8e21a10feb931c9b + # which i assume means the path can have embedded periods. + # so we don't want the first period, we want the last. + forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") + forwarded_dll = forwarded_dll.lower() + + yield Export(f"{forwarded_dll}.{forwarded_symbol}"), AbsoluteVirtualAddress(ea) + yield Characteristic("forwarded export"), AbsoluteVirtualAddress(ea) def extract_file_import_names() -> Iterator[Tuple[Feature, Address]]: From 7c67fae52a39a77c7734d1a3c8b0938dcbcc6466 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Thu, 13 Jul 2023 16:53:35 +0200 Subject: [PATCH 10/34] changelog: formatting --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d35632b..7b0a2ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,14 @@ # Change Log ## master (unreleased) -- extract function and API names from ELF symtab entries @yelhamer https://github.com/mandiant/capa-rules/issues/736 ### New Features - Utility script to detect feature overlap between new and existing CAPA rules [#1451](https://github.com/mandiant/capa/issues/1451) [@Aayush-Goel-04](https://github.com/aayush-goel-04) - use fancy box drawing characters for default output #1586 @williballenthin - use [pre-commit](https://pre-commit.com/) to invoke linters #1579 @williballenthin - extract forwarded exports from PE files #1624 @williballenthin +- extract function and API names from ELF symtab entries @yelhamer https://github.com/mandiant/capa-rules/issues/736 + ### Breaking Changes - Update Metadata type in capa main [#1411](https://github.com/mandiant/capa/issues/1411) [@Aayush-Goel-04](https://github.com/aayush-goel-04) @manasghandat - Python 3.8 is now the minimum supported Python version #1578 @williballenthin From d2567692a8cca58088c01069ce92cad3e90da562 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Mon, 17 Jul 2023 17:32:40 +0200 Subject: [PATCH 11/34] factor out common forwarded export name normalization --- capa/features/extractors/helpers.py | 17 +++++++++++++++++ capa/features/extractors/ida/file.py | 11 ++--------- capa/features/extractors/pefile.py | 12 ++---------- capa/features/extractors/viv/file.py | 13 +++---------- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/capa/features/extractors/helpers.py b/capa/features/extractors/helpers.py index 66399b12..e17a66f2 100644 --- a/capa/features/extractors/helpers.py +++ b/capa/features/extractors/helpers.py @@ -70,6 +70,23 @@ def generate_symbols(dll: str, symbol: str) -> Iterator[str]: yield symbol[:-1] +def reformat_forwarded_export_name(forwarded_name: str) -> str: + """ + a forwarded export has a DLL name/path an symbol name. + we want the former to be lowercase, and the latter to be verbatim. + """ + + # use rpartition so we can split on separator between dll and name. + # the dll name can be a full path, like in the case of + # ef64d6d7c34250af8e21a10feb931c9b + # which i assume means the path can have embedded periods. + # so we don't want the first period, we want the last. + forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") + forwarded_dll = forwarded_dll.lower() + + return f"{forwarded_dll}.{forwarded_symbol}" + + def all_zeros(bytez: bytes) -> bool: return all(b == 0 for b in builtins.bytes(bytez)) diff --git a/capa/features/extractors/ida/file.py b/capa/features/extractors/ida/file.py index c18dc557..efa4b66c 100644 --- a/capa/features/extractors/ida/file.py +++ b/capa/features/extractors/ida/file.py @@ -89,15 +89,8 @@ def extract_file_export_names() -> Iterator[Tuple[Feature, Address]]: if forwarded_name is None: yield Export(name), AbsoluteVirtualAddress(ea) else: - # use rpartition so we can split on separator between dll and name. - # the dll name can be a full path, like in the case of - # ef64d6d7c34250af8e21a10feb931c9b - # which i assume means the path can have embedded periods. - # so we don't want the first period, we want the last. - forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") - forwarded_dll = forwarded_dll.lower() - - yield Export(f"{forwarded_dll}.{forwarded_symbol}"), AbsoluteVirtualAddress(ea) + forwarded_name = capa.features.extractors.helpers.reformat_forwarded_export_name(forwarded_name) + yield Export(forwarded_name), AbsoluteVirtualAddress(ea) yield Characteristic("forwarded export"), AbsoluteVirtualAddress(ea) diff --git a/capa/features/extractors/pefile.py b/capa/features/extractors/pefile.py index 51cdf539..c51675e8 100644 --- a/capa/features/extractors/pefile.py +++ b/capa/features/extractors/pefile.py @@ -50,17 +50,9 @@ def extract_file_export_names(pe, **kwargs): forwarded_name = export.forwarder.partition(b"\x00")[0].decode("ascii") except UnicodeDecodeError: continue - - # use rpartition so we can split on separator between dll and name. - # the dll name can be a full path, like in the case of - # ef64d6d7c34250af8e21a10feb931c9b - # which i assume means the path can have embedded periods. - # so we don't want the first period, we want the last. - forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") - forwarded_dll = forwarded_dll.lower() - + forwarded_name = capa.features.extractors.helpers.reformat_forwarded_export_name(forwarded_name) va = base_address + export.address - yield Export(f"{forwarded_dll}.{forwarded_symbol}"), AbsoluteVirtualAddress(va) + yield Export(forwarded_name), AbsoluteVirtualAddress(va) yield Characteristic("forwarded export"), AbsoluteVirtualAddress(va) diff --git a/capa/features/extractors/viv/file.py b/capa/features/extractors/viv/file.py index 12258356..b9df6467 100644 --- a/capa/features/extractors/viv/file.py +++ b/capa/features/extractors/viv/file.py @@ -16,7 +16,7 @@ import capa.features.insn import capa.features.extractors.common import capa.features.extractors.helpers import capa.features.extractors.strings -from capa.features.file import Export, Import, Section, FunctionName +from capa.features.file import Export, FunctionName, Import, Section from capa.features.common import Feature, Characteristic from capa.features.address import Address, FileOffsetAddress, AbsoluteVirtualAddress @@ -50,16 +50,9 @@ def extract_file_export_names(vw: vivisect.VivWorkspace, **kwargs) -> Iterator[T except UnicodeDecodeError: continue - # use rpartition so we can split on separator between dll and name. - # the dll name can be a full path, like in the case of - # ef64d6d7c34250af8e21a10feb931c9b - # which i assume means the path can have embedded periods. - # so we don't want the first period, we want the last. - forwarded_dll, _, forwarded_symbol = forwarded_name.rpartition(".") - forwarded_dll = forwarded_dll.lower() - + forwarded_name = capa.features.extractors.helpers.reformat_forwarded_export_name(forwarded_name) va = baseaddr + rva - yield Export(f"{forwarded_dll}.{forwarded_symbol}"), AbsoluteVirtualAddress(va) + yield Export(forwarded_name), AbsoluteVirtualAddress(va) yield Characteristic("forwarded export"), AbsoluteVirtualAddress(va) From 221a5a9f032a38cffe501831985da7e91afac610 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Mon, 17 Jul 2023 17:56:33 +0200 Subject: [PATCH 12/34] tests: xfail binja forwarded exports --- .gitignore | 2 ++ capa/features/extractors/viv/file.py | 2 +- tests/test_binja_features.py | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 95bc3044..38d72570 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,5 @@ Pipfile Pipfile.lock /cache/ .github/binja/binaryninja +.github/binja/download_headless.py +.github/binja/BinaryNinja-headless.zip diff --git a/capa/features/extractors/viv/file.py b/capa/features/extractors/viv/file.py index b9df6467..204d8e69 100644 --- a/capa/features/extractors/viv/file.py +++ b/capa/features/extractors/viv/file.py @@ -16,7 +16,7 @@ import capa.features.insn import capa.features.extractors.common import capa.features.extractors.helpers import capa.features.extractors.strings -from capa.features.file import Export, FunctionName, Import, Section +from capa.features.file import Export, Import, Section, FunctionName from capa.features.common import Feature, Characteristic from capa.features.address import Address, FileOffsetAddress, AbsoluteVirtualAddress diff --git a/tests/test_binja_features.py b/tests/test_binja_features.py index b2256f80..3932dcc5 100644 --- a/tests/test_binja_features.py +++ b/tests/test_binja_features.py @@ -12,6 +12,8 @@ import pytest import fixtures import capa.main +import capa.features.file +import capa.features.common logger = logging.getLogger(__file__) @@ -40,6 +42,13 @@ except ImportError: def test_binja_features(sample, scope, feature, expected): if feature == capa.features.common.Characteristic("stack string"): pytest.xfail("skip failing Binja stack string detection temporarily, see #1473") + + if isinstance(feature, capa.features.file.Export) and "." in str(feature.value): + pytest.xfail("skip Binja unsupported forwarded export feature, see #1624") + + if feature == capa.features.common.Characteristic("forwarded export"): + pytest.xfail("skip Binja unsupported forwarded export feature, see #1624") + fixtures.do_test_feature_presence(fixtures.get_binja_extractor, sample, scope, feature, expected) From 40793eeefbd2882cc5f1ba69b34339e358ed5687 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Mon, 17 Jul 2023 18:07:25 +0200 Subject: [PATCH 13/34] tests: bn: update link to tracking issue --- tests/test_binja_features.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_binja_features.py b/tests/test_binja_features.py index 3932dcc5..4daaa790 100644 --- a/tests/test_binja_features.py +++ b/tests/test_binja_features.py @@ -44,10 +44,10 @@ def test_binja_features(sample, scope, feature, expected): pytest.xfail("skip failing Binja stack string detection temporarily, see #1473") if isinstance(feature, capa.features.file.Export) and "." in str(feature.value): - pytest.xfail("skip Binja unsupported forwarded export feature, see #1624") + pytest.xfail("skip Binja unsupported forwarded export feature, see #1646") if feature == capa.features.common.Characteristic("forwarded export"): - pytest.xfail("skip Binja unsupported forwarded export feature, see #1624") + pytest.xfail("skip Binja unsupported forwarded export feature, see #1646") fixtures.do_test_feature_presence(fixtures.get_binja_extractor, sample, scope, feature, expected) From 18e040857723b5a5109075b232f16435ea211e2f Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 11:18:28 +0200 Subject: [PATCH 14/34] contributing: document CLA --- .github/CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 59a3b6a4..969443e8 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -159,12 +159,25 @@ The process described here has several goals: Please follow these steps to have your contribution considered by the maintainers: +0. Sign the [Contributor License Agreement](#contributor-license-agreement) 1. Follow the [styleguides](#styleguides) 2. Update the CHANGELOG and add tests and documentation. In case they are not needed, indicate it in [the PR template](pull_request_template.md). 3. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing
What if the status checks are failing? If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.
While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. +### Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution, +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + ## Styleguides ### Git Commit Messages From faf3ca53f72476d608129be2293da454b4f5c658 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 09:21:51 +0000 Subject: [PATCH 15/34] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0a2ca5..c1363ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Update Metadata type in capa main [#1411](https://github.com/mandiant/capa/issues/1411) [@Aayush-Goel-04](https://github.com/aayush-goel-04) @manasghandat - Python 3.8 is now the minimum supported Python version #1578 @williballenthin - Updated file paths to use pathlib.Path for improved path handling and compatibility [#1534](https://github.com/mandiant/capa/issues/1534) [@Aayush-Goel-04](https://github.com/aayush-goel-04) +- Require a Contributor License Agreement (CLA) for PRs going forward #1642 @williballenthin ### New Rules (26) From d47703fada4cdd20fa6b7ebdfbd19f71c38f1778 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 10:02:07 +0000 Subject: [PATCH 16/34] v6.0 changelog --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0a2ca5..5e4b2d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,16 +3,39 @@ ## master (unreleased) ### New Features -- Utility script to detect feature overlap between new and existing CAPA rules [#1451](https://github.com/mandiant/capa/issues/1451) [@Aayush-Goel-04](https://github.com/aayush-goel-04) -- use fancy box drawing characters for default output #1586 @williballenthin -- use [pre-commit](https://pre-commit.com/) to invoke linters #1579 @williballenthin -- extract forwarded exports from PE files #1624 @williballenthin -- extract function and API names from ELF symtab entries @yelhamer https://github.com/mandiant/capa-rules/issues/736 ### Breaking Changes -- Update Metadata type in capa main [#1411](https://github.com/mandiant/capa/issues/1411) [@Aayush-Goel-04](https://github.com/aayush-goel-04) @manasghandat + +### New Rules (0) + +- + +### Bug Fixes + +### capa explorer IDA Pro plugin + +### Development + +### Raw diffs +- [capa v6.0...master](https://github.com/mandiant/capa/compare/v6.0...master) +- [capa-rules v6.0...master](https://github.com/mandiant/capa-rules/compare/v6.0...master) + +## v6.0 + +capa v6.0 brings many bug fixes and quality improvements, including 64 rule updates and 26 new rules. We're now publishing to PyPI via [Trusted Publishing](https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/) and have migrated to using a `pyproject.toml` file. @Aayush-Goel-04 contributed a lot of new code across many files, so please welcome them to the project, along with @anders-v @crowface28 @dkelly2e @RonnieSalomonsen and @ejfocampo as first-time rule contributors! + +For those that use capa as a library, we've introduced some limited breaking changes that better represent data types (versus less-structured data like dictionaries and strings). With the recent deprecation, we've also dropped support for Python 3.7. + +### New Features +- add script to detect feature overlap between new and existing capa rules [#1451](https://github.com/mandiant/capa/issues/1451) [@Aayush-Goel-04](https://github.com/aayush-goel-04) +- extract forwarded exports from PE files #1624 @williballenthin +- extract function and API names from ELF symtab entries @yelhamer https://github.com/mandiant/capa-rules/issues/736 +- use fancy box drawing characters for default output #1586 @williballenthin + +### Breaking Changes +- use a class to represent Metadata (not dict) #1411 @Aayush-Goel-04 @manasghandat +- use pathlib.Path to represent file paths #1534 @Aayush-Goel-04 - Python 3.8 is now the minimum supported Python version #1578 @williballenthin -- Updated file paths to use pathlib.Path for improved path handling and compatibility [#1534](https://github.com/mandiant/capa/issues/1534) [@Aayush-Goel-04](https://github.com/aayush-goel-04) ### New Rules (26) @@ -51,9 +74,9 @@ - symtab: fix struct.unpack() format for 64-bit ELF files @yelhamer - symtab: safeguard against ZeroDivisionError for files containing a symtab with a null entry size @yelhamer - improve ELF strtab and needed parsing @mr-tz -- better handle exceptional cases when parsing ELF files [#1458](https://github.com/mandiant/capa/issues/1458) [@Aayush-Goel-04](https://github.com/aayush-goel-04) -- Improved testing coverage for Binary Ninja Backend [#1446](https://github.com/mandiant/capa/issues/1446) [@Aayush-Goel-04](https://github.com/aayush-goel-04) -- Add logging and print redirect to tqdm for capa main [#749](https://github.com/mandiant/capa/issues/749) [@Aayush-Goel-04](https://github.com/aayush-goel-04) +- better handle exceptional cases when parsing ELF files #1458 @Aayush-Goel-04 +- improved testing coverage for Binary Ninja backend #1446 @Aayush-Goel-04 +- add logging and print redirect to tqdm for capa main #749 @Aayush-Goel-04 - extractor: fix binja installation path detection does not work with Python 3.11 - tests: refine the IDA test runner script #1513 @williballenthin - output: don't leave behind traces of progress bar @williballenthin @@ -68,11 +91,12 @@ - tests: make fixture available via conftest.py #1592 @williballenthin - publish via PyPI trusted publishing #1491 @williballenthin - migrate to pyproject.toml #1301 @williballenthin +- use [pre-commit](https://pre-commit.com/) to invoke linters #1579 @williballenthin ### Raw diffs -- [capa v5.1.0...master](https://github.com/mandiant/capa/compare/v5.1.0...master) -- [capa-rules v5.1.0...master](https://github.com/mandiant/capa-rules/compare/v5.1.0...master) +- [capa v5.1.0...v6.0](https://github.com/mandiant/capa/compare/v5.1.0...v6.0) +- [capa-rules v5.1.0...v6.0](https://github.com/mandiant/capa-rules/compare/v5.1.0...v6.0) ## v5.1.0 capa version 5.1.0 adds a Protocol Buffers (protobuf) format for result documents. Additionally, the [Vector35](https://vector35.com/) team contributed a new feature extractor using Binary Ninja. Other new features are a new CLI flag to override the detected operating system, functionality to read and render existing result documents, and a output color format that's easier to read. From 14f05891949b964386a1f139d5c12b148b6cae26 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 10:04:39 +0000 Subject: [PATCH 17/34] v6.0.0a1 --- CHANGELOG.md | 6 +++--- capa/version.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4b2d41..8d3633a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ - [capa v6.0...master](https://github.com/mandiant/capa/compare/v6.0...master) - [capa-rules v6.0...master](https://github.com/mandiant/capa-rules/compare/v6.0...master) -## v6.0 +## v6.0.0 capa v6.0 brings many bug fixes and quality improvements, including 64 rule updates and 26 new rules. We're now publishing to PyPI via [Trusted Publishing](https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/) and have migrated to using a `pyproject.toml` file. @Aayush-Goel-04 contributed a lot of new code across many files, so please welcome them to the project, along with @anders-v @crowface28 @dkelly2e @RonnieSalomonsen and @ejfocampo as first-time rule contributors! @@ -95,8 +95,8 @@ For those that use capa as a library, we've introduced some limited breaking cha ### Raw diffs -- [capa v5.1.0...v6.0](https://github.com/mandiant/capa/compare/v5.1.0...v6.0) -- [capa-rules v5.1.0...v6.0](https://github.com/mandiant/capa-rules/compare/v5.1.0...v6.0) +- [capa v5.1.0...v6.0.0](https://github.com/mandiant/capa/compare/v5.1.0...v6.0.0a1) +- [capa-rules v5.1.0...v6.0.0](https://github.com/mandiant/capa-rules/compare/v5.1.0...v6.0.0a1) ## v5.1.0 capa version 5.1.0 adds a Protocol Buffers (protobuf) format for result documents. Additionally, the [Vector35](https://vector35.com/) team contributed a new feature extractor using Binary Ninja. Other new features are a new CLI flag to override the detected operating system, functionality to read and render existing result documents, and a output color format that's easier to read. diff --git a/capa/version.py b/capa/version.py index 5ce717b2..183aee6b 100644 --- a/capa/version.py +++ b/capa/version.py @@ -5,7 +5,7 @@ # 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. -__version__ = "5.1.0" +__version__ = "6.0.0a1" def get_major_version(): From fff1248ec4dbb39af23ff675c73553c82a391985 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 10:07:18 +0000 Subject: [PATCH 18/34] changelog: fix links --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d3633a0..218be06b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,8 @@ ### Development ### Raw diffs -- [capa v6.0...master](https://github.com/mandiant/capa/compare/v6.0...master) -- [capa-rules v6.0...master](https://github.com/mandiant/capa-rules/compare/v6.0...master) +- [capa v6.0.0...master](https://github.com/mandiant/capa/compare/v6.0.0...master) +- [capa-rules v6.0.0...master](https://github.com/mandiant/capa-rules/compare/v6.0.0...master) ## v6.0.0 From 1c6434a38015af0fb6eabb6524ca6764a9905df2 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 10:10:36 +0000 Subject: [PATCH 19/34] changelog: remove old formatting --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 218be06b..03e7635a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,6 @@ For those that use capa as a library, we've introduced some limited breaking cha - anti-analysis/anti-av/patch-event-tracing-for-windows-function jakub.jozwiak@mandiant.com - data-manipulation/encoding/xor/covertly-decode-and-write-data-to-windows-directory-using-indirect-calls dan.kelly@mandiant.com - linking/runtime-linking/resolve-function-by-brute-ratel-badger-hash jakub.jozwiak@mandiant.com -- ### Bug Fixes - extractor: add a Binary Ninja test that asserts its version #1487 @xusheng6 From 15caa9ee6e63f35fa0f99130877f0e546f898e9b Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 13:35:24 +0200 Subject: [PATCH 20/34] ci: publish: remove incorrect name --- .github/workflows/publish.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 314f5261..92ba4b02 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,7 +32,6 @@ jobs: - name: upload package artifacts uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: - name: ${{ matrix.asset_name }} path: dist/* - name: upload package to GH Release uses: svenstaro/upload-release-action@2728235f7dc9ff598bd86ce3c274b74f802d2208 # v2 From be58f65ae5744e6caef210b2396b8eeaad550394 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 11:37:45 +0000 Subject: [PATCH 21/34] v6.0.0a2 --- capa/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/version.py b/capa/version.py index 183aee6b..aac8e525 100644 --- a/capa/version.py +++ b/capa/version.py @@ -5,7 +5,7 @@ # 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. -__version__ = "6.0.0a1" +__version__ = "6.0.0a2" def get_major_version(): From dbfcbaa98e43b0ae5c766a924c404be16683fd2e Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 12:23:15 +0000 Subject: [PATCH 22/34] ci: publish: fix file name globbing --- .github/workflows/publish.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 92ba4b02..a1f43af6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,12 +33,14 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: path: dist/* - - name: upload package to GH Release + - name: upload package to GitHub release uses: svenstaro/upload-release-action@2728235f7dc9ff598bd86ce3c274b74f802d2208 # v2 with: repo_token: ${{ secrets.GITHUB_TOKEN}} file: dist/* tag: ${{ github.ref }} + overwrite: true + file_glob: true - name: publish package uses: pypa/gh-action-pypi-publish@f5622bde02b04381239da3573277701ceca8f6a0 # release/v1 with: From 5277f3b640457f63087478e331e0970b146958fb Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 12:23:25 +0000 Subject: [PATCH 23/34] v6.0.0a3 --- capa/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/version.py b/capa/version.py index aac8e525..aaf31aec 100644 --- a/capa/version.py +++ b/capa/version.py @@ -5,7 +5,7 @@ # 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. -__version__ = "6.0.0a2" +__version__ = "6.0.0a3" def get_major_version(): From 33cb81449c30316ba56553f629aabf543d633988 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 13:21:47 +0000 Subject: [PATCH 24/34] ci: publish: try to fix perm errors --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a1f43af6..44bfae4f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,8 +6,8 @@ on: release: types: [published] -permissions: - contents: write +#permissions: +# contents: write jobs: pypi-publish: From f99824d996b4af76b5eb0e74985059626e590063 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 13:22:11 +0000 Subject: [PATCH 25/34] v6.0.0a4 --- capa/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/version.py b/capa/version.py index aaf31aec..43dd4270 100644 --- a/capa/version.py +++ b/capa/version.py @@ -5,7 +5,7 @@ # 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. -__version__ = "6.0.0a3" +__version__ = "6.0.0a4" def get_major_version(): From e3c8cb74df7b5178564320e8fb043c92fffa0421 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 13:33:01 +0000 Subject: [PATCH 26/34] ci: publish: dev release --- .github/workflows/publish.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 44bfae4f..b8b71a92 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,6 +3,9 @@ name: publish to pypi on: + push: + branches: + - 'williballenthin-patch-1' release: types: [published] From 25624a1b46ad9e5ed9d45870fc073f3ee8388d5c Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 13:38:05 +0000 Subject: [PATCH 27/34] ci: publish: dev release --- .github/workflows/publish.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b8b71a92..bd0fe500 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,14 +36,14 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: path: dist/* - - name: upload package to GitHub release - uses: svenstaro/upload-release-action@2728235f7dc9ff598bd86ce3c274b74f802d2208 # v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN}} - file: dist/* - tag: ${{ github.ref }} - overwrite: true - file_glob: true + #- name: upload package to GitHub release + # uses: svenstaro/upload-release-action@2728235f7dc9ff598bd86ce3c274b74f802d2208 # v2 + # with: + # repo_token: ${{ secrets.GITHUB_TOKEN}} + # file: dist/* + # tag: ${{ github.ref }} + # overwrite: true + # file_glob: true - name: publish package uses: pypa/gh-action-pypi-publish@f5622bde02b04381239da3573277701ceca8f6a0 # release/v1 with: From b73e1e3d7f46f04a099f235889953340ba8b6001 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 13:56:04 +0000 Subject: [PATCH 28/34] pyproject: set readme context type --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1e73090f..a28e244c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ authors = [ {name = "Mike Hunhoff", email = "michael.hunhoff@mandiant.com"}, ] description = "The FLARE team's open-source tool to identify capabilities in executable files." +readme = {file = "README.md", content-type = "text/markdown"} license = {file = "LICENSE.txt"} requires-python = ">=3.8" keywords = ["malware analysis", "reverse engineering", "capability detection", "software behaviors", "capa", "FLARE"] @@ -50,11 +51,10 @@ dependencies = [ "pydantic==1.10.9", "protobuf==4.23.4", ] -dynamic = ["version", "readme"] +dynamic = ["version"] [tool.setuptools.dynamic] version = {attr = "capa.version.__version__"} -readme = {file = "README.md"} [tool.setuptools] packages = ["capa"] From 3899662cbdbacb690293b78b63af63e09b8b3678 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 14:00:09 +0000 Subject: [PATCH 29/34] v6.0.0 --- CHANGELOG.md | 4 ++-- capa/version.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8345b27..2f58683b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,8 +95,8 @@ For those that use capa as a library, we've introduced some limited breaking cha ### Raw diffs -- [capa v5.1.0...v6.0.0](https://github.com/mandiant/capa/compare/v5.1.0...v6.0.0a1) -- [capa-rules v5.1.0...v6.0.0](https://github.com/mandiant/capa-rules/compare/v5.1.0...v6.0.0a1) +- [capa v5.1.0...v6.0.0](https://github.com/mandiant/capa/compare/v5.1.0...v6.0.0) +- [capa-rules v5.1.0...v6.0.0](https://github.com/mandiant/capa-rules/compare/v5.1.0...v6.0.0) ## v5.1.0 capa version 5.1.0 adds a Protocol Buffers (protobuf) format for result documents. Additionally, the [Vector35](https://vector35.com/) team contributed a new feature extractor using Binary Ninja. Other new features are a new CLI flag to override the detected operating system, functionality to read and render existing result documents, and a output color format that's easier to read. diff --git a/capa/version.py b/capa/version.py index 43dd4270..f2f931fc 100644 --- a/capa/version.py +++ b/capa/version.py @@ -5,7 +5,7 @@ # 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. -__version__ = "6.0.0a4" +__version__ = "6.0.0" def get_major_version(): From 91b65d1d7f93bac7380cb4cdc50b4ed5eaa427f6 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 14:01:58 +0000 Subject: [PATCH 30/34] ci: publish: remove old commented code --- .github/workflows/publish.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bd0fe500..07f31067 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,8 +9,8 @@ on: release: types: [published] -#permissions: -# contents: write +permissions: + contents: write jobs: pypi-publish: @@ -36,14 +36,6 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: path: dist/* - #- name: upload package to GitHub release - # uses: svenstaro/upload-release-action@2728235f7dc9ff598bd86ce3c274b74f802d2208 # v2 - # with: - # repo_token: ${{ secrets.GITHUB_TOKEN}} - # file: dist/* - # tag: ${{ github.ref }} - # overwrite: true - # file_glob: true - name: publish package uses: pypa/gh-action-pypi-publish@f5622bde02b04381239da3573277701ceca8f6a0 # release/v1 with: From 70a1e660205ac940f42a76bb371b3ec799a721ce Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Tue, 18 Jul 2023 14:02:35 +0000 Subject: [PATCH 31/34] ci: publish: remove dev code --- .github/workflows/publish.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 07f31067..4188cf09 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,9 +3,6 @@ name: publish to pypi on: - push: - branches: - - 'williballenthin-patch-1' release: types: [published] From 1790dab1ab966cdb8c8ba6dd0589859fdeb58dd8 Mon Sep 17 00:00:00 2001 From: Ronnie Salomonsen Date: Wed, 19 Jul 2023 11:27:52 +0200 Subject: [PATCH 32/34] rules: Add forwarded export characteristics to rule syntax under file_scope --- capa/rules/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/capa/rules/__init__.py b/capa/rules/__init__.py index 066c3a11..45d822a5 100644 --- a/capa/rules/__init__.py +++ b/capa/rules/__init__.py @@ -106,6 +106,7 @@ SUPPORTED_FEATURES: Dict[str, Set] = { capa.features.common.Class, capa.features.common.Namespace, capa.features.common.Characteristic("mixed mode"), + capa.features.common.Characteristic("forwarded export"), }, FUNCTION_SCOPE: { capa.features.common.MatchedRule, From de6bdf062195807d0bdaf94ca46217b7abc584d7 Mon Sep 17 00:00:00 2001 From: Ronnie Salomonsen Date: Wed, 19 Jul 2023 15:05:10 +0200 Subject: [PATCH 33/34] Update CHANGELOG with fix for the new feature for forwarded export characteristics --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f58683b..e66b9d9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ For those that use capa as a library, we've introduced some limited breaking cha - output: don't leave behind traces of progress bar @williballenthin - import-to-ida: fix bug introduced with JSON report changes in v5 #1584 @williballenthin - main: don't show spinner when emitting debug messages #1636 @williballenthin +- rules: add forwarded export characteristics to rule syntax file scope #1653 @RonnieSalomonsen ### capa explorer IDA Pro plugin From 4eabee7329e3e57a56bf4bc4744ad244abe9f27b Mon Sep 17 00:00:00 2001 From: Capa Bot Date: Wed, 19 Jul 2023 13:49:59 +0000 Subject: [PATCH 34/34] Sync capa rules submodule --- CHANGELOG.md | 3 ++- README.md | 2 +- rules | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e66b9d9f..64996533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ ### Breaking Changes -### New Rules (0) +### New Rules (1) +- executable/pe/export/forwarded-export ronnie.salomonsen@mandiant.com - ### Bug Fixes diff --git a/README.md b/README.md index e7c3b5f6..1c08af30 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/flare-capa)](https://pypi.org/project/flare-capa) [![Last release](https://img.shields.io/github/v/release/mandiant/capa)](https://github.com/mandiant/capa/releases) -[![Number of rules](https://img.shields.io/badge/rules-823-blue.svg)](https://github.com/mandiant/capa-rules) +[![Number of rules](https://img.shields.io/badge/rules-824-blue.svg)](https://github.com/mandiant/capa-rules) [![CI status](https://github.com/mandiant/capa/workflows/CI/badge.svg)](https://github.com/mandiant/capa/actions?query=workflow%3ACI+event%3Apush+branch%3Amaster) [![Downloads](https://img.shields.io/github/downloads/mandiant/capa/total)](https://github.com/mandiant/capa/releases) [![License](https://img.shields.io/badge/license-Apache--2.0-green.svg)](LICENSE.txt) diff --git a/rules b/rules index 85a980a6..a49c174f 160000 --- a/rules +++ b/rules @@ -1 +1 @@ -Subproject commit 85a980a6cc8557af10bc1220eac9251ce3334ab5 +Subproject commit a49c174fee5058ca3617a23e782bdcadacb12406