Compare commits

..

34 Commits

Author SHA1 Message Date
Mike Hunhoff
5ad1dda918 v9.2.0 (#2677) 2025-06-05 09:03:56 -06:00
Capa Bot
eabb2cc809 Sync capa rules submodule 2025-06-02 19:16:57 +00:00
Capa Bot
a34c3ecc57 Sync capa rules submodule 2025-06-02 18:47:17 +00:00
dependabot[bot]
d22de5cf7f build(deps): bump rich from 13.9.2 to 14.0.0 (#2675)
Bumps [rich](https://github.com/Textualize/rich) from 13.9.2 to 14.0.0.
- [Release notes](https://github.com/Textualize/rich/releases)
- [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Textualize/rich/compare/v13.9.2...v14.0.0)

---
updated-dependencies:
- dependency-name: rich
  dependency-version: 14.0.0
  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-06-02 12:39:31 -06:00
dependabot[bot]
8f78834cae build(deps): bump setuptools from 78.1.1 to 80.9.0 (#2674)
Bumps [setuptools](https://github.com/pypa/setuptools) from 78.1.1 to 80.9.0.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v78.1.1...v80.9.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 80.9.0
  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-06-02 12:39:08 -06:00
dependabot[bot]
08dbb0e02d build(deps): bump flake8-simplify from 0.21.0 to 0.22.0 (#2673)
Bumps [flake8-simplify](https://github.com/MartinThoma/flake8-simplify) from 0.21.0 to 0.22.0.
- [Release notes](https://github.com/MartinThoma/flake8-simplify/releases)
- [Changelog](https://github.com/MartinThoma/flake8-simplify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/MartinThoma/flake8-simplify/commits)

---
updated-dependencies:
- dependency-name: flake8-simplify
  dependency-version: 0.22.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-06-02 12:38:41 -06:00
Mike Hunhoff
98725c52dc vmray: do not restrict analysis to PE and ELF files, e.g. docx (#2672)
* vmray: do not restrict analysis to PE and ELF files, e.g. docx

* update CHANGELOG
2025-05-30 13:53:12 -06:00
dependabot[bot]
eb87153064 build(deps): bump setuptools from 76.0.0 to 78.1.1 (#2667)
Bumps [setuptools](https://github.com/pypa/setuptools) from 76.0.0 to 78.1.1.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v76.0.0...v78.1.1)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 78.1.1
  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>
Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2025-05-30 12:35:35 -06:00
dependabot[bot]
56aa7176b0 build(deps): bump pip from 25.0 to 25.1.1 (#2666)
Bumps [pip](https://github.com/pypa/pip) from 25.0 to 25.1.1.
- [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/pip/compare/25.0...25.1.1)

---
updated-dependencies:
- dependency-name: pip
  dependency-version: 25.1.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>
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2025-05-30 12:35:15 -06:00
dependabot[bot]
8b41671409 build(deps): bump flake8 from 7.1.1 to 7.2.0 (#2648)
* build(deps): bump flake8 from 7.1.1 to 7.2.0

Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.1 to 7.2.0.
- [Commits](https://github.com/pycqa/flake8/compare/7.1.1...7.2.0)

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

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

* fix lints (#2671)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2025-05-30 12:09:16 -06:00
dependabot[bot]
5dbbc2b468 build(deps): bump pre-commit from 4.1.0 to 4.2.0 (#2646)
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.1.0 to 4.2.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.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: pre-commit
  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>
Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2025-05-30 11:28:19 -06:00
Mike Hunhoff
96d1eb64c3 update binja core version (#2670)
* update binja core version

* update CHANGELOG
2025-05-30 10:52:56 -06:00
Capa Bot
9234b33051 Sync capa rules submodule 2025-05-28 20:44:44 +00:00
Capa Bot
51f5114ad7 Sync capa rules submodule 2025-05-22 18:53:54 +00:00
Capa Bot
4b72f8a872 Sync capa-testfiles submodule 2025-05-22 17:48:58 +00:00
Moritz
8206a97b0f Merge pull request #2659 from mandiant/dependabot/npm_and_yarn/web/explorer/vite-6.3.4
build(deps-dev): bump vite from 6.2.3 to 6.3.4 in /web/explorer
2025-05-20 16:47:50 +02:00
Capa Bot
5a33b4b2a8 Sync capa rules submodule 2025-05-19 18:21:38 +00:00
Capa Bot
fcfdeec377 Sync capa rules submodule 2025-05-19 18:21:13 +00:00
Capa Bot
37a63a751c Sync capa-testfiles submodule 2025-05-19 18:12:00 +00:00
zdw@
3a9f2136bb lint: log the failed example+rule (#2661)
* lint: log the failed example+rule

* Update scripts/lint.py

Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>

* fix lint

---------

Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2025-05-12 15:11:22 -06:00
Capa Bot
390e2a6315 Sync capa-testfiles submodule 2025-05-12 16:17:27 +00:00
Capa Bot
6a43084915 Sync capa-testfiles submodule 2025-05-12 16:06:51 +00:00
dependabot[bot]
6d7ca57fa9 build(deps): bump pydantic from 2.10.1 to 2.11.4 (#2660)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.10.1 to 2.11.4.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.10.1...v2.11.4)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.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-05-12 09:48:46 -06:00
Ana María Martínez Gómez
d1090e8391 ci: Update Ubuntu version in actions (#2656)
ubuntu-20.04 has been deprecated causing several GH actions to fail:
https://github.com/actions/runner-images/issues/11101
2025-05-09 15:40:59 -06:00
dependabot[bot]
b07efe773b build(deps-dev): bump vite from 6.2.3 to 6.3.4 in /web/explorer
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.3 to 6.3.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.4/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-30 18:53:28 +00:00
Moritz
9d3d3be21d Merge pull request #2644 from mandiant/dependabot/npm_and_yarn/web/explorer/vite-6.2.3 2025-03-25 22:06:15 +01:00
dependabot[bot]
8251a4c16f build(deps-dev): bump vite from 6.2.2 to 6.2.3 in /web/explorer
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.2 to 6.2.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 15:30:15 +00:00
Mike Hunhoff
7407cb39ca add lint for registry control set regex that is not complete (#2643)
* add lint for registry control set regex that is not complete

* update CHANGELOG
2025-03-24 12:17:12 -06:00
Capa Bot
0162e447fd Sync capa rules submodule 2025-03-24 16:38:44 +00:00
Capa Bot
829dae388f Sync capa rules submodule 2025-03-21 16:15:53 +00:00
Capa Bot
2a4d0ae080 Sync capa rules submodule 2025-03-21 14:40:08 +00:00
Capa Bot
d9a754730c Sync capa rules submodule 2025-03-20 15:06:54 +00:00
Capa Bot
4acacba9d6 Sync capa rules submodule 2025-03-20 15:00:54 +00:00
Capa Bot
d00f172973 Sync capa rules submodule 2025-03-19 17:29:32 +00:00
21 changed files with 225 additions and 165 deletions

View File

@@ -22,12 +22,12 @@ jobs:
fail-fast: true
matrix:
include:
- os: ubuntu-20.04
- os: ubuntu-24.04
# use old linux so that the shared library versioning is more portable
artifact_name: capa
asset_name: linux
python_version: '3.10'
- os: ubuntu-20.04
- os: ubuntu-24.04
artifact_name: capa
asset_name: linux-py312
python_version: '3.12'
@@ -49,7 +49,7 @@ jobs:
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: ${{ matrix.python_version }}
- if: matrix.os == 'ubuntu-20.04'
- if: matrix.os == 'ubuntu-24.04'
run: sudo apt-get install -y libyaml-dev
- name: Upgrade pip, setuptools
run: python -m pip install --upgrade pip setuptools
@@ -82,10 +82,10 @@ jobs:
matrix:
include:
# OSs not already tested above
- os: ubuntu-22.04
- os: ubuntu-24.04
artifact_name: capa
asset_name: linux
- os: ubuntu-22.04
- os: ubuntu-24.04
artifact_name: capa
asset_name: linux-py312
- os: windows-2022

View File

@@ -26,7 +26,7 @@ env:
jobs:
changelog_format:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Checkout capa
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -37,7 +37,7 @@ jobs:
if [ $number != 1 ]; then exit 1; fi
code_style:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Checkout capa
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -64,7 +64,7 @@ jobs:
run: pre-commit run deptry --hook-stage manual
rule_linter:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Checkout capa with submodules
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -88,16 +88,16 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, windows-2019, macos-13]
os: [ubuntu-24.04, windows-2019, macos-13]
# across all operating systems
python-version: ["3.10", "3.11"]
include:
# on Ubuntu run these as well
- os: ubuntu-20.04
- os: ubuntu-24.04
python-version: "3.10"
- os: ubuntu-20.04
- os: ubuntu-24.04
python-version: "3.11"
- os: ubuntu-20.04
- os: ubuntu-24.04
python-version: "3.12"
steps:
- name: Checkout capa with submodules
@@ -109,7 +109,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install pyyaml
if: matrix.os == 'ubuntu-20.04'
if: matrix.os == 'ubuntu-24.04'
run: sudo apt-get install -y libyaml-dev
- name: Install capa
run: |
@@ -126,7 +126,7 @@ jobs:
name: Binary Ninja tests for ${{ matrix.python-version }}
env:
BN_SERIAL: ${{ secrets.BN_SERIAL }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs: [tests]
strategy:
fail-fast: false
@@ -168,7 +168,7 @@ jobs:
ghidra-tests:
name: Ghidra tests for ${{ matrix.python-version }}
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
needs: [tests]
strategy:
fail-fast: false

View File

@@ -6,16 +6,11 @@
### Breaking Changes
### New Rules (4)
### New Rules (0)
- communication/socket/connect-socket moritz.raabe@mandiant.com joakim@intezer.com mrhafizfarhad@gmail.com
- communication/socket/udp/connect-udp-socket mrhafizfarhad@gmail.com
- nursery/enter-debug-mode-in-dotnet @v1bh475u
-
### Bug Fixes
- cape: make some fields optional @williballenthin #2631 #2632
- lint: add WARN for regex features that contain unescaped dot #2635
### capa Explorer Web
@@ -24,8 +19,52 @@
### Development
### Raw diffs
- [capa v9.1.0...master](https://github.com/mandiant/capa/compare/v9.1.0...master)
- [capa-rules v9.1.0...master](https://github.com/mandiant/capa-rules/compare/v9.1.0...master)
- [capa v9.2.0...master](https://github.com/mandiant/capa/compare/v9.2.0...master)
- [capa-rules v9.2.0...master](https://github.com/mandiant/capa-rules/compare/v9.2.0...master)
## v9.2.0
This release improves a few aspects of dynamic analysis, including relaxing our validation on fields across many CAPE versions and processing additional VMRay submission file types, for example.
It also includes an updated rule pack containing new rules and rule fixes.
### New Features
- vmray: do not restrict analysis to PE and ELF files, e.g. docx @mike-hunhoff #2672
### Breaking Changes
### New Rules (22)
- communication/socket/connect-socket moritz.raabe@mandiant.com joakim@intezer.com mrhafizfarhad@gmail.com
- communication/socket/udp/connect-udp-socket mrhafizfarhad@gmail.com
- nursery/enter-debug-mode-in-dotnet @v1bh475u
- nursery/decrypt-data-using-tripledes-in-dotnet 0xRavenspar
- nursery/encrypt-data-using-tripledes-in-dotnet 0xRavenspar
- nursery/disable-system-features-via-registry-on-windows mehunhoff@google.com
- data-manipulation/encryption/chaskey/encrypt-data-using-chaskey still@teamt5.org
- data-manipulation/encryption/speck/encrypt-data-using-speck still@teamt5.org
- load-code/dotnet/load-assembly-via-iassembly still@teamt5.org
- malware-family/donut-loader/load-shellcode-via-donut still@teamt5.org
- nursery/disable-device-guard-features-via-registry-on-windows mehunhoff@google.com
- nursery/disable-firewall-features-via-registry-on-windows mehunhoff@google.com
- nursery/disable-system-restore-features-via-registry-on-windows mehunhoff@google.com
- nursery/disable-windows-defender-features-via-registry-on-windows mehunhoff@google.com
- host-interaction/file-system/write/clear-file-content jakeperalta7
- host-interaction/filter/unload-minifilter-driver JakePeralta7
- exploitation/enumeration/make-suspicious-ntquerysysteminformation-call zdw@google.com
- exploitation/gadgets/load-ntoskrnl zdw@google.com
- exploitation/gadgets/resolve-ntoskrnl-gadgets zdw@google.com
- exploitation/spraying/make-suspicious-ntfscontrolfile-call zdw@google.com
- anti-analysis/anti-forensic/unload-sysmon JakePeralta7
### Bug Fixes
- cape: make some fields optional @williballenthin #2631 #2632
- lint: add WARN for regex features that contain unescaped dot #2635
- lint: add ERROR for incomplete registry control set regex #2643
- binja: update unit test core version #2670
### Raw diffs
- [capa v9.1.0...v9.2.0](https://github.com/mandiant/capa/compare/v9.1.0...v9.2.0)
- [capa-rules v9.1.0...v9.2.0](https://github.com/mandiant/capa-rules/compare/v9.1.0...v9.2.0)
## v9.1.0

View File

@@ -21,9 +21,9 @@ import capa.features.extractors.cape.file
import capa.features.extractors.cape.thread
import capa.features.extractors.cape.global_
import capa.features.extractors.cape.process
from capa.exceptions import EmptyReportError
from capa.exceptions import EmptyReportError, UnsupportedFormatError
from capa.features.common import Feature
from capa.features.address import NO_ADDRESS, Address, AbsoluteVirtualAddress, _NoAddress
from capa.features.address import Address, AbsoluteVirtualAddress, _NoAddress
from capa.features.extractors.cape.models import Call, Static, Process, CapeReport
from capa.features.extractors.base_extractor import (
CallHandle,
@@ -53,14 +53,9 @@ class CapeExtractor(DynamicFeatureExtractor):
self.global_features = list(capa.features.extractors.cape.global_.extract_features(self.report))
def get_base_address(self) -> Union[AbsoluteVirtualAddress, _NoAddress, None]:
if self.report.static is None:
return NO_ADDRESS
if self.report.static.pe is None:
# TODO: handle ELF
return NO_ADDRESS
# value according to the PE header, the actual trace may use a different imagebase
assert self.report.static is not None
assert self.report.static.pe is not None
return AbsoluteVirtualAddress(self.report.static.pe.imagebase)
def extract_global_features(self) -> Iterator[tuple[Feature, Address]]:
@@ -125,10 +120,8 @@ class CapeExtractor(DynamicFeatureExtractor):
parts.append(" -> ")
if call.pretty_return:
parts.append(call.pretty_return)
elif call.return_:
parts.append(hex(call.return_))
else:
parts.append("?")
parts.append(hex(call.return_))
return "".join(parts)
@@ -139,11 +132,25 @@ class CapeExtractor(DynamicFeatureExtractor):
if cr.info.version not in TESTED_VERSIONS:
logger.warning("CAPE version '%s' not tested/supported yet", cr.info.version)
# TODO(mr-tz): support more file types
# https://github.com/mandiant/capa/issues/1933
if "PE" not in cr.target.file.type:
logger.error(
"capa currently only supports PE target files, this target file's type is: '%s'.\nPlease report this at: https://github.com/mandiant/capa/issues/1933",
cr.target.file.type,
)
# observed in 2.4-CAPE reports from capesandbox.com
if cr.static is None and cr.target.file.pe is not None:
cr.static = Static()
cr.static.pe = cr.target.file.pe
if cr.static is None:
raise UnsupportedFormatError("CAPE report missing static analysis")
if cr.static.pe is None:
raise UnsupportedFormatError("CAPE report missing PE analysis")
if len(cr.behavior.processes) == 0:
raise EmptyReportError("CAPE did not capture any processes")

View File

@@ -32,13 +32,7 @@ def get_processes(report: CapeReport) -> Iterator[ProcessHandle]:
"""
seen_processes = {}
for process in report.behavior.processes:
if process.parent_id is None:
# on CAPE for Linux, the root process may have no parent id, so we set that to 0
ppid = 0
else:
ppid = process.parent_id
addr = ProcessAddress(pid=process.process_id, ppid=ppid)
addr = ProcessAddress(pid=process.process_id, ppid=process.parent_id)
yield ProcessHandle(address=addr, inner=process)
# check for pid and ppid reuse
@@ -58,13 +52,7 @@ def extract_import_names(report: CapeReport) -> Iterator[tuple[Feature, Address]
"""
extract imported function names
"""
if report.static is None:
return
if report.static.pe is None:
# TODO: elf
return
assert report.static is not None and report.static.pe is not None
imports = report.static.pe.imports
if isinstance(imports, dict):
@@ -82,25 +70,13 @@ def extract_import_names(report: CapeReport) -> Iterator[tuple[Feature, Address]
def extract_export_names(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
if report.static is None:
return
if report.static.pe is None:
# TODO: elf
return
assert report.static is not None and report.static.pe is not None
for function in report.static.pe.exports:
yield Export(function.name), AbsoluteVirtualAddress(function.address)
def extract_section_names(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
if report.static is None:
return
if report.static.pe is None:
# TODO: elf
return
assert report.static is not None and report.static.pe is not None
for section in report.static.pe.sections:
yield Section(section.name), AbsoluteVirtualAddress(section.virtual_address)

View File

@@ -42,6 +42,9 @@ def extract_arch(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
yield Arch(ARCH_AMD64), NO_ADDRESS
else:
logger.warning("unrecognized Architecture: %s", report.target.file.type)
raise ValueError(
f"unrecognized Architecture from the CAPE report; output of file command: {report.target.file.type}"
)
def extract_format(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
@@ -51,6 +54,9 @@ def extract_format(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
yield Format(FORMAT_ELF), NO_ADDRESS
else:
logger.warning("unknown file format, file command output: %s", report.target.file.type)
raise ValueError(
f"unrecognized file format from the CAPE report; output of file command: {report.target.file.type}"
)
def extract_os(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
@@ -74,10 +80,7 @@ def extract_os(report: CapeReport) -> Iterator[tuple[Feature, Address]]:
else:
# if the operating system information is missing from the cape report, it's likely a bug
logger.warning("unrecognized OS: %s", file_output)
elif report.info.machine and report.info.machine.platform == "windows":
yield OS(OS_WINDOWS), NO_ADDRESS
elif report.info.machine and report.info.machine.platform == "linux":
yield OS(OS_LINUX), NO_ADDRESS
raise ValueError(f"unrecognized OS from the CAPE report; output of file command: {file_output}")
else:
# the sample is shellcode
logger.debug("unsupported file format, file command output: %s", file_output)

View File

@@ -29,26 +29,8 @@ def validate_hex_bytes(value):
return bytes.fromhex(value) if isinstance(value, str) else value
def validate_status_code(value):
if isinstance(value, str):
if value == "?":
# TODO: check for this in the return handling
return None
# like: -1 EINVAL (Invalid argument)
# like: 0 (Timeout)
# like: 0x8002 (flags O_RDWR|O_LARGEFILE)
assert value.endswith(")")
num = value.partition(" ")[0]
return int(num, 16) if num.startswith("0x") else int(num, 10)
else:
return value
HexInt = Annotated[int, BeforeValidator(validate_hex_int)]
HexBytes = Annotated[bytes, BeforeValidator(validate_hex_bytes)]
# this is a status code, such as returned by CAPE for Linux, like: "0 (Timeout)" or "0x8002 (flags O_RDWR|O_LARGEFILE)
StatusCode = Annotated[int | None, BeforeValidator(validate_status_code)]
# a model that *cannot* have extra fields
@@ -89,13 +71,8 @@ Emptydict: TypeAlias = BaseModel
EmptyList: TypeAlias = list[Any]
class Machine(FlexibleModel):
platform: Optional[str] = None
class Info(FlexibleModel):
version: str
machine: Optional[Machine] = None
class ImportedSymbol(FlexibleModel):
@@ -310,38 +287,16 @@ class Argument(FlexibleModel):
pretty_value: Optional[str] = None
def validate_argument(value):
if isinstance(value, str):
# for a few calls on CAPE for Linux, we see arguments like in this call:
#
# timestamp: "18:12:17.199276"
# category: "misc"
# api: "uname"
# return: "0"
# ▽ arguments:
# [0]: "{sysname=\"Linux\", nodename=\"laptop\", ...}"
#
# which is just a string with a JSON-like thing inside,
# that we want to map a default unnamed argument.
return Argument(name="", value=value)
else:
return value
# mypy isn't happy about assigning to type
Argument = Annotated[Argument, BeforeValidator(validate_argument)] # type: ignore
class Call(FlexibleModel):
# timestamp: str
thread_id: int | None = None
thread_id: int
# category: str
api: str
arguments: list[Argument]
# status: bool
return_: HexInt | StatusCode = Field(alias="return")
return_: HexInt = Field(alias="return")
pretty_return: Optional[str] = None
# repeated: int
@@ -360,12 +315,12 @@ class Call(FlexibleModel):
class Process(FlexibleModel):
process_id: int
process_name: str
parent_id: int | None
parent_id: int
# module_path: str
# first_seen: str
calls: list[Call]
threads: list[int] | None = None # this can be None for CAPE for Linux, which doesn't track threads.
environ: dict[str, str] = Field(default_factory=dict) # type: ignore
threads: list[int]
environ: dict[str, str]
"""

View File

@@ -29,13 +29,6 @@ def get_threads(ph: ProcessHandle) -> Iterator[ThreadHandle]:
get the threads associated with a given process
"""
process: Process = ph.inner
if not process.threads:
# CAPE for linux doesn't record threads
# so we return a default 0 value
yield ThreadHandle(address=ThreadAddress(process=ph.address, tid=0), inner={})
return
threads: list[int] = process.threads
for thread in threads:
@@ -49,9 +42,6 @@ def extract_environ_strings(ph: ProcessHandle) -> Iterator[tuple[Feature, Addres
"""
process: Process = ph.inner
if not process.environ:
return
for value in (value for value in process.environ.values() if value):
yield String(value), ph.address

View File

@@ -29,16 +29,8 @@ def get_calls(ph: ProcessHandle, th: ThreadHandle) -> Iterator[CallHandle]:
tid = th.address.tid
for call_index, call in enumerate(process.calls):
if call.thread_id is None:
# CAPE for linux doesn't record threads
# so this must be the 0 value
# and we'll enumerate all the calls in this process
assert tid == 0
else:
if call.thread_id != tid:
continue
if call.thread_id != tid:
continue
for symbol in generate_symbols("", call.api):
call.api = symbol

View File

@@ -96,14 +96,7 @@ class VMRayAnalysis:
% (self.submission_name, self.submission_type)
)
if self.submission_static is not None:
if self.submission_static.pe is None and self.submission_static.elf is None:
# we only support static analysis for PE and ELF files for now
raise UnsupportedFormatError(
"archive does not contain a supported file format (submission_name: %s, submission_type: %s)"
% (self.submission_name, self.submission_type)
)
else:
if self.submission_static is None:
# VMRay may not record static analysis for certain file types, e.g. MSI, but we'd still like to match dynamic
# execution so we continue without and accept that the results may be incomplete
logger.warning(

View File

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

View File

@@ -121,11 +121,11 @@ 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.1.0",
"pre-commit==4.2.0",
"pytest==8.0.0",
"pytest-sugar==1.0.0",
"pytest-instafail==0.5.0",
"flake8==7.1.1",
"flake8==7.2.0",
"flake8-bugbear==24.12.12",
"flake8-encodings==0.5.1",
"flake8-comprehensions==3.16.0",
@@ -133,7 +133,7 @@ dev = [
"flake8-no-implicit-concat==0.3.5",
"flake8-print==5.0.0",
"flake8-todos==0.3.1",
"flake8-simplify==0.21.0",
"flake8-simplify==0.22.0",
"flake8-use-pathlib==0.3.0",
"flake8-copyright==0.2.4",
"ruff==0.11.0",
@@ -157,7 +157,7 @@ build = [
# These dependencies are not used in production environments
# and should not conflict with other libraries/tooling.
"pyinstaller==6.12.0",
"setuptools==76.0.0",
"setuptools==80.9.0",
"build==1.2.2"
]
scripts = [

View File

@@ -21,12 +21,12 @@ mdurl==0.1.2
msgpack==1.0.8
networkx==3.4.2
pefile==2024.8.26
pip==25.0
pip==25.1.1
protobuf==6.30.1
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycparser==2.22
pydantic==2.10.1
pydantic==2.11.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.
@@ -36,10 +36,10 @@ pyelftools==0.32
pygments==2.19.1
python-flirt==0.9.2
pyyaml==6.0.2
rich==13.9.2
rich==14.0.0
ruamel-yaml==0.18.6
ruamel-yaml-clib==0.2.8
setuptools==76.0.0
setuptools==80.9.0
six==1.17.0
sortedcontainers==2.4.0
viv-utils==0.8.0

2
rules

Submodule rules updated: e85887a875...edabdffa8c

View File

@@ -175,8 +175,6 @@ def convert_rule(rule, rulename, cround, depth):
depth += 1
logger.info("recursion depth: %d", depth)
global var_names
def do_statement(s_type, kid):
yara_strings = ""
yara_condition = ""

View File

@@ -406,6 +406,7 @@ class DoesntMatchExample(Lint):
return True
if rule.name not in capabilities:
logger.info('rule "%s" does not match for sample %s', rule.name, example_id)
return True
@@ -721,6 +722,29 @@ class FeatureStringTooShort(Lint):
return False
class FeatureRegexRegistryControlSetMatchIncomplete(Lint):
name = "feature regex registry control set match incomplete"
recommendation = (
'use "(ControlSet\\d{3}|CurrentControlSet)" to match both indirect references '
+ 'via "CurrentControlSet" and direct references via "ControlSetXXX"'
)
def check_features(self, ctx: Context, features: list[Feature]):
for feature in features:
if not isinstance(feature, (Regex,)):
continue
assert isinstance(feature.value, str)
pat = feature.value.lower()
if "system\\\\" in pat and "controlset" in pat or "currentcontrolset" in pat:
if "system\\\\(controlset\\d{3}|currentcontrolset)" not in pat:
return True
return False
class FeatureRegexContainsUnescapedPeriod(Lint):
name = "feature regex contains unescaped period"
recommendation_template = 'escape the period in "{:s}" unless it should be treated as a regex dot operator'
@@ -983,6 +1007,7 @@ FEATURE_LINTS = (
FeatureNegativeNumber(),
FeatureNtdllNtoskrnlApi(),
FeatureRegexContainsUnescapedPeriod(),
FeatureRegexRegistryControlSetMatchIncomplete(),
)

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 == 4 and version.minor == 2
assert version.major == 5 and version.minor == 0

View File

@@ -27,7 +27,7 @@
"eslint-plugin-vue": "^9.23.0",
"jsdom": "^24.1.0",
"prettier": "^3.2.5",
"vite": "^6.2.2",
"vite": "^6.3.4",
"vite-plugin-singlefile": "^2.2.0",
"vitest": "^3.0.9"
}
@@ -3426,6 +3426,51 @@
"dev": true,
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tinypool": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
@@ -3561,15 +3606,18 @@
"dev": true
},
"node_modules/vite": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
"integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz",
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
"picomatch": "^4.0.2",
"postcss": "^8.5.3",
"rollup": "^4.30.1"
"rollup": "^4.34.9",
"tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"
@@ -3672,6 +3720,34 @@
"vite": "^5.4.11 || ^6.0.0"
}
},
"node_modules/vite/node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vitest": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz",

View File

@@ -33,7 +33,7 @@
"eslint-plugin-vue": "^9.23.0",
"jsdom": "^24.1.0",
"prettier": "^3.2.5",
"vite": "^6.2.2",
"vite": "^6.3.4",
"vite-plugin-singlefile": "^2.2.0",
"vitest": "^3.0.9"
}

View File

@@ -239,6 +239,12 @@
<h2 class="mt-3">Tool Updates</h2>
<h3 class="mt-2">v9.2.0 (<em>2025-06-03</em>)</h3>
<p class="mt-0">
This release improves a few aspects of dynamic analysis, including relaxing our validation on fields across many CAPE versions and processing additional VMRay submission file types, for example.
It also includes an updated rule pack containing new rules and rule fixes.
</p>
<h3 class="mt-2">v9.1.0 (<em>2025-03-02</em>)</h3>
<p class="mt-0">
This release improves a few aspects of dynamic analysis, relaxing our validation on fields across many CAPE versions, for example.