From 14b0c5fdbf7645ede32913036996a903d8f9d815 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Fri, 19 May 2023 14:38:55 -0700 Subject: [PATCH 01/12] colton: ghidra runtime detection & GhidraFeatureExtractor --- capa/features/extractors/ghidra/__init__.py | 0 capa/features/extractors/ghidra/extractor.py | 14 +++++++ capa/features/extractors/ghidra/file.py | 0 capa/features/extractors/ghidra/global_.py | 0 capa/main.py | 39 ++++++++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 capa/features/extractors/ghidra/__init__.py create mode 100644 capa/features/extractors/ghidra/extractor.py create mode 100644 capa/features/extractors/ghidra/file.py create mode 100644 capa/features/extractors/ghidra/global_.py diff --git a/capa/features/extractors/ghidra/__init__.py b/capa/features/extractors/ghidra/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/capa/features/extractors/ghidra/extractor.py b/capa/features/extractors/ghidra/extractor.py new file mode 100644 index 00000000..65a5f13f --- /dev/null +++ b/capa/features/extractors/ghidra/extractor.py @@ -0,0 +1,14 @@ +import logging +import contextlib +from typing import Tuple, Iterator + +from capa.features.common import Feature +from capa.features.address import Address, AbsoluteVirtualAddress +from capa.features.extractors.base_extractor import FeatureExtractor +import capa.features.extractors.ghidra.global_ + +class GhidraFeatureExtractor(FeatureExtractor): + def __init__(self): + super().__init__() + self.global_features: List[Tuple[Feature, Address]] = [] + self.global_features.extend(capa.features.extractors.ghidra.global_.extract_os()) diff --git a/capa/features/extractors/ghidra/file.py b/capa/features/extractors/ghidra/file.py new file mode 100644 index 00000000..e69de29b diff --git a/capa/features/extractors/ghidra/global_.py b/capa/features/extractors/ghidra/global_.py new file mode 100644 index 00000000..e69de29b diff --git a/capa/main.py b/capa/main.py index 14411b0e..d56a04a1 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1311,6 +1311,31 @@ def ida_main(): print(capa.render.default.render(meta, rules, capabilities)) +def ghidra_main(): + import capa.rules + #import capa.render.default + #import capa.features.extractors.ghidra.extractor + import capa.features.extractors.ghidra.global_ + + logging.basicConfig(level=logging.INFO) + logging.getLogger().setLevel(logging.INFO) + + logger.debug("-" * 80) + logger.debug(" Using default embedded rules.") + logger.debug(" ") + logger.debug(" You can see the current default rule set here:") + logger.debug(" https://github.com/mandiant/capa-rules") + logger.debug("-" * 80) + + rules_path = os.path.join(get_default_root(), "rules") + logger.debug("rule path: %s", rules_path) + rules = get_rules([rules_path]) + + globl_features: List[Tuple[Feature, Address]] = [] + globl_features.extend(capa.features.extractors.ghidra.global_.extract_os()) + print(globl_features) + + def is_runtime_ida(): try: import idc @@ -1320,8 +1345,22 @@ def is_runtime_ida(): return True +def is_runtime_ghidra(): + try: + import ghidra.program.flatapi + except ImportError: + print("Not in Ghidra...") + return False + else: + return True + + if __name__ == "__main__": if is_runtime_ida(): ida_main() + elif is_runtime_ghidra(): + print("Calling Ghidra Main") + ghidra_main() else: sys.exit(main()) + From 1f09c923069be8597e21571d2836ce683578e3d8 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Fri, 19 May 2023 18:38:13 -0700 Subject: [PATCH 02/12] colton: OS extraction functionality implemented --- capa/features/extractors/ghidra/global_.py | 53 ++++++++++++++++++++++ capa/main.py | 2 - 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/capa/features/extractors/ghidra/global_.py b/capa/features/extractors/ghidra/global_.py index e69de29b..645fd587 100644 --- a/capa/features/extractors/ghidra/global_.py +++ b/capa/features/extractors/ghidra/global_.py @@ -0,0 +1,53 @@ +import logging +import contextlib +from io import BytesIO +from typing import Tuple, Iterator + +import ghidra.program.flatapi as flatapi +ghidraapi = flatapi.FlatProgramAPI(currentProgram) # Ghidrathon hacks :) + +import capa.features.extractors.elf +from capa.features.common import OS, ARCH_I386, ARCH_AMD64, OS_WINDOWS, Arch, Feature +from capa.features.address import NO_ADDRESS, Address + +logger = logging.getLogger(__name__) + +def extract_os() -> Iterator[Tuple[Feature, Address]]: + current_program = ghidraapi.getCurrentProgram() + format_name: str = current_program.getExecutableFormat() + + if "PE" in format_name: + yield OS(OS_WINDOWS), NO_ADDRESS + + elif "ELF" in format_name: + program_memory = current_program.getMemory() + fbytes_list = program_memory.getAllFileBytes() # java.util.List + fbytes = fbytes_list[0] # ghidra.program.database.mem.FileBytes + + # Java likes to return signed ints, so we must convert them + # back into unsigned bytes manually and write to BytesIO + pb_arr = b'' + for i in range(fbytes.getSize()): + pb_arr = pb_arr + (fbytes.getOriginalByte(i) & 0xff).to_bytes(1, 'little') + buf = BytesIO(pb_arr) + + with contextlib.closing(buf) as f: + os = capa.features.extractors.elf.detect_elf_os(f) + + yield OS(os), NO_ADDRESS + + else: + # we likely end up here: + # 1. handling shellcode, or + # 2. handling a new file format (e.g. macho) + # + # for (1) we can't do much - its shellcode and all bets are off. + # we could maybe accept a further CLI argument to specify the OS, + # but i think this would be rarely used. + # rules that rely on OS conditions will fail to match on shellcode. + # + # for (2), this logic will need to be updated as the format is implemented. + logger.debug("unsupported file format: %s, will not guess OS", format_name) + return + + diff --git a/capa/main.py b/capa/main.py index d56a04a1..39a2632a 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1349,7 +1349,6 @@ def is_runtime_ghidra(): try: import ghidra.program.flatapi except ImportError: - print("Not in Ghidra...") return False else: return True @@ -1359,7 +1358,6 @@ if __name__ == "__main__": if is_runtime_ida(): ida_main() elif is_runtime_ghidra(): - print("Calling Ghidra Main") ghidra_main() else: sys.exit(main()) From ffe089d4448603d0ee67b6b9af5bf6fc82742e24 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Fri, 19 May 2023 19:10:39 -0700 Subject: [PATCH 03/12] colton: GhidraFeatureExtractor constructor pulls OS & Arch --- capa/features/extractors/ghidra/extractor.py | 1 + capa/features/extractors/ghidra/global_.py | 30 +++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/capa/features/extractors/ghidra/extractor.py b/capa/features/extractors/ghidra/extractor.py index 65a5f13f..2f5a593a 100644 --- a/capa/features/extractors/ghidra/extractor.py +++ b/capa/features/extractors/ghidra/extractor.py @@ -12,3 +12,4 @@ class GhidraFeatureExtractor(FeatureExtractor): super().__init__() self.global_features: List[Tuple[Feature, Address]] = [] self.global_features.extend(capa.features.extractors.ghidra.global_.extract_os()) + self.global_features.extend(capa.features.extractors.ghidra.global_.extract_arch()) diff --git a/capa/features/extractors/ghidra/global_.py b/capa/features/extractors/ghidra/global_.py index 645fd587..b0fad5cf 100644 --- a/capa/features/extractors/ghidra/global_.py +++ b/capa/features/extractors/ghidra/global_.py @@ -3,6 +3,10 @@ import contextlib from io import BytesIO from typing import Tuple, Iterator +# imports for clarity +# note: currentProgram is a static variable accessible in +# the specific ghidra runtime environment +import ghidra.program.database.mem import ghidra.program.flatapi as flatapi ghidraapi = flatapi.FlatProgramAPI(currentProgram) # Ghidrathon hacks :) @@ -20,12 +24,13 @@ def extract_os() -> Iterator[Tuple[Feature, Address]]: yield OS(OS_WINDOWS), NO_ADDRESS elif "ELF" in format_name: - program_memory = current_program.getMemory() + program_memory = current_program.getMemory() # ghidra.program.database.mem.MemoryMapDB fbytes_list = program_memory.getAllFileBytes() # java.util.List fbytes = fbytes_list[0] # ghidra.program.database.mem.FileBytes # Java likes to return signed ints, so we must convert them # back into unsigned bytes manually and write to BytesIO + # note: May be deprecated if Jep has implements better support for Java Lists pb_arr = b'' for i in range(fbytes.getSize()): pb_arr = pb_arr + (fbytes.getOriginalByte(i) & 0xff).to_bytes(1, 'little') @@ -51,3 +56,26 @@ def extract_os() -> Iterator[Tuple[Feature, Address]]: return +def extract_arch() -> Iterator[Tuple[Feature, Address]]: + current_program = ghidraapi.getCurrentProgram() + lang_id = current_program.getMetadata().get('Language ID') + + if 'x86' in lang_id and '64' in lang_id: + yield Arch(ARCH_AMD64), NO_ADDRESS + + elif 'x86' in lang_id and '32' in lang_id: + yield Arch(ARCH_I386), NO_ADDRESS + + elif 'x86' not in lang_id: + logger.debug("unsupported architecture: non-32-bit nor non-64-bit intel") + return + + else: + # we likely end up here: + # 1. handling a new architecture (e.g. aarch64) + # + # for (1), this logic will need to be updated as the format is implemented. + logger.debug("unsupported architecture: %s", lang_id) + return + + From 50afc2f9b204a330f1367e2bf2c6efdd7df901fa Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Fri, 26 May 2023 17:51:48 -0700 Subject: [PATCH 04/12] colton: developing ghidra backend tests --- capa/main.py | 1 + tests/test_ghidra_features.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/test_ghidra_features.py diff --git a/capa/main.py b/capa/main.py index 39a2632a..6d270fc1 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1333,6 +1333,7 @@ def ghidra_main(): globl_features: List[Tuple[Feature, Address]] = [] globl_features.extend(capa.features.extractors.ghidra.global_.extract_os()) + globl_features.extend(capa.features.extractors.ghidra.global_.extract_arch()) print(globl_features) diff --git a/tests/test_ghidra_features.py b/tests/test_ghidra_features.py new file mode 100644 index 00000000..a4a9a748 --- /dev/null +++ b/tests/test_ghidra_features.py @@ -0,0 +1,25 @@ +import logging + +import fixtures +from fixtures import * + +import capa.main + +logger = logging.getLogger(__file__) + +# We need to skip the ghidra test if we cannot import ghidra modules, e.g., in GitHub CI. +ghidra_present: bool = False +try: + import ghidra.program.flatapi as flatapi + ghidraapi = flatapi.FlatProgramAPI(currentProgram) + + try: + current_program_test = ghidraapi.getCurrentProgram() + except RuntimeError as e: + logger.warning("Ghidra runtime not detected") + else: + ghidra_present = True +except ImportError: + pass + + From 78bd5e1e3b49796d5d58d33964dd6e22f02ba1b5 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Sun, 28 May 2023 19:04:31 -0700 Subject: [PATCH 05/12] colton: tests.yml installs Java, Ghidra, and Ghidrathon --- .github/workflows/tests.yml | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 64475f65..27d82ab6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -136,3 +136,43 @@ jobs: env: BN_LICENSE: ${{ secrets.BN_LICENSE }} run: pytest -v tests/test_binja_features.py # explicitly refer to the binja tests for performance. other tests run above. + + ghidra-tests: + name: Ghidra tests for ${{ matrix.python-version }} + runs-on: ubuntu-20.04 + needs: [code_style, rule_linter] + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.11"] + java-version: ["17"] + steps: + - name: Checkout capa with submodules + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4.5.0 + with: + python-version: ${{ matrix.python-version }} + - name: Set up Java ${{ matrix.java-version }} + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ matrix.java-version }} + - name: Install Ghidra + run: | + mkdir ./.github/ghidra + wget "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_10.3_build/ghidra_10.3_PUBLIC_20230510.zip" -O ./.github/ghidra/ghidra_10.3_PUBLIC.zip + unzip .github/ghidra/ghidra_10.3_PUBLIC.zip -d .github/ghidra/ + env: + GHIDRA_INSTALL_DIR: /github/ghidra + - name: Install Ghidrathon + run : | + mkdir ./.github/ghidrathon + wget "" + - name: Install pyyaml + run: sudo apt-get install -y libyaml-dev + - name: Install capa + run: pip install -e .[dev] + From 8c40e82796a1da8fe678118964694060999bc58b Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Mon, 29 May 2023 19:58:59 -0700 Subject: [PATCH 06/12] configuring runner for ghidra tests --- .github/workflows/tests.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 27d82ab6..de67a6f7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -146,6 +146,7 @@ jobs: matrix: python-version: ["3.7", "3.11"] java-version: ["17"] + gradle-version: ["7.3"] steps: - name: Checkout capa with submodules uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 @@ -160,17 +161,31 @@ jobs: with: distribution: 'temurin' java-version: ${{ matrix.java-version }} - - name: Install Ghidra + - name: Set up Gradle ${{ matrix.gradle-version }} + uses: actions/gradle/gradle-build-action@v2 + with: + gradle-version: ${{ matrix.gradle-version }} + - name: Install Ghidra 10.3 run: | mkdir ./.github/ghidra wget "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_10.3_build/ghidra_10.3_PUBLIC_20230510.zip" -O ./.github/ghidra/ghidra_10.3_PUBLIC.zip unzip .github/ghidra/ghidra_10.3_PUBLIC.zip -d .github/ghidra/ + export PATH="/.github/ghidra/ghidra_10.3_PUBLIC/Support:$PATH" env: - GHIDRA_INSTALL_DIR: /github/ghidra + GHIDRA_INSTALL_DIR: /github/ghidra/ghidra_10.3_PUBLIC + GHIDRA_EXT_DIR: /github/ghidra/ghidra_10.3_PUBLIC/Extensions + - name: Install Jep 4.1.1 + run : | + mkdir ./.github/jep + wget "https://github.com/ninia/jep/archive/refs/tags/v4.1.1.zip" -O ./.github/jep/jep-4.1.1.zip + unzip .github/jep/jep-4.1.1.zip -d .github/jep/ + pip install .github/jep/jep-4.1.1/ - name: Install Ghidrathon run : | mkdir ./.github/ghidrathon - wget "" + wget "https://github.com/mandiant/Ghidrathon/archive/refs/tags/v2.1.0.zip" -O ./.github/ghidrathon/ghidrathon-2.1.0.zip + unzip .github/ghidrathon/ghidrathon-2.1.0.zip -d .github/ghidrathon/ + gradle -p ./.github/ghidrathon/Ghidrathon-2.1.0/ - name: Install pyyaml run: sudo apt-get install -y libyaml-dev - name: Install capa From b35cfdaf6ae339cef35b3f2a871c05abbd9bc0b1 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Mon, 29 May 2023 20:13:35 -0700 Subject: [PATCH 07/12] workflow_dispatch - temp --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de67a6f7..fb8cc232 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,7 @@ on: branches: [ master ] pull_request: branches: [ master ] + workflow_dispatch: # save workspaces to speed up testing env: From 73183e9c1970831d2b479ed40a13d8085019709d Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Mon, 29 May 2023 20:16:10 -0700 Subject: [PATCH 08/12] run tests.yml on workflow dispatch --- .github/workflows/tests.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fb8cc232..4ab33047 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,11 +1,6 @@ name: CI -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - workflow_dispatch: +on: workflow_dispatch # save workspaces to speed up testing env: From 5af1a42bf1e0c45b2b303436423a9a007e537e77 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Mon, 29 May 2023 20:24:37 -0700 Subject: [PATCH 09/12] reverting tests.yml --- .github/workflows/tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ab33047..f346e5a3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,10 @@ name: CI -on: workflow_dispatch +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] # save workspaces to speed up testing env: From 16444fe5edececfebc9be31cefb039462a1bd952 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Thu, 1 Jun 2023 11:24:21 -0700 Subject: [PATCH 10/12] first working CI install --- .github/workflows/tests.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f346e5a3..f4933e5f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -140,13 +140,11 @@ jobs: ghidra-tests: name: Ghidra tests for ${{ matrix.python-version }} runs-on: ubuntu-20.04 - needs: [code_style, rule_linter] strategy: fail-fast: false matrix: python-version: ["3.7", "3.11"] java-version: ["17"] - gradle-version: ["7.3"] steps: - name: Checkout capa with submodules uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 @@ -161,19 +159,16 @@ jobs: with: distribution: 'temurin' java-version: ${{ matrix.java-version }} - - name: Set up Gradle ${{ matrix.gradle-version }} - uses: actions/gradle/gradle-build-action@v2 - with: - gradle-version: ${{ matrix.gradle-version }} + - name: Set up Gradle 7.3 # must be done manually due to no gradle build in capa + run: | + mkdir /opt/gradle + wget "https://services.gradle.org/distributions/gradle-7.3-bin.zip" -O /opt/gradle/gradle-7.3.zip + unzip /opt/gradle/gradle-7.3.zip -d /opt/gradle - name: Install Ghidra 10.3 run: | mkdir ./.github/ghidra wget "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_10.3_build/ghidra_10.3_PUBLIC_20230510.zip" -O ./.github/ghidra/ghidra_10.3_PUBLIC.zip unzip .github/ghidra/ghidra_10.3_PUBLIC.zip -d .github/ghidra/ - export PATH="/.github/ghidra/ghidra_10.3_PUBLIC/Support:$PATH" - env: - GHIDRA_INSTALL_DIR: /github/ghidra/ghidra_10.3_PUBLIC - GHIDRA_EXT_DIR: /github/ghidra/ghidra_10.3_PUBLIC/Extensions - name: Install Jep 4.1.1 run : | mkdir ./.github/jep @@ -185,7 +180,9 @@ jobs: mkdir ./.github/ghidrathon wget "https://github.com/mandiant/Ghidrathon/archive/refs/tags/v2.1.0.zip" -O ./.github/ghidrathon/ghidrathon-2.1.0.zip unzip .github/ghidrathon/ghidrathon-2.1.0.zip -d .github/ghidrathon/ - gradle -p ./.github/ghidrathon/Ghidrathon-2.1.0/ + workdir=$(pwd) + /opt/gradle/gradle-7.3/bin/gradle -p ./.github/ghidrathon/Ghidrathon-2.1.0/ -PGHIDRA_INSTALL_DIR=$workdir/.github/ghidra/ghidra_10.3_PUBLIC + unzip .github/ghidrathon/Ghidrathon-2.1.0/dist/*.zip -d $workdir/.github/ghidra/ghidra_10.3_PUBLIC/Extensions - name: Install pyyaml run: sudo apt-get install -y libyaml-dev - name: Install capa From b849cfd4a5b0ebf302951b61570ccdb68e6d459a Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Fri, 2 Jun 2023 22:41:29 -0700 Subject: [PATCH 11/12] ghidra ci setup, test files in development --- .github/workflows/tests.yml | 2 +- tests/fixtures.py | 12 +++++++++++ tests/test_ghidra_features.py | 38 +++++++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f4933e5f..841044ee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -155,7 +155,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Set up Java ${{ matrix.java-version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3 with: distribution: 'temurin' java-version: ${{ matrix.java-version }} diff --git a/tests/fixtures.py b/tests/fixtures.py index 04c9c53b..612f49a7 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -183,6 +183,18 @@ def get_binja_extractor(path): return extractor +@lru_cache(maxsize=1) +def get_ghidra_extractor(path): + import capa.features.extractors.ghidra.extractor + + extractor = capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor(path) + + # overload the extractor so that the fixture exposes `extractor.path` + setattr(extractor, "path", path) + + return extractor + + def extract_global_features(extractor): features = collections.defaultdict(set) for feature, va in extractor.extract_global_features(): diff --git a/tests/test_ghidra_features.py b/tests/test_ghidra_features.py index a4a9a748..ff8e6485 100644 --- a/tests/test_ghidra_features.py +++ b/tests/test_ghidra_features.py @@ -1,11 +1,21 @@ +import sys import logging +import os.path +import binascii +import traceback -import fixtures -from fixtures import * +import pytest -import capa.main +try: + sys.path.append(os.path.dirname(__file__)) + import fixtures + from fixtures import * +finally: + sys.path.pop() + + +logger = logging.getLogger("test_ghidra_features") -logger = logging.getLogger(__file__) # We need to skip the ghidra test if we cannot import ghidra modules, e.g., in GitHub CI. ghidra_present: bool = False @@ -23,3 +33,23 @@ except ImportError: pass +@pytest.mark.skipif(ghidra_present is False, reason="Skip ghidra tests if the ghidra Python API is not installed") +@fixtures.parametrize( + "sample,scope,feature,expected", + fixtures.FEATURE_PRESENCE_TESTS, + indirect=["sample", "scope"], +) +def test_ghidra_features(sample, scope, feature, expected): + fixtures.do_test_feature_presence(fixtures.get_ghidra_extractor, sample, scope, feature, expected) + + +@pytest.mark.skipif(ghidra_present is False, reason="Skip ghidra tests if the ghidra Python API is not installed") +@fixtures.parametrize( + "sample,scope,feature,expected", + fixtures.FEATURE_COUNT_TESTS, + indirect=["sample", "scope"], +) +def test_ghidra_feature_counts(sample, scope, feature, expected): + fixtures.do_test_feature_count(fixtures.get_ghidra_extractor, sample, scope, feature, expected) + + From a7639d33b910457a9fe98cbf63bad2e48b15cb99 Mon Sep 17 00:00:00 2001 From: colton-gabertan Date: Fri, 2 Jun 2023 23:11:18 -0700 Subject: [PATCH 12/12] colton: update CHANGELOG --- CHANGELOG.md | 3 ++- capa/main.py | 1 + rules | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe6ae2b..27915d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 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) +- extractor: Implement Ghidra Backend [@colton-gabertan](https://github.com/colton-gabertan) ### Breaking Changes @@ -16,7 +17,7 @@ - communication/mailslot/read-from-mailslot nick.simonian@mandiant.com - nursery/hash-data-using-sha512managed-in-dotnet jonathanlepore@google.com - nursery/compiled-with-exescript jonathanlepore@google.com -- + ### Bug Fixes - extractor: update vivisect Arch extraction #1334 @mr-tz diff --git a/capa/main.py b/capa/main.py index ee9676cd..d4978d31 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1337,6 +1337,7 @@ def ghidra_main(): logger.debug("rule path: %s", rules_path) rules = get_rules([rules_path]) + # temp test for OS & ARCH extractions globl_features: List[Tuple[Feature, Address]] = [] globl_features.extend(capa.features.extractors.ghidra.global_.extract_os()) globl_features.extend(capa.features.extractors.ghidra.global_.extract_arch()) diff --git a/rules b/rules index 188e6552..312d4cad 160000 --- a/rules +++ b/rules @@ -1 +1 @@ -Subproject commit 188e65528ec496eaaa792c3470cb4ab680a1b156 +Subproject commit 312d4cad891498e1d360dffcc98f669b63869c94