Compare commits

...

32 Commits

Author SHA1 Message Date
dependabot[bot]
9b42b45d21 build(deps-dev): bump flake8-bugbear from 24.12.12 to 25.10.21 (#2773)
* build(deps-dev): bump flake8-bugbear from 24.12.12 to 25.10.21

Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 24.12.12 to 25.10.21.
- [Release notes](https://github.com/PyCQA/flake8-bugbear/releases)
- [Commits](https://github.com/PyCQA/flake8-bugbear/compare/24.12.12...25.10.21)

---
updated-dependencies:
- dependency-name: flake8-bugbear
  dependency-version: 25.10.21
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix flake8 raised bugs

* use super

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mr-tz <moritz.raabe@mandiant.com>
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2025-12-04 10:19:16 -07:00
Capa Bot
d17264c928 Sync capa rules submodule 2025-12-04 17:17:51 +00:00
Capa Bot
f313852e70 Sync capa rules submodule 2025-12-04 12:11:09 +00:00
Capa Bot
c0ae1352c6 Sync capa-testfiles submodule 2025-12-03 21:00:48 +00:00
dependabot[bot]
ccb3e6de74 build(deps-dev): bump flake8-comprehensions from 3.16.0 to 3.17.0 (#2782)
Bumps [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) from 3.16.0 to 3.17.0.
- [Changelog](https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/flake8-comprehensions/compare/3.16.0...3.17.0)

---
updated-dependencies:
- dependency-name: flake8-comprehensions
  dependency-version: 3.17.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 11:27:08 -07:00
dependabot[bot]
26c6ffd62d build(deps-dev): bump ruff from 0.12.0 to 0.14.7 (#2781)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.12.0 to 0.14.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.12.0...0.14.7)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.14.7
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 11:26:34 -07:00
Capa Bot
18923601c7 Sync capa rules submodule 2025-11-25 20:39:18 +00:00
0x1622
1568ce4832 Use SafeLoader for YAML (#2776) 2025-11-25 07:01:23 -07:00
Mike Hunhoff
ffce77b13d ci: deprecate macos-13 runner and use Python v3.13 for testing (#2777) 2025-11-24 19:53:39 -07:00
dependabot[bot]
895b2440c0 build(deps-dev): bump pre-commit from 4.2.0 to 4.5.0 (#2772)
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.2.0 to 4.5.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.2.0...v4.5.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-version: 4.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 08:40:13 -07:00
dependabot[bot]
c901f809a2 build(deps-dev): bump black from 25.1.0 to 25.11.0 (#2771)
Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.11.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/25.1.0...25.11.0)

---
updated-dependencies:
- dependency-name: black
  dependency-version: 25.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 08:39:17 -07:00
dependabot[bot]
308b3e5c1c build(deps): bump xmltodict from 0.14.2 to 1.0.2 (#2774)
Bumps [xmltodict](https://github.com/martinblech/xmltodict) from 0.14.2 to 1.0.2.
- [Release notes](https://github.com/martinblech/xmltodict/releases)
- [Changelog](https://github.com/martinblech/xmltodict/blob/master/CHANGELOG.md)
- [Commits](https://github.com/martinblech/xmltodict/compare/v0.14.2...v1.0.2)

---
updated-dependencies:
- dependency-name: xmltodict
  dependency-version: 1.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 08:30:16 -07:00
Mike Hunhoff
7844ebb144 v9.3.1 (#2769) 2025-11-20 08:37:49 -07:00
dependabot[bot]
e393cff0e1 build(deps): bump glob from 10.4.2 to 10.5.0 in /web/explorer (#2766)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.4.2 to 10.5.0.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.4.2...v10.5.0)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 10.5.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-19 08:34:25 -07:00
Mike Hunhoff
7780b9e8a8 explorer: add missing ida-netnode dependency to project.toml (#2765) 2025-11-18 08:55:57 -07:00
Mike Hunhoff
8d39765e7b ci: bump binja minor version (#2763) 2025-11-17 11:10:46 -07:00
dependabot[bot]
dec0bcfe79 build(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 in /web/explorer (#2758)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-17 10:02:54 -07:00
dependabot[bot]
99ccecba4e build(deps): bump humanize from 4.13.0 to 4.14.0 (#2762)
Bumps [humanize](https://github.com/python-humanize/humanize) from 4.13.0 to 4.14.0.
- [Release notes](https://github.com/python-humanize/humanize/releases)
- [Commits](https://github.com/python-humanize/humanize/compare/4.13.0...4.14.0)

---
updated-dependencies:
- dependency-name: humanize
  dependency-version: 4.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-17 09:10:40 -07:00
dependabot[bot]
af27463c37 build(deps-dev): bump pyinstaller from 6.14.1 to 6.16.0 (#2761)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 6.14.1 to 6.16.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v6.14.1...v6.16.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-version: 6.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-17 09:10:30 -07:00
dependabot[bot]
f4f47b4d55 build(deps): bump protobuf from 6.31.1 to 6.33.1 (#2760)
Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.31.1 to 6.33.1.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl)
- [Commits](https://github.com/protocolbuffers/protobuf/commits)

---
updated-dependencies:
- dependency-name: protobuf
  dependency-version: 6.33.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-17 09:10:19 -07:00
dependabot[bot]
adc2401136 build(deps): bump pycparser from 2.22 to 2.23 (#2759)
Bumps [pycparser](https://github.com/eliben/pycparser) from 2.22 to 2.23.
- [Release notes](https://github.com/eliben/pycparser/releases)
- [Changelog](https://github.com/eliben/pycparser/blob/main/CHANGES)
- [Commits](https://github.com/eliben/pycparser/compare/release_v2.22...release_v2.23)

---
updated-dependencies:
- dependency-name: pycparser
  dependency-version: '2.23'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-17 09:10:09 -07:00
Moritz
0ff7855467 Release v9.3.0 2025-11-12 17:30:07 +01:00
Capa Bot
d5411cadad Sync capa rules submodule 2025-11-12 10:26:46 +00:00
dependabot[bot]
cbd6d2a189 build(deps): bump pydantic from 2.11.4 to 2.12.4 (#2755)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.4 to 2.12.4.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.12.4/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.4...v2.12.4)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.12.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 09:24:02 -07:00
dependabot[bot]
29af6dc875 build(deps-dev): bump build from 1.2.2 to 1.3.0 (#2754)
Bumps [build](https://github.com/pypa/build) from 1.2.2 to 1.3.0.
- [Release notes](https://github.com/pypa/build/releases)
- [Changelog](https://github.com/pypa/build/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/build/compare/1.2.2...1.3.0)

---
updated-dependencies:
- dependency-name: build
  dependency-version: 1.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 09:23:14 -07:00
Moritz
66a3aac815 Merge pull request #2753 from mandiant/fix/capa-explorer-qt-load
delay import to not load Qt* when running under idalib
2025-11-10 16:51:14 +01:00
mr-tz
7525de7bbd delay import to not load Qt* when running under idalib
closes #2752
2025-11-10 12:13:35 +00:00
Capa Bot
cbd1cb2b7d Sync capa rules submodule 2025-11-07 07:40:05 +00:00
Matthew Haigh
503c34b8f9 added mailinglist cta (#2744)
* added mailinglist cta

* Update README.md

Added mailto: link for better user experience

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: Matt Haigh <matthaigh@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-03 09:11:14 -07:00
dependabot[bot]
888295b37a build(deps): bump types-protobuf from 6.30.2.20250516 to 6.32.1.20250918 (#2733)
Bumps [types-protobuf](https://github.com/typeshed-internal/stub_uploader) from 6.30.2.20250516 to 6.32.1.20250918.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-protobuf
  dependency-version: 6.32.1.20250918
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 09:07:26 -07:00
dependabot[bot]
5f9c908315 build(deps): bump pip from 25.2 to 25.3 (#2741)
Bumps [pip](https://github.com/pypa/pip) from 25.2 to 25.3.
- [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/pip/compare/25.2...25.3)

---
updated-dependencies:
- dependency-name: pip
  dependency-version: '25.3'
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2025-11-03 09:07:02 -07:00
Willi Ballenthin
cb2e2323f9 explorer: add support for IDA 9.2 (#2723)
* ida: add Qt compatibility layer for PyQt5 and PySide6

Introduce a new module `qt_compat.py` providing a unified import
interface and API compatibility for Qt modules. It handles differences between
PyQt5 (used in IDA <9.2) and PySide6 (used in IDA >=9.2). Update all
plugin modules to import Qt components via this compatibility layer
instead of directly importing from PyQt5. This enhances plugin
compatibility across different IDA versions.

thanks @mike-hunhoff!

changelog

* qt_compat: use __all__ rather than noqa

---------

Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2025-11-03 13:29:06 +01:00
25 changed files with 235 additions and 88 deletions

View File

@@ -1,5 +1,5 @@
[tool.bumpversion]
current_version = "9.2.1"
current_version = "9.3.1"
[[tool.bumpversion.files]]
filename = "capa/version.py"
@@ -19,4 +19,9 @@ replace = '"flare-capa=={new_version}"'
[[tool.bumpversion.files]]
filename = "CHANGELOG.md"
search = "v{current_version}...master"
replace = "{current_version}...{new_version}"
replace = "v{current_version}...{new_version}"
[[tool.bumpversion.files]]
filename = "CHANGELOG.md"
search = "master (unreleased)"
replace = "v{new_version}"

View File

@@ -46,8 +46,8 @@ jobs:
# artifact_name: capa.exe
# asset_name: windows-arm64
# python_version: '3.12'
- os: macos-13
# use older macOS for assumed better portability
- os: macos-15-intel
# macos-15-intel is the lowest native intel build
artifact_name: capa
asset_name: macos
python_version: '3.10'

View File

@@ -42,10 +42,10 @@ jobs:
- name: Checkout capa
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# use latest available python to take advantage of best performance
- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: "3.12"
python-version: "3.13"
- name: Install dependencies
run: |
pip install -r requirements.txt
@@ -70,10 +70,10 @@ jobs:
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: recursive
- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: "3.12"
python-version: "3.13"
- name: Install capa
run: |
pip install -r requirements.txt
@@ -88,13 +88,11 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, windows-2022, macos-13]
os: [ubuntu-22.04, ubuntu-22.04-arm, windows-2022, macos-15-intel, macos-14]
# across all operating systems
python-version: ["3.10", "3.11"]
python-version: ["3.10", "3.13"]
include:
# on Ubuntu run these as well
- os: ubuntu-22.04
python-version: "3.10"
- os: ubuntu-22.04
python-version: "3.11"
- os: ubuntu-22.04
@@ -131,7 +129,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11"]
python-version: ["3.10", "3.13"]
steps:
- name: Checkout capa with submodules
# do only run if BN_SERIAL is available, have to do this in every step, see https://github.com/orgs/community/discussions/26726#discussioncomment-3253118
@@ -173,7 +171,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11"]
python-version: ["3.10", "3.13"]
java-version: ["17"]
ghidra-version: ["11.0.1"]
public-version: ["PUBLIC_20240130"] # for ghidra releases

1
.gitignore vendored
View File

@@ -122,6 +122,7 @@ scripts/perf/*.zip
*/.DS_Store
Pipfile
Pipfile.lock
uv.lock
/cache/
.github/binja/binaryninja
.github/binja/download_headless.py

View File

@@ -3,11 +3,61 @@
## master (unreleased)
### New Features
### Breaking Changes
### New Rules (4)
- nursery/run-as-nodejs-native-module mehunhoff@google.com
- nursery/inject-shellcode-using-thread-pool-work-insertion-with-tp_io still@teamt5.org
- nursery/inject-shellcode-using-thread-pool-work-insertion-with-tp_timer still@teamt5.org
- nursery/inject-shellcode-using-thread-pool-work-insertion-with-tp_work still@teamt5.org
-
### Bug Fixes
- Fixed insecure deserialization vulnerability in YAML loading @0x1622 (#2770)
### capa Explorer Web
### capa Explorer IDA Pro plugin
### Development
- ci: deprecate macos-13 runner and use Python v3.13 for testing @mike-hunhoff #2777
### Raw diffs
- [capa v9.3.1...master](https://github.com/mandiant/capa/compare/v9.3.1...master)
- [capa-rules v9.3.1...master](https://github.com/mandiant/capa-rules/compare/v9.3.1...master)
## v9.3.1
This patch release fixes a missing import for the capa explorer plugin for IDA Pro.
### Bug Fixes
- add missing ida-netnode dependency to project.toml @mike-hunhoff #2765
### Development
- ci: bump binja min version @mike-hunhoff #2763
### Raw diffs
- [capa v9.3.0...master](https://github.com/mandiant/capa/compare/v9.3.0...master)
- [capa-rules v9.3.0...master](https://github.com/mandiant/capa-rules/compare/v9.3.0...master)
## v9.3.0
capa v9.3.0 comes with over 20 new and/or impoved rules.
For IDA users the capa explorer plugin is now available via the IDA Pro plugin repository and contains Qt compatibility layer for PyQt5 and PySide6 support.
Additionally a Binary Ninja bug has been fixed. Released binaries now include ARM64 binaries (Linux and macOS).
### New Features
- ci: add support for arm64 binary releases
### Breaking Changes
### New Rules (21)
### New Rules (24)
- anti-analysis/anti-vm/vm-detection/detect-mouse-movement-via-activity-checks-on-windows tevajdr@gmail.com
- nursery/create-executable-heap moritz.raabe@mandiant.com
@@ -29,7 +79,9 @@
- host-interaction/network/routing-table/get-routing-table michael.hunhoff@mandiant.com
- host-interaction/file-system/use-io_uring-io-interface-on-linux jakubjozwiak@google.com
- collection/keylog/log-keystrokes-via-direct-input zeze-zeze
-
- nursery/compiled-from-fsharp mehunhoff@google.com
- nursery/decrypt-data-using-aes-via-dotnet mehunhoff@google.com
- nursery/get-dotnet-assembly-entry-point mehunhoff@google.com
### Bug Fixes
@@ -40,6 +92,8 @@
### capa Explorer IDA Pro plugin
- add `ida-plugin.json` for inclusion in the IDA Pro plugin repository @williballenthin
- ida plugin: add Qt compatibility layer for PyQt5 and PySide6 support @williballenthin #2707
- delay import to not load Qt* when running under idalib @mr-tz #2752
### Development
@@ -47,8 +101,8 @@
- dev: add bumpmyversion to bump and sync versions across the project @mr-tz
### Raw diffs
- [capa v9.2.1...master](https://github.com/mandiant/capa/compare/v9.2.1...master)
- [capa-rules v9.2.1...master](https://github.com/mandiant/capa-rules/compare/v9.2.1...master)
- [capa v9.2.1...9.3.0](https://github.com/mandiant/capa/compare/v9.2.1...9.3.0)
- [capa-rules v9.2.1...9.3.0](https://github.com/mandiant/capa-rules/compare/v9.2.1...9.3.0)
## v9.2.1

View File

@@ -315,3 +315,6 @@ If you use Ghidra, then you can use the [capa + Ghidra integration](/capa/ghidra
## capa testfiles
The [capa-testfiles repository](https://github.com/mandiant/capa-testfiles) contains the data we use to test capa's code and rules
## mailing list
Subscribe to the FLARE mailing list for community announcements! Email "subscribe" to [flare-external@google.com](mailto:flare-external@google.com?subject=subscribe).

View File

@@ -17,7 +17,6 @@ import logging
import idaapi
import ida_kernwin
from capa.ida.plugin.form import CapaExplorerForm
from capa.ida.plugin.icon import ICON
logger = logging.getLogger(__name__)
@@ -74,6 +73,9 @@ class CapaExplorerPlugin(idaapi.plugin_t):
arg (int): bitflag. Setting LSB enables automatic analysis upon
loading. The other bits are currently undefined. See `form.Options`.
"""
# delay import to not trigger load of Qt components when not running in idaq, i.e., in idalib
from capa.ida.plugin.form import CapaExplorerForm
if not self.form:
self.form = CapaExplorerForm(self.PLUGIN_NAME, arg)
else:

View File

@@ -14,9 +14,9 @@
import ida_kernwin
from PyQt5 import QtCore
from capa.ida.plugin.error import UserCancelledError
from capa.ida.plugin.qt_compat import QtCore, Signal
from capa.features.extractors.ida.extractor import IdaFeatureExtractor
from capa.features.extractors.base_extractor import FunctionHandle
@@ -24,7 +24,7 @@ from capa.features.extractors.base_extractor import FunctionHandle
class CapaExplorerProgressIndicator(QtCore.QObject):
"""implement progress signal, used during feature extraction"""
progress = QtCore.pyqtSignal(str)
progress = Signal(str)
def update(self, text):
"""emit progress update

View File

@@ -23,7 +23,6 @@ from pathlib import Path
import idaapi
import ida_kernwin
import ida_settings
from PyQt5 import QtGui, QtCore, QtWidgets
import capa.main
import capa.rules
@@ -51,6 +50,7 @@ from capa.ida.plugin.hooks import CapaExplorerIdaHooks
from capa.ida.plugin.model import CapaExplorerDataModel
from capa.ida.plugin.proxy import CapaExplorerRangeProxyModel, CapaExplorerSearchProxyModel
from capa.ida.plugin.extractor import CapaExplorerFeatureExtractor
from capa.ida.plugin.qt_compat import QtGui, QtCore, QtWidgets
from capa.features.extractors.base_extractor import FunctionHandle
logger = logging.getLogger(__name__)
@@ -1358,7 +1358,7 @@ class CapaExplorerForm(idaapi.PluginForm):
@param state: checked state
"""
if state == QtCore.Qt.Checked:
if state:
self.limit_results_to_function(idaapi.get_func(idaapi.get_screen_ea()))
else:
self.range_model_proxy.reset_address_range_filter()
@@ -1367,7 +1367,7 @@ class CapaExplorerForm(idaapi.PluginForm):
def slot_checkbox_limit_features_by_ea(self, state):
""" """
if state == QtCore.Qt.Checked:
if state:
self.view_rulegen_features.filter_items_by_ea(idaapi.get_screen_ea())
else:
self.view_rulegen_features.show_all_items()

View File

@@ -3,7 +3,7 @@
"plugin": {
"name": "capa",
"entryPoint": "capa_explorer.py",
"version": "9.2.1",
"version": "9.3.1",
"idaVersions": ">=7.4",
"description": "Identify capabilities in executable files using FLARE's capa framework",
"license": "Apache-2.0",
@@ -12,7 +12,7 @@
"api-scripting-and-automation",
"ui-ux-and-visualization"
],
"pythonDependencies": ["flare-capa==9.2.1"],
"pythonDependencies": ["flare-capa==9.3.1"],
"urls": {
"repository": "https://github.com/mandiant/capa"
},

View File

@@ -18,10 +18,10 @@ from typing import Iterator, Optional
import idc
import idaapi
from PyQt5 import QtCore
import capa.ida.helpers
from capa.features.address import Address, FileOffsetAddress, AbsoluteVirtualAddress
from capa.ida.plugin.qt_compat import QtCore, qt_get_item_flag_tristate
def info_to_name(display):
@@ -55,7 +55,7 @@ class CapaExplorerDataItem:
self.flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
if self._can_check:
self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate
self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | qt_get_item_flag_tristate()
if self.pred:
self.pred.appendChild(self)

View File

@@ -18,7 +18,6 @@ from collections import deque
import idc
import idaapi
from PyQt5 import QtGui, QtCore
import capa.rules
import capa.ida.helpers
@@ -42,6 +41,7 @@ from capa.ida.plugin.item import (
CapaExplorerInstructionViewItem,
)
from capa.features.address import Address, AbsoluteVirtualAddress
from capa.ida.plugin.qt_compat import QtGui, QtCore
# default highlight color used in IDA window
DEFAULT_HIGHLIGHT = 0xE6C700
@@ -269,7 +269,7 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
visited.add(child_index)
for idx in range(self.rowCount(child_index)):
stack.append(child_index.child(idx, 0))
stack.append(self.index(idx, 0, child_index))
def reset_ida_highlighting(self, item, checked):
"""reset IDA highlight for item

View File

@@ -12,10 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from capa.ida.plugin.model import CapaExplorerDataModel
from capa.ida.plugin.qt_compat import Qt, QtCore
class CapaExplorerRangeProxyModel(QtCore.QSortFilterProxyModel):

View File

@@ -0,0 +1,79 @@
# Copyright 2020 Google LLC
#
# 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
"""
Qt compatibility layer for capa IDA Pro plugin.
Handles PyQt5 (IDA < 9.2) vs PySide6 (IDA >= 9.2) differences.
This module provides a unified import interface for Qt modules and handles
API changes between Qt5 and Qt6.
"""
try:
# IDA 9.2+ uses PySide6
from PySide6 import QtGui, QtCore, QtWidgets
from PySide6.QtGui import QAction
QT_LIBRARY = "PySide6"
Signal = QtCore.Signal
except ImportError:
# Older IDA versions use PyQt5
try:
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QAction
QT_LIBRARY = "PyQt5"
Signal = QtCore.pyqtSignal
except ImportError:
raise ImportError("Neither PySide6 nor PyQt5 is available. Cannot initialize capa IDA plugin.")
Qt = QtCore.Qt
def qt_get_item_flag_tristate():
"""
Get the tristate item flag compatible with Qt5 and Qt6.
Qt5 (PyQt5): Uses Qt.ItemIsTristate
Qt6 (PySide6): Qt.ItemIsTristate was removed, uses Qt.ItemIsAutoTristate
ItemIsAutoTristate automatically manages tristate based on child checkboxes,
matching the original ItemIsTristate behavior where parent checkboxes reflect
the check state of their children.
Returns:
int: The appropriate flag value for the Qt version
Raises:
AttributeError: If the tristate flag cannot be found in the Qt library
"""
if QT_LIBRARY == "PySide6":
# Qt6: ItemIsTristate was removed, replaced with ItemIsAutoTristate
# Try different possible locations (API varies slightly across PySide6 versions)
if hasattr(Qt, "ItemIsAutoTristate"):
return Qt.ItemIsAutoTristate
elif hasattr(Qt, "ItemFlag") and hasattr(Qt.ItemFlag, "ItemIsAutoTristate"):
return Qt.ItemFlag.ItemIsAutoTristate
else:
raise AttributeError(
"Cannot find ItemIsAutoTristate in PySide6. "
+ "Your PySide6 version may be incompatible with capa. "
+ f"Available Qt attributes: {[attr for attr in dir(Qt) if 'Item' in attr]}"
)
else:
# Qt5: Use the original ItemIsTristate flag
return Qt.ItemIsTristate
__all__ = ["qt_get_item_flag_tristate", "Signal", "QAction", "QtGui", "QtCore", "QtWidgets"]

View File

@@ -18,7 +18,6 @@ from collections import Counter
import idc
import idaapi
from PyQt5 import QtGui, QtCore, QtWidgets
import capa.rules
import capa.engine
@@ -28,6 +27,7 @@ import capa.features.basicblock
from capa.ida.plugin.item import CapaExplorerFunctionItem
from capa.features.address import AbsoluteVirtualAddress, _NoAddress
from capa.ida.plugin.model import CapaExplorerDataModel
from capa.ida.plugin.qt_compat import QtGui, QtCore, Signal, QAction, QtWidgets
MAX_SECTION_SIZE = 750
@@ -147,7 +147,7 @@ def calc_item_depth(o):
def build_action(o, display, data, slot):
""" """
action = QtWidgets.QAction(display, o)
action = QAction(display, o)
action.setData(data)
action.triggered.connect(lambda checked: slot(action))
@@ -312,7 +312,7 @@ class CapaExplorerRulegenPreview(QtWidgets.QTextEdit):
class CapaExplorerRulegenEditor(QtWidgets.QTreeWidget):
updated = QtCore.pyqtSignal()
updated = Signal()
def __init__(self, preview, parent=None):
""" """

View File

@@ -392,6 +392,7 @@ class ShouldExitError(Exception):
"""raised when a main-related routine indicates the program should exit."""
def __init__(self, status_code: int):
super().__init__(status_code)
self.status_code = status_code

View File

@@ -274,12 +274,8 @@ SUPPORTED_FEATURES[Scope.FUNCTION].update(SUPPORTED_FEATURES[Scope.BASIC_BLOCK])
class InvalidRule(ValueError):
def __init__(self, msg):
super().__init__()
self.msg = msg
def __str__(self):
return f"invalid rule: {self.msg}"
return f"invalid rule: {super().__str__()}"
def __repr__(self):
return str(self)
@@ -289,20 +285,15 @@ class InvalidRuleWithPath(InvalidRule):
def __init__(self, path, msg):
super().__init__(msg)
self.path = path
self.msg = msg
self.__cause__ = None
def __str__(self):
return f"invalid rule: {self.path}: {self.msg}"
return f"invalid rule: {self.path}: {super(InvalidRule, self).__str__()}"
class InvalidRuleSet(ValueError):
def __init__(self, msg):
super().__init__()
self.msg = msg
def __str__(self):
return f"invalid rule set: {self.msg}"
return f"invalid rule set: {super().__str__()}"
def __repr__(self):
return str(self)
@@ -1102,15 +1093,15 @@ class Rule:
@lru_cache()
def _get_yaml_loader():
try:
# prefer to use CLoader to be fast, see #306
# prefer to use CLoader to be fast, see #306 / CSafeLoader is the same as CLoader but with safe loading
# on Linux, make sure you install libyaml-dev or similar
# on Windows, get WHLs from pyyaml.org/pypi
logger.debug("using libyaml CLoader.")
return yaml.CLoader
logger.debug("using libyaml CSafeLoader.")
return yaml.CSafeLoader
except Exception:
logger.debug("unable to import libyaml CLoader, falling back to Python yaml parser.")
logger.debug("unable to import libyaml CSafeLoader, falling back to Python yaml parser.")
logger.debug("this will be slower to load rules.")
return yaml.Loader
return yaml.SafeLoader
@staticmethod
def _get_ruamel_yaml_parser():

View File

@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
__version__ = "9.2.1"
__version__ = "9.3.1"
def get_major_version():

View File

@@ -74,7 +74,8 @@ dependencies = [
# comments and context.
"pyyaml>=6",
"colorama>=0.4",
"ida-settings>=2.1.0,<3", # v3 has breaking changes
"ida-netnode>=3.0",
"ida-settings>=3.1.0",
"ruamel.yaml>=0.18",
"pefile>=2023.2.7",
"pyelftools>=0.31",
@@ -104,7 +105,7 @@ dependencies = [
"networkx>=3",
"dnfile>=0.15.0",
"dnfile>=0.17.0",
]
dynamic = ["version"]
@@ -121,14 +122,14 @@ dev = [
# we want all developer environments to be consistent.
# These dependencies are not used in production environments
# and should not conflict with other libraries/tooling.
"pre-commit==4.2.0",
"pre-commit==4.5.0",
"pytest==8.0.0",
"pytest-sugar==1.1.1",
"pytest-instafail==0.5.0",
"flake8==7.3.0",
"flake8-bugbear==24.12.12",
"flake8-bugbear==25.10.21",
"flake8-encodings==0.5.1",
"flake8-comprehensions==3.16.0",
"flake8-comprehensions==3.17.0",
"flake8-logging-format==0.9.0",
"flake8-no-implicit-concat==0.3.5",
"flake8-print==5.0.0",
@@ -136,8 +137,8 @@ dev = [
"flake8-simplify==0.22.0",
"flake8-use-pathlib==0.3.0",
"flake8-copyright==0.2.4",
"ruff==0.12.0",
"black==25.1.0",
"ruff==0.14.7",
"black==25.11.0",
"isort==6.0.0",
"mypy==1.17.1",
"mypy-protobuf==3.6.0",
@@ -149,7 +150,7 @@ dev = [
"types-PyYAML==6.0.8",
"types-psutil==7.0.0.20250218",
"types_requests==2.32.0.20240712",
"types-protobuf==6.30.2.20250516",
"types-protobuf==6.32.1.20250918",
"deptry==0.23.0"
]
build = [
@@ -157,16 +158,18 @@ build = [
# we want all developer environments to be consistent.
# These dependencies are not used in production environments
# and should not conflict with other libraries/tooling.
"pyinstaller==6.14.1",
"pyinstaller==6.16.0",
"setuptools==80.9.0",
"build==1.2.2"
"build==1.3.0"
]
scripts = [
# can (optionally) be more lenient on dependencies here
# see comment on dependencies for more context
"jschema_to_python==1.2.3",
"psutil==7.0.0",
"psutil==7.1.2",
"stix2==3.0.1",
"sarif_om==1.0.4",
"requests==2.32.3",
"requests>=2.32.4",
]
[tool.deptry]
@@ -198,7 +201,8 @@ known_first_party = [
"idc",
"java",
"netnode",
"PyQt5"
"PyQt5",
"PySide6"
]
[tool.deptry.per_rule_ignores]

View File

@@ -10,35 +10,35 @@ annotated-types==0.7.0
colorama==0.4.6
cxxfilt==0.3.0
dncil==1.0.2
dnfile==0.16.4
dnfile==0.17.0
funcy==2.0
humanize==4.13.0
humanize==4.14.0
ida-netnode==3.0
ida-settings==2.1.0
ida-settings==3.2.2
intervaltree==3.1.0
markdown-it-py==4.0.0
mdurl==0.1.2
msgpack==1.0.8
networkx==3.4.2
pefile==2024.8.26
pip==25.2
protobuf==6.31.1
pip==25.3
protobuf==6.33.1
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycparser==2.22
pydantic==2.11.4
pycparser==2.23
pydantic==2.12.4
# pydantic pins pydantic-core,
# but dependabot updates these separately (which is broken) and is annoying,
# so we rely on pydantic to pull in the right version of pydantic-core.
# pydantic-core==2.23.4
xmltodict==0.14.2
xmltodict==1.0.2
pyelftools==0.32
pygments==2.19.1
python-flirt==0.9.2
pyyaml==6.0.2
rich==14.2.0
ruamel-yaml==0.18.6
ruamel-yaml-clib==0.2.8
ruamel-yaml-clib==0.2.14
setuptools==80.9.0
six==1.17.0
sortedcontainers==2.4.0

2
rules

Submodule rules updated: 9e4cc28265...6120dfb6e0

View File

@@ -70,4 +70,4 @@ def test_standalone_binja_backend():
@pytest.mark.skipif(binja_present is False, reason="Skip binja tests if the binaryninja Python API is not installed")
def test_binja_version():
version = binaryninja.core_version_info()
assert version.major == 5 and version.minor == 1
assert version.major == 5 and version.minor == 2

View File

@@ -2272,10 +2272,11 @@
}
},
"node_modules/glob": {
"version": "10.4.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz",
"integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==",
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
@@ -2287,9 +2288,6 @@
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
@@ -2641,10 +2639,11 @@
}
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},

View File

@@ -212,6 +212,18 @@
<h2 class="mt-3">Tool Updates</h2>
<h3 class="mt-2">v9.3.1 (<em>2025-11-19</em>)</h3>
<p class="mt-0">
This patch release fixes a missing import for the capa explorer plugin for IDA Pro.
</p>
<h3 class="mt-2">v9.3.0 (<em>2025-11-12</em>)</h3>
<p class="mt-0">
capa v9.3.0 comes with over 20 new and/or impoved rules.
For IDA users the capa explorer plugin is now available via the IDA Pro plugin repository and contains Qt compatibility layer for PyQt5 and PySide6 support.
Additionally a Binary Ninja bug has been fixed. Released binaries now include ARM64 binaries (Linux and macOS).
</p>
<h3 class="mt-2">v9.2.1 (<em>2025-06-06</em>)</h3>
<p class="mt-0">
This point release fixes bugs including removing an unnecessary PyInstaller warning message and enabling the standalone binary to execute on systems running older versions of glibc.