Compare commits

...

267 Commits

Author SHA1 Message Date
Willi Ballenthin
c0851fc643 Merge pull request #863 from mandiant/v3.1.0
version: v3.1.0
2022-01-12 14:18:22 -07:00
Willi Ballenthin
de7592b351 changelog: add additional contributor 2022-01-11 14:29:15 -07:00
Willi Ballenthin
5530bbad53 Update CHANGELOG.md
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2022-01-11 14:28:17 -07:00
Willi Ballenthin
4f0067e408 Update CHANGELOG.md
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2022-01-11 14:27:59 -07:00
Willi Ballenthin
b444c28a19 changelog: fix format 2022-01-11 10:05:40 -07:00
Willi Ballenthin
a4cc409c95 Update capa/version.py
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2022-01-10 12:39:07 -07:00
Moritz
fcb08501c0 Merge pull request #865 from mandiant/mr-tz-patch-1
Update global_.py
2022-01-10 19:21:24 +01:00
Moritz
cb2d00cefc Update global_.py 2022-01-10 19:04:52 +01:00
Willi Ballenthin
1cb9fc8a40 Merge pull request #864 from doomedraven/patch-1
Fix deprication warning from IDA
2022-01-10 10:52:10 -07:00
doomedraven
85cfc04bdb Fix deprication warning from IDA
```
    if info.procName == "metapc" and info.is_64bit():
```
Please use "procname" instead of "procName" ("procName" is kept for backward-compatibility, and will be removed soon.)
2022-01-10 18:37:59 +01:00
Willi Ballenthin
6555a3604f changelog: intro section 2022-01-10 09:49:00 -07:00
Willi Ballenthin
a97262d022 changelog: v3.1.0 2022-01-10 09:39:46 -07:00
Willi Ballenthin
8ad54271e9 version: v3.1.0 2022-01-10 09:33:39 -07:00
Willi Ballenthin
e5b9a20d09 changelog: add rule changes and contributors 2022-01-10 09:32:49 -07:00
Willi Ballenthin
0d37d182ea changelog: add some additional entries 2022-01-10 09:26:14 -07:00
Willi Ballenthin
6690634a3f Merge pull request #858 from mandiant/dependabot/pip/types-pyyaml-6.0.3
build(deps-dev): bump types-pyyaml from 6.0.1 to 6.0.3
2022-01-10 08:26:25 -07:00
dependabot[bot]
8f3730bae3 build(deps-dev): bump types-pyyaml from 6.0.1 to 6.0.3
Bumps [types-pyyaml](https://github.com/python/typeshed) from 6.0.1 to 6.0.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-pyyaml
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 15:25:42 +00:00
Willi Ballenthin
8f4e726774 Merge pull request #859 from mandiant/dependabot/pip/types-tabulate-0.8.5
build(deps-dev): bump types-tabulate from 0.8.4 to 0.8.5
2022-01-10 08:25:12 -07:00
Willi Ballenthin
5b8eda0f08 Merge pull request #861 from mandiant/dependabot/pip/mypy-0.931
build(deps-dev): bump mypy from 0.930 to 0.931
2022-01-10 08:24:59 -07:00
Willi Ballenthin
f5f62bbd71 Merge pull request #862 from mandiant/dependabot/pip/types-psutil-5.8.19
build(deps-dev): bump types-psutil from 5.8.17 to 5.8.19
2022-01-10 08:24:41 -07:00
dependabot[bot]
24c3edc7ec build(deps-dev): bump types-psutil from 5.8.17 to 5.8.19
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.17 to 5.8.19.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 14:18:21 +00:00
dependabot[bot]
0e3d46ef5e build(deps-dev): bump mypy from 0.930 to 0.931
Bumps [mypy](https://github.com/python/mypy) from 0.930 to 0.931.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.930...v0.931)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 14:18:19 +00:00
dependabot[bot]
a3546b65f7 build(deps-dev): bump types-tabulate from 0.8.4 to 0.8.5
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.4 to 0.8.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 14:18:09 +00:00
Willi Ballenthin
01b694b6ab Merge pull request #851 from kn0wl3dge/fix/430
smda: fix negative number extraction
2022-01-03 12:08:41 -07:00
Moritz
3598f83091 Merge pull request #856 from mandiant/dependabot/pip/psutil-5.9.0
build(deps-dev): bump psutil from 5.8.0 to 5.9.0
2022-01-03 17:33:56 +01:00
Moritz
2085dd7b02 Merge pull request #853 from mandiant/dependabot/pip/ruamel-yaml-0.17.20
build(deps): bump ruamel-yaml from 0.17.19 to 0.17.20
2022-01-03 17:33:40 +01:00
Moritz
65d916332d Merge pull request #855 from mandiant/dependabot/pip/types-psutil-5.8.17
build(deps-dev): bump types-psutil from 5.8.16 to 5.8.17
2022-01-03 17:33:26 +01:00
Moritz
1937efce88 Merge pull request #852 from mandiant/dependabot/pip/types-tabulate-0.8.4
build(deps-dev): bump types-tabulate from 0.8.3 to 0.8.4
2022-01-03 17:33:19 +01:00
Moritz
501d607b3a Merge pull request #854 from mandiant/dependabot/pip/types-colorama-0.4.5
build(deps-dev): bump types-colorama from 0.4.4 to 0.4.5
2022-01-03 17:33:07 +01:00
dependabot[bot]
7d6670c59e build(deps-dev): bump psutil from 5.8.0 to 5.9.0
Bumps [psutil](https://github.com/giampaolo/psutil) from 5.8.0 to 5.9.0.
- [Release notes](https://github.com/giampaolo/psutil/releases)
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-5.8.0...release-5.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 14:11:03 +00:00
dependabot[bot]
fe608db16a build(deps-dev): bump types-psutil from 5.8.16 to 5.8.17
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.16 to 5.8.17.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 14:10:58 +00:00
dependabot[bot]
be1f313d57 build(deps-dev): bump types-colorama from 0.4.4 to 0.4.5
Bumps [types-colorama](https://github.com/python/typeshed) from 0.4.4 to 0.4.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-colorama
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 14:10:55 +00:00
dependabot[bot]
cb77c55d2c build(deps): bump ruamel-yaml from 0.17.19 to 0.17.20
Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.19 to 0.17.20.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 14:10:53 +00:00
dependabot[bot]
417aa35c60 build(deps-dev): bump types-tabulate from 0.8.3 to 0.8.4
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.3 to 0.8.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 14:10:46 +00:00
Baptistin Boilot
18877eb676 changelog: add fixed issue 2021-12-31 21:14:56 +01:00
Baptistin Boilot
a9670c9510 smda: fix number extractor to return only unsigned values
SmdaInstruction operands are python `str` objects. SMDA number operands are signed integers.
This commit adds a converter to the SMDA number extractor.
The goal is to convert any signed number to the two’s complement representation with the correct bitness.
2021-12-31 20:10:36 +01:00
Baptistin Boilot
8474369575 tests: add fixtures for two's complement numbers
Add fixtures to validate the following number features:
- number(0x0): to check feature extraction for null number
- number(0xFFFFFFFF): to check feature extraction for -1 number
- number(0xFFFFFFF0): to check feature extraction for negative number (-0x10 in this case)
2021-12-31 20:08:56 +01:00
Baptistin Boilot
4739d121a2 scripts: add backend parameter (-b) to show-features.py 2021-12-31 20:07:34 +01:00
Mike Hunhoff
e47f5a2548 Merge pull request #849 from mandiant/fix/845
capa explorer: updating supported IDA versions
2021-12-31 10:48:53 -07:00
Willi Ballenthin
51f5628383 Merge pull request #847 from mandiant/dependabot/pip/ruamel-yaml-0.17.19
build(deps): bump ruamel-yaml from 0.17.17 to 0.17.19
2021-12-29 09:44:24 -07:00
Willi Ballenthin
aa67a1b285 Merge pull request #846 from mandiant/dependabot/pip/types-psutil-5.8.16
build(deps-dev): bump types-psutil from 5.8.15 to 5.8.16
2021-12-29 09:44:15 -07:00
Willi Ballenthin
d22e51fd84 Merge pull request #848 from mandiant/dependabot/pip/mypy-0.930
build(deps-dev): bump mypy from 0.920 to 0.930
2021-12-29 09:42:21 -07:00
Michael Hunhoff
cde4af40fe capa explorer: updating supported IDA versions 2021-12-28 10:51:53 -07:00
dependabot[bot]
a147755d13 build(deps-dev): bump mypy from 0.920 to 0.930
Bumps [mypy](https://github.com/python/mypy) from 0.920 to 0.930.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.920...v0.930)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 14:12:16 +00:00
dependabot[bot]
7b6c293069 build(deps): bump ruamel-yaml from 0.17.17 to 0.17.19
Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.17 to 0.17.19.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 14:12:12 +00:00
dependabot[bot]
b3f1244641 build(deps-dev): bump types-psutil from 5.8.15 to 5.8.16
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.15 to 5.8.16.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 14:12:06 +00:00
Capa Bot
e6423700b9 Sync capa rules submodule 2021-12-23 16:34:46 +00:00
Moritz
9462a26a05 Merge pull request #844 from mandiant/dependabot/pip/mypy-0.920
build(deps-dev): bump mypy from 0.910 to 0.920
2021-12-20 16:31:41 +01:00
dependabot[bot]
c059a52d0e build(deps-dev): bump mypy from 0.910 to 0.920
Bumps [mypy](https://github.com/python/mypy) from 0.910 to 0.920.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.910...v0.920)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 14:09:06 +00:00
Capa Bot
a221db8a59 Sync capa rules submodule 2021-12-20 12:48:22 +00:00
Moritz
df43ed0219 Merge pull request #842 from mandiant/fix/maec-mal-fam
support maec/malware-family meta
2021-12-20 13:15:50 +01:00
Capa Bot
90430f52c6 Sync capa-testfiles submodule 2021-12-15 15:33:39 +00:00
Moritz Raabe
4e7f0b4591 support maec/malware-family meta 2021-12-15 10:40:34 +01:00
Capa Bot
bda76c22ec Sync capa rules submodule 2021-12-14 21:52:49 +00:00
Capa Bot
d67223c321 Sync capa rules submodule 2021-12-14 21:46:38 +00:00
Capa Bot
21278ff595 Sync capa rules submodule 2021-12-14 21:45:58 +00:00
Capa Bot
21fd6b27e2 Sync capa rules submodule 2021-12-13 18:48:16 +00:00
Capa Bot
cc8d57b242 Sync capa-testfiles submodule 2021-12-13 17:24:52 +00:00
Capa Bot
6081f4573c Sync capa-testfiles submodule 2021-12-13 17:24:32 +00:00
Capa Bot
ea2cafa715 Sync capa-testfiles submodule 2021-12-13 17:24:02 +00:00
Capa Bot
a34c993e31 Sync capa rules submodule 2021-12-07 04:32:49 +00:00
Willi Ballenthin
1a5fc3a21a Merge pull request #839 from cl3o/master
types: Add assert_never for exhaustivenes checking with mypy
2021-12-06 13:55:41 -07:00
cl3o
c15a9a72f5 Add local variable for easy_rules_by_feature at the beginning of match 2021-12-06 20:55:15 +01:00
cl3o
5b35058338 Forgot to add the second fix to the first commit. 2021-12-06 20:32:44 +01:00
cl3o
a0ca6e18c8 Made proposed changes to fix mypy errors 2021-12-06 20:30:07 +01:00
Capa Bot
1917004292 Sync capa rules submodule 2021-12-06 19:22:59 +00:00
Capa Bot
8ee3bb08bc Sync capa rules submodule 2021-12-06 18:24:54 +00:00
Capa Bot
7e96059fb5 Sync capa rules submodule 2021-12-06 17:58:59 +00:00
Capa Bot
4f7f06d316 Sync capa rules submodule 2021-12-06 17:57:11 +00:00
Capa Bot
448b5392be Sync capa rules submodule 2021-12-06 17:56:26 +00:00
Willi Ballenthin
6f5f3e091a Merge pull request #840 from mandiant/dependabot/pip/black-21.12b0
build(deps-dev): bump black from 21.11b1 to 21.12b0
2021-12-06 10:45:51 -07:00
dependabot[bot]
fa6a2069ce build(deps-dev): bump black from 21.11b1 to 21.12b0
Bumps [black](https://github.com/psf/black) from 21.11b1 to 21.12b0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 14:12:23 +00:00
Capa Bot
09fd371b9d Sync capa-testfiles submodule 2021-12-06 10:13:41 +00:00
Capa Bot
a598745938 Sync capa-testfiles submodule 2021-12-06 10:06:57 +00:00
Capa Bot
7751f693c8 Sync capa-testfiles submodule 2021-12-06 10:02:45 +00:00
Capa Bot
7ade9ca43e Sync capa-testfiles submodule 2021-12-06 10:01:17 +00:00
cl3o
061a66e437 create function assert_never 2021-12-04 19:02:54 +01:00
Capa Bot
39536e2727 Sync capa rules submodule 2021-12-03 15:29:51 +00:00
Capa Bot
38038626d4 Sync capa rules submodule 2021-12-03 15:29:28 +00:00
Capa Bot
c3d34abe89 Sync capa-testfiles submodule 2021-12-03 12:12:30 +00:00
Capa Bot
baf5005998 Sync capa-testfiles submodule 2021-12-03 12:12:20 +00:00
Capa Bot
107c3c0cf9 Sync capa rules submodule 2021-11-30 22:06:21 +00:00
Capa Bot
2d1bd37816 Sync capa rules submodule 2021-11-30 15:24:28 +00:00
Capa Bot
de017b15d0 Sync capa-testfiles submodule 2021-11-30 15:24:09 +00:00
Capa Bot
3b0974ae3e Sync capa rules submodule 2021-11-29 23:46:52 +00:00
Willi Ballenthin
cf6cbc16df Merge pull request #838 from mandiant/dependabot/pip/types-psutil-5.8.15
build(deps-dev): bump types-psutil from 5.8.14 to 5.8.15
2021-11-29 08:47:44 -07:00
dependabot[bot]
bd60a8d9cd build(deps-dev): bump types-psutil from 5.8.14 to 5.8.15
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.14 to 5.8.15.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 14:09:09 +00:00
Capa Bot
c77240c6b4 Sync capa rules submodule 2021-11-26 16:21:34 +00:00
Moritz
14d803c604 Merge pull request #837 from mandiant/dependabot/pip/black-21.11b1
build(deps-dev): bump black from 21.10b0 to 21.11b1
2021-11-22 18:45:02 +01:00
dependabot[bot]
f764829ca9 build(deps-dev): bump black from 21.10b0 to 21.11b1
Bumps [black](https://github.com/psf/black) from 21.10b0 to 21.11b1.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 14:10:19 +00:00
Willi Ballenthin
418eedd7bd freeze: fix doc describing format 2021-11-17 12:06:56 -07:00
Willi Ballenthin
b9f1fe56c8 Merge pull request #834 from mandiant/williballenthin-patch-1
setup: bump viv-utils to v0.6.9
2021-11-16 11:21:30 -07:00
Willi Ballenthin
7e50a957ff ci: tests: python versions are strings not floats 2021-11-16 10:12:34 -07:00
Willi Ballenthin
137cff6127 ci: tests: test under py3.10 too 2021-11-16 10:06:32 -07:00
Willi Ballenthin
807b99e5e5 changelog 2021-11-15 14:12:07 -07:00
Willi Ballenthin
e21c69f4e3 setup: bump viv-utils to v0.6.9
closes #816 
closes #683
2021-11-15 14:10:48 -07:00
Moritz
9f7daca86e Merge pull request #833 from mandiant/dependabot/pip/types-pyyaml-6.0.1
build(deps-dev): bump types-pyyaml from 6.0.0 to 6.0.1
2021-11-15 16:54:11 +01:00
Moritz
1b89e274c9 Merge pull request #832 from mandiant/dependabot/pip/isort-5.10.1
build(deps-dev): bump isort from 5.10.0 to 5.10.1
2021-11-15 16:54:02 +01:00
Moritz
dd768dc080 Merge pull request #831 from mandiant/dependabot/pip/viv-utils-flirt--0.6.8
build(deps): bump viv-utils[flirt] from 0.6.7 to 0.6.8
2021-11-15 16:53:53 +01:00
dependabot[bot]
4aea481967 build(deps-dev): bump types-pyyaml from 6.0.0 to 6.0.1
Bumps [types-pyyaml](https://github.com/python/typeshed) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-pyyaml
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 14:12:07 +00:00
dependabot[bot]
265629d127 build(deps-dev): bump isort from 5.10.0 to 5.10.1
Bumps [isort](https://github.com/pycqa/isort) from 5.10.0 to 5.10.1.
- [Release notes](https://github.com/pycqa/isort/releases)
- [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pycqa/isort/compare/5.10.0...5.10.1)

---
updated-dependencies:
- dependency-name: isort
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 14:12:04 +00:00
dependabot[bot]
cef0cb809f build(deps): bump viv-utils[flirt] from 0.6.7 to 0.6.8
Bumps [viv-utils[flirt]](https://github.com/williballenthin/viv-utils) from 0.6.7 to 0.6.8.
- [Release notes](https://github.com/williballenthin/viv-utils/releases)
- [Commits](https://github.com/williballenthin/viv-utils/compare/v0.6.7...v0.6.8)

---
updated-dependencies:
- dependency-name: viv-utils[flirt]
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 14:11:59 +00:00
Willi Ballenthin
57fe1e27b6 Merge pull request #830 from mandiant/perf/rule-selection
perf: don't try to match rules that will never match
2021-11-12 11:54:29 -07:00
Willi Ballenthin
83253eb7d0 rules: better variable name 2021-11-12 11:53:03 -07:00
Willi Ballenthin
9b5e8ff45d Update capa/rules.py
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2021-11-12 11:51:39 -07:00
William Ballenthin
cdfacc6247 Merge branch 'master' of github.com:fireeye/capa into perf/rule-selection 2021-11-10 14:30:08 -07:00
Capa Bot
10d747cc8c Sync capa rules submodule 2021-11-10 21:29:25 +00:00
William Ballenthin
a6b366602c mypy 2021-11-10 14:21:28 -07:00
William Ballenthin
80fb9dec3c pep8 2021-11-10 14:15:52 -07:00
William Ballenthin
68c86cf620 rules: easy/hard: better detect edge cases in optional, some, and range 2021-11-10 14:13:57 -07:00
William Ballenthin
e550d48bcd linter: optional maps to some, not range 2021-11-10 14:13:37 -07:00
William Ballenthin
1aaaa8919c rules: easy/hard: simplify indexing by considering not: hard 2021-11-10 13:55:34 -07:00
William Ballenthin
72c2ffc40b linter: add checks for not and optional not under and 2021-11-10 13:47:30 -07:00
William Ballenthin
f7ab2fb13a rules: easy/hard rules: detect not/optional at the root 2021-11-10 13:36:10 -07:00
William Ballenthin
3a1272246f rules: code consistency 2021-11-10 13:36:00 -07:00
William Ballenthin
6039a33bf8 engine: remove old import 2021-11-10 12:56:40 -07:00
William Ballenthin
2d68fb2536 pep8 2021-11-10 12:51:27 -07:00
William Ballenthin
845df282ef tests: split out match tests and validate alternative algorithms 2021-11-10 12:44:58 -07:00
William Ballenthin
1406dc28d9 rules: ruleset: fix collection of features under not statements 2021-11-10 12:44:19 -07:00
William Ballenthin
67884dd255 rules: match: more documentation 2021-11-09 16:42:32 -07:00
William Ballenthin
2bf05ac631 rules: index easy/hard: better handle not: statements 2021-11-09 16:37:30 -07:00
William Ballenthin
8cb04e4737 Merge branch 'master' into perf/rule-selection 2021-11-09 16:28:03 -07:00
William Ballenthin
733126591e Merge branch 'perf/query-optimizer' 2021-11-09 16:27:09 -07:00
William Ballenthin
d4d801c246 optimizer: tweak costs slightly 2021-11-09 16:26:26 -07:00
Willi Ballenthin
84ba32a8fe Merge pull request #829 from mandiant/perf/query-optimizer
perf: add query optimizer
2021-11-09 16:25:22 -07:00
William Ballenthin
ea386d02b6 tests: add test demonstrating optimizer 2021-11-09 16:24:26 -07:00
William Ballenthin
77cac63443 Merge branch 'master' into perf/query-optimizer 2021-11-09 16:12:30 -07:00
Willi Ballenthin
9350ee9479 Merge pull request #827 from mandiant/perf/short-circuit
perf: short circuit logic nodes when appropriate
2021-11-09 16:10:20 -07:00
Willi Ballenthin
025d156068 Merge pull request #828 from mandiant/profiling
profile infrastructure
2021-11-09 16:09:34 -07:00
William Ballenthin
7a4aee592b profile-time: add doc 2021-11-09 16:08:39 -07:00
Willi Ballenthin
f427c5e961 Update capa/engine.py
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2021-11-09 10:49:10 -07:00
Willi Ballenthin
51af2d4a56 Update capa/engine.py
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2021-11-09 10:49:01 -07:00
Willi Ballenthin
a68812b223 Update capa/engine.py
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
2021-11-09 10:48:54 -07:00
William Ballenthin
e05f8c7034 changelog 2021-11-09 10:27:33 -07:00
William Ballenthin
182377581a main: use ruleset.match instead of engine.mathc 2021-11-09 09:52:45 -07:00
William Ballenthin
e647ae2ac4 rules: ruleset: add optimized match routine 2021-11-09 09:52:32 -07:00
William Ballenthin
1311da99ff rules: make Scope an enum 2021-11-09 09:51:50 -07:00
William Ballenthin
8badf226a2 engine: document match routine 2021-11-09 09:51:18 -07:00
William Ballenthin
6909d6a541 changelog 2021-11-08 16:04:15 -07:00
William Ballenthin
e287dc9a32 optimizer: fix sort order 2021-11-08 15:54:14 -07:00
William Ballenthin
152d0f3244 ruleset: add query optimizer 2021-11-08 15:34:59 -07:00
William Ballenthin
a6e2cfc90a Merge branch 'profiling' into perf/short-circuit 2021-11-08 15:24:50 -07:00
William Ballenthin
18c30e4f12 main: remove perf debug msgs 2021-11-08 15:24:43 -07:00
William Ballenthin
3c4f4d302c Merge branch 'profiling' into perf/short-circuit 2021-11-08 15:23:23 -07:00
William Ballenthin
2abebfbce7 main: remove perf messages 2021-11-08 15:22:58 -07:00
William Ballenthin
0b517c51d8 main: remove perf messages 2021-11-08 15:22:01 -07:00
William Ballenthin
9fbbda11b8 Merge branch 'profiling' into perf/short-circuit 2021-11-08 15:20:22 -07:00
William Ballenthin
6f6831f812 perf: document that counters is unstable 2021-11-08 15:20:11 -07:00
William Ballenthin
d425bb31c4 Merge branch 'profiling' into perf/short-circuit 2021-11-08 15:16:22 -07:00
William Ballenthin
334425a08f changelog 2021-11-08 15:16:08 -07:00
William Ballenthin
3e74da96a6 engine: make short circuiting configurable 2021-11-08 14:55:11 -07:00
William Ballenthin
ad119d789b Merge branch 'profiling' into perf/short-circuit 2021-11-08 14:35:26 -07:00
William Ballenthin
6c8d246af9 fix bad merge 2021-11-08 14:31:43 -07:00
William Ballenthin
26b7a0b91d Merge branch 'master' into profiling 2021-11-08 14:29:40 -07:00
Willi Ballenthin
0b6c6227b9 Merge pull request #825 from mandiant/fix/circular-import-freeze
fix circular import freeze
2021-11-08 14:28:01 -07:00
William Ballenthin
94fd7673fd common: mypy 2021-11-08 14:27:44 -07:00
William Ballenthin
f598acb8fc scripts: remove old profiling scripts 2021-11-08 14:24:48 -07:00
William Ballenthin
b621205a06 mypy 2021-11-08 14:24:13 -07:00
William Ballenthin
9fa9c6a5d0 tests: add test demonstrating short circuiting 2021-11-08 14:07:44 -07:00
William Ballenthin
1a84051679 changelog 2021-11-08 14:07:31 -07:00
William Ballenthin
d987719889 engine: some: correctly count satisfied children 2021-11-08 13:53:37 -07:00
William Ballenthin
96813c37b7 remove old improt 2021-11-08 13:48:33 -07:00
William Ballenthin
70f007525d pep8 2021-11-08 12:11:01 -07:00
William Ballenthin
e3496b0660 engine: move optimizer into its own module 2021-11-08 12:10:22 -07:00
William Ballenthin
24b4c99635 changelog 2021-11-08 11:58:02 -07:00
William Ballenthin
27b4a8ba73 common: remove old import 2021-11-08 11:55:58 -07:00
William Ballenthin
51b3f38f55 common: move Result to capa.common from capa.engine
fixes circular import error in capa.features.freeze
2021-11-08 11:54:36 -07:00
William Ballenthin
a35be4a666 scripts: add py script for profiling time 2021-11-08 11:52:34 -07:00
William Ballenthin
5770d0c12d perf: add reset routine 2021-11-08 11:52:25 -07:00
William Ballenthin
0629c584e1 common: move Result to capa.common from capa.engine
fixes circular import error in capa.features.freeze
2021-11-08 11:52:13 -07:00
William Ballenthin
480df323e5 scripts: add py script for profiling time 2021-11-08 11:51:09 -07:00
William Ballenthin
a995b53c38 perf: add reset routine 2021-11-08 11:50:49 -07:00
William Ballenthin
35fa50dbee pep8 2021-11-08 11:50:37 -07:00
William Ballenthin
d86c3f4d48 common: move Result to capa.common from capa.engine
fixes circular import error in capa.features.freeze
2021-11-08 11:50:16 -07:00
Moritz
4696c0ebb6 Merge pull request #822 from mandiant/dependabot/pip/types-psutil-5.8.14
build(deps-dev): bump types-psutil from 5.8.13 to 5.8.14
2021-11-08 17:02:58 +01:00
Moritz
09724e9787 Merge pull request #823 from mandiant/dependabot/pip/isort-5.10.0
build(deps-dev): bump isort from 5.9.3 to 5.10.0
2021-11-08 17:02:33 +01:00
dependabot[bot]
636548cdec build(deps-dev): bump isort from 5.9.3 to 5.10.0
Bumps [isort](https://github.com/pycqa/isort) from 5.9.3 to 5.10.0.
- [Release notes](https://github.com/pycqa/isort/releases)
- [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pycqa/isort/compare/5.9.3...5.10.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-08 14:16:28 +00:00
dependabot[bot]
b3970808df build(deps-dev): bump types-psutil from 5.8.13 to 5.8.14
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.13 to 5.8.14.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-08 14:16:15 +00:00
William Ballenthin
d573b83c94 rule: optimization: add some documentation 2021-11-05 16:49:38 -06:00
William Ballenthin
e63f072e40 rules: optimizer: use recursive cost of statements 2021-11-05 16:39:00 -06:00
William Ballenthin
a329147d28 engine: some: short circuit 2021-11-05 16:32:23 -06:00
William Ballenthin
18ba986eba engine: or: short circuit 2021-11-05 16:32:12 -06:00
William Ballenthin
8d9f418b2b rules: optimize by cost 2021-11-05 16:20:22 -06:00
William Ballenthin
623bac1a40 engine: statement: document that the order of children is important 2021-11-05 16:19:16 -06:00
William Ballenthin
702d00da91 gitignore 2021-11-05 15:24:24 -06:00
William Ballenthin
3a12472be8 perf: render: show evaluate.feature counter 2021-11-05 15:23:34 -06:00
William Ballenthin
6524449ad1 main: perf: human format the numbers 2021-11-05 15:23:22 -06:00
William Ballenthin
86cab26a69 add perf counters in module capa.perf 2021-11-05 14:59:22 -06:00
William Ballenthin
3d068fe3cd scripts: add utilities for collecting profile traces 2021-11-04 13:17:38 -06:00
William Ballenthin
f98236046b main: add coarse timing measurements 2021-11-04 12:38:35 -06:00
William Ballenthin
ed3bd4ef75 main: add timing ctx manager 2021-11-04 12:20:05 -06:00
Capa Bot
7d3ae7a91b Sync capa rules submodule 2021-11-03 18:29:09 +00:00
Capa Bot
0409c431b8 Sync capa rules submodule 2021-11-02 18:47:47 +00:00
Capa Bot
ffbb841b03 Sync capa rules submodule 2021-11-02 18:47:18 +00:00
Willi Ballenthin
e9a7dbc2ff Merge pull request #820 from mandiant/fix/linter-file-format
auto recognize shellcode based on file extension
2021-11-02 11:31:33 -06:00
Capa Bot
10dc8950c1 Sync capa rules submodule 2021-11-02 17:29:30 +00:00
Capa Bot
fe0fb1ccd2 Sync capa rules submodule 2021-11-02 17:17:47 +00:00
Moritz Raabe
e9170a1d4b auto recognize shellcode based on file extension 2021-11-02 18:02:37 +01:00
Capa Bot
02bd8581d8 Sync capa-testfiles submodule 2021-11-02 16:42:40 +00:00
Moritz
ca574201a4 Merge pull request #818 from mandiant/dependabot/pip/ruamel-yaml-0.17.17
build(deps): bump ruamel-yaml from 0.17.16 to 0.17.17
2021-11-02 17:36:03 +01:00
Moritz
8e744d94e6 Merge pull request #817 from mandiant/dependabot/pip/black-21.10b0
build(deps-dev): bump black from 21.9b0 to 21.10b0
2021-11-02 17:35:52 +01:00
dependabot[bot]
6a28330dd1 build(deps): bump ruamel-yaml from 0.17.16 to 0.17.17
Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.16 to 0.17.17.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-01 14:11:49 +00:00
dependabot[bot]
4537b52c18 build(deps-dev): bump black from 21.9b0 to 21.10b0
Bumps [black](https://github.com/psf/black) from 21.9b0 to 21.10b0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-01 14:11:42 +00:00
Willi Ballenthin
29e61e24a6 Merge pull request #815 from mandiant/feature-3.0.3
v3.0.3
2021-10-27 10:14:35 -06:00
William Ballenthin
041c8a4c2d changelog 2021-10-27 09:43:35 -06:00
Capa Bot
433dfd8fa9 Sync capa rules submodule 2021-10-27 15:34:46 +00:00
William Ballenthin
2b46043419 v3.0.3 2021-10-27 09:32:45 -06:00
William Ballenthin
d31c8b0190 changelog 2021-10-27 09:29:54 -06:00
Willi Ballenthin
9003fdc1a2 Merge pull request #814 from mandiant/fix-802
bail with unique error codes
2021-10-27 09:25:55 -06:00
William Ballenthin
b1f4a2853e Merge branch 'master' of github.com:fireeye/capa into fix-802 2021-10-27 09:25:29 -06:00
William Ballenthin
07412f047d tests: fix check of status code E_MISSING_FILE 2021-10-27 09:24:22 -06:00
Willi Ballenthin
26ac21b908 Merge pull request #813 from mandiant/fix-130
Fix 130
2021-10-27 09:20:43 -06:00
William Ballenthin
4cc496a8e5 main: use constants to represent error codes 2021-10-26 16:57:33 -06:00
William Ballenthin
4f4e0881b5 changelog 2021-10-26 16:48:02 -06:00
William Ballenthin
9fe164665c main: exit with unique error codes when bailing
TODO: create an enum of all these things so they're easy for a human to
read.

closes #802
2021-10-26 16:46:43 -06:00
William Ballenthin
c74193b5d7 Merge branch 'master' of github.com:fireeye/capa into fix-130 2021-10-26 15:26:22 -06:00
William Ballenthin
31ef06ef2b sync testfiles 2021-10-26 15:26:18 -06:00
Capa Bot
83a95d66d1 Sync capa-testfiles submodule 2021-10-26 21:24:10 +00:00
William Ballenthin
4451b76f89 pep8 2021-10-26 15:21:28 -06:00
William Ballenthin
a1075b63ec tests: add demonstration of bb layout 2021-10-26 15:20:08 -06:00
William Ballenthin
97c41228e0 changelog 2021-10-26 15:10:50 -06:00
William Ballenthin
8903d2abcb show-capabilities-by-function: also include matches from BBs in fn 2021-10-26 15:05:53 -06:00
William Ballenthin
328e13fbfe main: compute function & bb layout
so bb can be associated with function in output.
only captures BBs that have a rule match,
otherwise, there might be too much data captured.
closes #130.
2021-10-26 15:04:50 -06:00
Capa Bot
b7cd5fec76 Sync capa rules submodule 2021-10-25 19:26:56 +00:00
Willi Ballenthin
6086dbcd84 Merge pull request #812 from mandiant/dependabot/pip/viv-utils-flirt--0.6.7
build(deps): bump viv-utils[flirt] from 0.6.6 to 0.6.7
2021-10-25 09:14:41 -06:00
Willi Ballenthin
5f88e02aa3 Merge pull request #811 from mandiant/dependabot/pip/types-pyyaml-6.0.0
build(deps-dev): bump types-pyyaml from 5.4.12 to 6.0.0
2021-10-25 09:04:56 -06:00
dependabot[bot]
96a4f585cd build(deps): bump viv-utils[flirt] from 0.6.6 to 0.6.7
Bumps [viv-utils[flirt]](https://github.com/williballenthin/viv-utils) from 0.6.6 to 0.6.7.
- [Release notes](https://github.com/williballenthin/viv-utils/releases)
- [Commits](https://github.com/williballenthin/viv-utils/compare/v0.6.6...v0.6.7)

---
updated-dependencies:
- dependency-name: viv-utils[flirt]
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-25 14:09:33 +00:00
dependabot[bot]
73ec980e01 build(deps-dev): bump types-pyyaml from 5.4.12 to 6.0.0
Bumps [types-pyyaml](https://github.com/python/typeshed) from 5.4.12 to 6.0.0.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-pyyaml
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-25 14:09:26 +00:00
Capa Bot
e5ed7ce0d3 Sync capa rules submodule 2021-10-25 03:39:00 +00:00
Capa Bot
08a7b8afb7 Sync capa-testfiles submodule 2021-10-24 22:00:33 +00:00
Capa Bot
bb7a588f6b Sync capa rules submodule 2021-10-22 17:23:31 +00:00
Capa Bot
9faa0734c1 Sync capa-testfiles submodule 2021-10-22 17:11:32 +00:00
Capa Bot
cf55b34b4e Sync capa-testfiles submodule 2021-10-22 16:57:10 +00:00
Capa Bot
5881899cc2 Sync capa-testfiles submodule 2021-10-22 16:56:36 +00:00
William Ballenthin
4e64ef8ab3 gitignore 2021-10-22 10:20:14 -06:00
Willi Ballenthin
7e5532ac84 Merge pull request #807 from mandiant/dependabot/pip/types-pyyaml-5.4.12
build(deps-dev): bump types-pyyaml from 5.4.10 to 5.4.12
2021-10-18 13:49:55 -06:00
dependabot[bot]
3d638df08c build(deps-dev): bump types-pyyaml from 5.4.10 to 5.4.12
Bumps [types-pyyaml](https://github.com/python/typeshed) from 5.4.10 to 5.4.12.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-pyyaml
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-18 18:50:32 +00:00
Willi Ballenthin
bf984a38ed Merge pull request #808 from mandiant/dependabot/pip/types-tabulate-0.8.3
build(deps-dev): bump types-tabulate from 0.8.2 to 0.8.3
2021-10-18 12:49:47 -06:00
dependabot[bot]
e68f2ce141 build(deps-dev): bump types-tabulate from 0.8.2 to 0.8.3
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.2 to 0.8.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-tabulate
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-18 18:42:21 +00:00
Willi Ballenthin
d0a3244108 Merge pull request #809 from mandiant/dependabot/pip/types-termcolor-1.1.2
build(deps-dev): bump types-termcolor from 1.1.1 to 1.1.2
2021-10-18 12:41:37 -06:00
dependabot[bot]
d09901d512 build(deps-dev): bump types-termcolor from 1.1.1 to 1.1.2
Bumps [types-termcolor](https://github.com/python/typeshed) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-termcolor
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-18 18:26:20 +00:00
Willi Ballenthin
2d46bac351 Merge pull request #810 from mandiant/dependabot/pip/types-psutil-5.8.13
build(deps-dev): bump types-psutil from 5.8.12 to 5.8.13
2021-10-18 12:25:22 -06:00
Willi Ballenthin
2285c76cbf Merge pull request #806 from mandiant/dependabot/pip/types-colorama-0.4.4
build(deps-dev): bump types-colorama from 0.4.3 to 0.4.4
2021-10-18 12:25:08 -06:00
dependabot[bot]
c003ab4e42 build(deps-dev): bump types-psutil from 5.8.12 to 5.8.13
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.12 to 5.8.13.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-18 18:24:52 +00:00
Willi Ballenthin
78e97a217a Merge pull request #805 from mandiant/dependabot/pip/pyyaml-6.0
build(deps): bump pyyaml from 5.4.1 to 6.0
2021-10-18 12:24:20 -06:00
dependabot[bot]
720585170c build(deps-dev): bump types-colorama from 0.4.3 to 0.4.4
Bumps [types-colorama](https://github.com/python/typeshed) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-colorama
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-18 14:15:08 +00:00
dependabot[bot]
19d54f3f4d build(deps): bump pyyaml from 5.4.1 to 6.0
Bumps [pyyaml](https://github.com/yaml/pyyaml) from 5.4.1 to 6.0.
- [Release notes](https://github.com/yaml/pyyaml/releases)
- [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES)
- [Commits](https://github.com/yaml/pyyaml/compare/5.4.1...6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-18 14:15:05 +00:00
Moritz
23a0aec1e6 Merge pull request #803 from mandiant/dependabot/pip/types-psutil-5.8.12
build(deps-dev): bump types-psutil from 5.8.8 to 5.8.12
2021-10-12 14:22:52 +02:00
Moritz
6b0db01c13 Merge pull request #804 from mandiant/dependabot/pip/pycodestyle-2.8.0
build(deps-dev): bump pycodestyle from 2.7.0 to 2.8.0
2021-10-12 14:22:44 +02:00
dependabot[bot]
93c14c3a1f build(deps-dev): bump pycodestyle from 2.7.0 to 2.8.0
Bumps [pycodestyle](https://github.com/PyCQA/pycodestyle) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/PyCQA/pycodestyle/releases)
- [Changelog](https://github.com/PyCQA/pycodestyle/blob/main/CHANGES.txt)
- [Commits](https://github.com/PyCQA/pycodestyle/compare/2.7.0...2.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-11 14:10:44 +00:00
dependabot[bot]
b66760fc5c build(deps-dev): bump types-psutil from 5.8.8 to 5.8.12
Bumps [types-psutil](https://github.com/python/typeshed) from 5.8.8 to 5.8.12.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-psutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-11 14:10:38 +00:00
Willi Ballenthin
64a801cc55 Merge pull request #801 from mandiant/dependabot/pip/pytest-cov-3.0.0
build(deps-dev): bump pytest-cov from 2.12.1 to 3.0.0
2021-10-04 14:13:43 -06:00
dependabot[bot]
35fc8ee3e8 build(deps-dev): bump pytest-cov from 2.12.1 to 3.0.0
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.12.1 to 3.0.0.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.12.1...v3.0.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 14:09:30 +00:00
Capa Bot
887c566f7c Sync capa rules submodule 2021-09-30 19:28:13 +00:00
Capa Bot
2f59499087 Sync capa rules submodule 2021-09-30 14:01:54 +00:00
Capa Bot
b4a239569c Sync capa rules submodule 2021-09-30 13:29:23 +00:00
Moritz
e4073a844b Merge pull request #794 from mandiant/go-mandiant
s/fireeye/mandiant
2021-09-30 15:28:53 +02:00
Capa Bot
f313ad37b3 Sync capa-testfiles submodule 2021-09-29 14:54:48 +00:00
Moritz Raabe
8de69c639a s/fireeye/mandiant 2021-09-29 12:55:16 +02:00
Willi Ballenthin
0714dbee0d changelog: formatting 2021-09-28 10:26:28 -06:00
Willi Ballenthin
ead8a836be Merge pull request #799 from mandiant/williballenthin-patch-1
v3.0.2
2021-09-28 10:25:10 -06:00
Willi Ballenthin
d471e66073 v3.0.2 2021-09-28 09:44:46 -06:00
Willi Ballenthin
4ddef1f60b changelog: v3.0.2 2021-09-28 09:41:12 -06:00
Moritz
7b9da896e8 Merge pull request #797 from mandiant/fix/pyinstaller-elf
PyInstaller fix: add hidden import and test
2021-09-28 17:37:36 +02:00
Moritz Raabe
41786f4ab8 add hidden import and test 2021-09-28 15:39:23 +02:00
Capa Bot
4661da729f Sync capa-testfiles submodule 2021-09-28 10:15:01 +00:00
Capa Bot
97dc40a585 Sync capa-testfiles submodule 2021-09-28 10:04:34 +00:00
92 changed files with 1935 additions and 876 deletions

View File

@@ -2,7 +2,7 @@
First off, thanks for taking the time to contribute!
The following is a set of guidelines for contributing to capa and its packages, which are hosted in the [FireEye Organization](https://github.com/fireeye) on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
The following is a set of guidelines for contributing to capa and its packages, which are hosted in the [Mandiant Organization](https://github.com/mandiant) on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
#### Table Of Contents
@@ -32,9 +32,9 @@ This project and everyone participating in it is governed by the [Capa Code of C
### Capa and its repositories
We host the capa project as three Github repositories:
- [capa](https://github.com/fireeye/capa)
- [capa-rules](https://github.com/fireeye/capa-rules)
- [capa-testfiles](https://github.com/fireeye/capa-testfiles)
- [capa](https://github.com/mandiant/capa)
- [capa-rules](https://github.com/mandiant/capa-rules)
- [capa-testfiles](https://github.com/mandiant/capa-testfiles)
The command line tools, logic engine, and other Python source code are found in the `capa` repository.
This is the repository to fork when you want to enhance the features, performance, or user interface of capa.
@@ -54,7 +54,7 @@ These are files you'll need in order to run the linter (in `--thorough` mode) an
### Design Decisions
When we make a significant decision in how we maintain the project and what we can or cannot support,
we will document it in the [capa issues tracker](https://github.com/fireeye/capa/issues).
we will document it in the [capa issues tracker](https://github.com/mandiant/capa/issues).
This is the best place review our discussions about what/how/why we do things in the project.
If you have a question, check to see if it is documented there.
If it is *not* documented there, or you can't find an answer, please open a issue.
@@ -78,7 +78,7 @@ Fill out [the required template](./ISSUE_TEMPLATE/bug_report.md),
#### Before Submitting A Bug Report
* **Determine [which repository the problem should be reported in](#capa-and-its-repositories)**.
* **Perform a [cursory search](https://github.com/fireeye/capa/issues?q=is%3Aissue)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
* **Perform a [cursory search](https://github.com/mandiant/capa/issues?q=is%3Aissue)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Bug Report?
@@ -101,7 +101,7 @@ Explain the problem and include additional details to help maintainers reproduce
Provide more context by answering these questions:
* **Did the problem start happening recently** (e.g. after updating to a new version of capa) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of capa?** What's the most recent version in which the problem doesn't happen? You can download older versions of capa from [the releases page](https://github.com/fireeye/capa/releases).
* If the problem started happening recently, **can you reproduce the problem in an older version of capa?** What's the most recent version in which the problem doesn't happen? You can download older versions of capa from [the releases page](https://github.com/mandiant/capa/releases).
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
* If the problem is related to working with files (e.g. opening and editing files), **does the problem happen for all files and projects or only some?** Does the problem happen only when working with local or remote files (e.g. on network drives), with files of a specific type (e.g. only JavaScript or Python files), with large files or files with very long lines, or with files in a specific encoding? Is there anything else special about the files you are using?
@@ -119,7 +119,7 @@ Before creating enhancement suggestions, please check [this list](#before-submit
#### Before Submitting An Enhancement Suggestion
* **Determine [which repository the enhancement should be suggested in](#capa-and-its-repositories).**
* **Perform a [cursory search](https://github.com/fireeye/capa/issues?q=is%3Aissue)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
* **Perform a [cursory search](https://github.com/mandiant/capa/issues?q=is%3Aissue)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Enhancement Suggestion?
@@ -138,15 +138,15 @@ Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com
Unsure where to begin contributing to capa? You can start by looking through these `good-first-issue` and `rule-idea` issues:
* [good-first-issue](https://github.com/fireeye/capa/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - issues which should only require a few lines of code, and a test or two.
* [rule-idea](https://github.com/fireeye/capa-rules/issues?q=is%3Aissue+is%3Aopen+label%3A%22rule+idea%22) - issues that describe potential new rule ideas.
* [good-first-issue](https://github.com/mandiant/capa/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - issues which should only require a few lines of code, and a test or two.
* [rule-idea](https://github.com/mandiant/capa-rules/issues?q=is%3Aissue+is%3Aopen+label%3A%22rule+idea%22) - issues that describe potential new rule ideas.
Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have.
#### Local development
capa and all its resources can be developed locally.
For instructions on how to do this, see the "Method 3" section of the [installation guide](https://github.com/fireeye/capa/blob/master/doc/installation.md).
For instructions on how to do this, see the "Method 3" section of the [installation guide](https://github.com/mandiant/capa/blob/master/doc/installation.md).
### Pull Requests
@@ -190,8 +190,8 @@ Our CI pipeline will reformat and enforce the Python styleguide.
All (non-nursery) capa rules must:
1. pass the [linter](https://github.com/fireeye/capa/blob/master/scripts/lint.py), and
2. be formatted with [capafmt](https://github.com/fireeye/capa/blob/master/scripts/capafmt.py)
1. pass the [linter](https://github.com/mandiant/capa/blob/master/scripts/lint.py), and
2. be formatted with [capafmt](https://github.com/mandiant/capa/blob/master/scripts/capafmt.py)
This ensures that all rules meet the same minimum level of quality and are structured in a consistent way.
Our CI pipeline will reformat and enforce the capa rules styleguide.

View File

@@ -5,16 +5,16 @@ about: Create a report to help us improve
---
<!--
# Is your bug report related to capa rules (for example a false positive)?
We use submodules to separate code, rules and test data. If your issue is related to capa rules, please report it at https://github.com/fireeye/capa-rules/issues.
We use submodules to separate code, rules and test data. If your issue is related to capa rules, please report it at https://github.com/mandiant/capa-rules/issues.
# Have you checked that your issue isn't already filed?
Please search if there is a similar issue at https://github.com/fireeye/capa/issues. If there is already a similar issue, please add more details there instead of opening a new one.
Please search if there is a similar issue at https://github.com/mandiant/capa/issues. If there is already a similar issue, please add more details there instead of opening a new one.
# Have you read capa's Code of Conduct?
By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/fireeye/capa/blob/master/.github/CODE_OF_CONDUCT.md
By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/mandiant/capa/blob/master/.github/CODE_OF_CONDUCT.md
# Have you read capa's CONTRIBUTING guide?
It contains helpful information about how to contribute to capa. Check https://github.com/fireeye/capa/blob/master/.github/CONTRIBUTING.md#reporting-bugs
It contains helpful information about how to contribute to capa. Check https://github.com/mandiant/capa/blob/master/.github/CONTRIBUTING.md#reporting-bugs
-->
### Description

View File

@@ -5,16 +5,16 @@ about: Suggest an idea for capa
---
<!--
# Is your issue related to capa rules (for example an idea for a new rule)?
We use submodules to separate code, rules and test data. If your issue is related to capa rules, please report it at https://github.com/fireeye/capa-rules/issues.
We use submodules to separate code, rules and test data. If your issue is related to capa rules, please report it at https://github.com/mandiant/capa-rules/issues.
# Have you checked that your issue isn't already filed?
Please search if there is a similar issue at https://github.com/fireeye/capa/issues. If there is already a similar issue, please add more details there instead of opening a new one.
Please search if there is a similar issue at https://github.com/mandiant/capa/issues. If there is already a similar issue, please add more details there instead of opening a new one.
# Have you read capa's Code of Conduct?
By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/fireeye/capa/blob/master/.github/CODE_OF_CONDUCT.md
By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/mandiant/capa/blob/master/.github/CODE_OF_CONDUCT.md
# Have you read capa's CONTRIBUTING guide?
It contains helpful information about how to contribute to capa. Check https://github.com/fireeye/capa/blob/master/.github/CONTRIBUTING.md#suggesting-enhancements
It contains helpful information about how to contribute to capa. Check https://github.com/mandiant/capa/blob/master/.github/CONTRIBUTING.md#suggesting-enhancements
-->
### Summary

View File

@@ -3,7 +3,7 @@
Thank you for contributing to capa! <3
Please read capa's CONTRIBUTING guide if you haven't done so already.
It contains helpful information about how to contribute to capa. Check https://github.com/fireeye/capa/blob/master/.github/CONTRIBUTING.md
It contains helpful information about how to contribute to capa. Check https://github.com/mandiant/capa/blob/master/.github/CONTRIBUTING.md
Please describe the changes in this pull request (PR). Include your motivation and context to help us review.

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
import PyInstaller.utils.hooks
# ref: https://groups.google.com/g/pyinstaller/c/amWi0-66uZI/m/miPoKfWjBAAJ

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
from PyInstaller.utils.hooks import copy_metadata
@@ -45,8 +45,8 @@ hiddenimports = [
"vivisect.analysis.crypto",
"vivisect.analysis.crypto.constants",
"vivisect.analysis.elf",
"vivisect.analysis.elf",
"vivisect.analysis.elf.elfplt",
"vivisect.analysis.elf.elfplt_late",
"vivisect.analysis.elf.libc_start_main",
"vivisect.analysis.generic",
"vivisect.analysis.generic",

View File

@@ -1,5 +1,5 @@
# -*- mode: python -*-
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
import os.path
import subprocess

View File

@@ -41,8 +41,12 @@ jobs:
run: pip install -e .
- name: Build standalone executable
run: pyinstaller .github/pyinstaller/pyinstaller.spec
- name: Does it run?
- name: Does it run (PE)?
run: dist/capa "tests/data/Practical Malware Analysis Lab 01-01.dll_"
- name: Does it run (Shellcode)?
run: dist/capa "tests/data/499c2a85f6e8142c3f48d4251c9c7cd6.raw32"
- name: Does it run (ELF)?
run: dist/capa "tests/data/7351f8a40c5450557b24622417fc478d.elf_"
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.asset_name }}

View File

@@ -12,18 +12,18 @@ jobs:
- name: Checkout capa-rules
uses: actions/checkout@v2
with:
repository: fireeye/capa-rules
repository: mandiant/capa-rules
token: ${{ secrets.CAPA_TOKEN }}
- name: Tag capa-rules
run: |
# user information is needed to create annotated tags (with a message)
git config user.email 'capa-dev@fireeye.com'
git config user.email 'capa-dev@mandiant.com'
git config user.name 'Capa Bot'
name=${{ github.event.release.tag_name }}
git tag $name -m "https://github.com/fireeye/capa/releases/$name"
git tag $name -m "https://github.com/mandiant/capa/releases/$name"
- name: Push tag to capa-rules
uses: ad-m/github-push-action@master
with:
repository: fireeye/capa-rules
repository: mandiant/capa-rules
github_token: ${{ secrets.CAPA_TOKEN }}
tags: true

View File

@@ -30,7 +30,7 @@ jobs:
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: "3.8"
- name: Install dependencies
run: pip install -e .[dev]
- name: Lint with isort
@@ -50,7 +50,7 @@ jobs:
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: "3.8"
- name: Install capa
run: pip install -e .
- name: Run rule linter
@@ -65,13 +65,15 @@ jobs:
matrix:
os: [ubuntu-20.04, windows-2019, macos-10.15]
# across all operating systems
python-version: [3.6, 3.9]
python-version: ["3.6", "3.10"]
include:
# on Ubuntu run these as well
- os: ubuntu-20.04
python-version: 3.7
python-version: "3.7"
- os: ubuntu-20.04
python-version: 3.8
python-version: "3.8"
- os: ubuntu-20.04
python-version: "3.9"
steps:
- name: Checkout capa with submodules
uses: actions/checkout@v2

4
.gitignore vendored
View File

@@ -114,3 +114,7 @@ venv.bak/
isort-output.log
black-output.log
rule-linter-output.log
.vscode
scripts/perf/*.txt
scripts/perf/*.svg
scripts/perf/*.zip

View File

@@ -17,8 +17,135 @@
### Development
### Raw diffs
- [capa v3.0.1...master](https://github.com/fireeye/capa/compare/v3.0.1...master)
- [capa-rules v3.0.1...master](https://github.com/fireeye/capa-rules/compare/v3.0.1...master)
- [capa v3.1.0...master](https://github.com/mandiant/capa/compare/v3.1.0...master)
- [capa-rules v3.1.0...master](https://github.com/mandiant/capa-rules/compare/v3.1.0...master)
## v3.1.0 (2022-01-10)
This release improves the performance of capa while also adding 23 new rules and many code quality enhancements. We profiled capa's CPU usage and optimized the way that it matches rules, such as by short circuiting when appropriate. According to our testing, the matching phase is approximately 66% faster than v3.0.3! We also added support for Python 3.10, aarch64 builds, and additional MAEC metadata in the rule headers.
This release adds 23 new rules, including nine by Jakub Jozwiak of Mandiant. @ryantxu1 and @dzbeck updated the ATT&CK and MBC mappings for many rules. Thank you!
And as always, welcome first time contributors!
- @kn0wl3dge
- @jtothej
- @cl30
### New Features
- engine: short circuit logic nodes for better performance #824 @williballenthin
- engine: add optimizer the order faster nodes first #829 @williballenthin
- engine: optimize rule evaluation by skipping rules that can't match #830 @williballenthin
- support python 3.10 #816 @williballenthin
- support aarch64 #683 @williballenthin
- rules: support maec/malware-family meta #841 @mr-tz
- engine: better type annotations/exhaustiveness checking #839 @cl30
### Breaking Changes: None
### New Rules (23)
- nursery/delete-windows-backup-catalog michael.hunhoff@mandiant.com
- nursery/disable-automatic-windows-recovery-features michael.hunhoff@mandiant.com
- nursery/capture-webcam-video @johnk3r
- nursery/create-registry-key-via-stdregprov michael.hunhoff@mandiant.com
- nursery/delete-registry-key-via-stdregprov michael.hunhoff@mandiant.com
- nursery/delete-registry-value-via-stdregprov michael.hunhoff@mandiant.com
- nursery/query-or-enumerate-registry-key-via-stdregprov michael.hunhoff@mandiant.com
- nursery/query-or-enumerate-registry-value-via-stdregprov michael.hunhoff@mandiant.com
- nursery/set-registry-value-via-stdregprov michael.hunhoff@mandiant.com
- data-manipulation/compression/decompress-data-using-ucl jakub.jozwiak@mandiant.com
- linking/static/wolfcrypt/linked-against-wolfcrypt jakub.jozwiak@mandiant.com
- linking/static/wolfssl/linked-against-wolfssl jakub.jozwiak@mandiant.com
- anti-analysis/packer/pespin/packed-with-pespin jakub.jozwiak@mandiant.com
- load-code/shellcode/execute-shellcode-via-windows-fibers jakub.jozwiak@mandiant.com
- load-code/shellcode/execute-shellcode-via-enumuilanguages jakub.jozwiak@mandiant.com
- anti-analysis/packer/themida/packed-with-themida william.ballenthin@mandiant.com
- load-code/shellcode/execute-shellcode-via-createthreadpoolwait jakub.jozwiak@mandiant.com
- host-interaction/process/inject/inject-shellcode-using-a-file-mapping-object jakub.jozwiak@mandiant.com
- load-code/shellcode/execute-shellcode-via-copyfile2 jakub.jozwiak@mandiant.com
- malware-family/plugx/match-known-plugx-module still@teamt5.org
### Rule Changes
- update ATT&CK mappings by @ryantxu1
- update ATT&CK and MBC mappings by @dzbeck
- aplib detection by @cdong1012
- golang runtime detection by @stevemk14eber
### Bug Fixes
- fix circular import error #825 @williballenthin
- fix smda negative number extraction #430 @kn0wl3dge
### capa explorer IDA Pro plugin
- pin supported versions to >= 7.4 and < 8.0 #849 @mike-hunhoff
### Development
- add profiling infrastructure #828 @williballenthin
- linter: detect shellcode extension #820 @mr-tz
- show features script: add backend flag #430 @kn0wl3dge
### Raw diffs
- [capa v3.0.3...v3.1.0](https://github.com/mandiant/capa/compare/v3.0.3...v3.1.0)
- [capa-rules v3.0.3...v3.1.0](https://github.com/mandiant/capa-rules/compare/v3.0.3...v3.1.0)
## v3.0.3 (2021-10-27)
This is primarily a rule maintenance release:
- eight new rules, including all relevant techniques from [ATT&CK v10](https://medium.com/mitre-attack/introducing-attack-v10-7743870b37e3), and
- two rules removed, due to the prevalence of false positives
We've also tweaked the status codes returned by capa.exe to be more specific and added a bit more metadata to the JSON output format.
As always, welcome first time contributors!
- still@teamt5.org
- zander.work@mandiant.com
### New Features
- show in which function a BB match is #130 @williballenthin
- main: exit with unique error codes when bailing #802 @williballenthin
### New Rules (8)
- nursery/resolve-function-by-fnv-1a-hash still@teamt5.org
- data-manipulation/encryption/encrypt-data-using-memfrob-from-glibc zander.work@mandiant.com
- collection/group-policy/discover-group-policy-via-gpresult william.ballenthin@mandiant.com
- host-interaction/bootloader/manipulate-safe-mode-programs william.ballenthin@mandiant.com
- nursery/enable-safe-mode-boot william.ballenthin@mandiant.com
- persistence/iis/persist-via-iis-module william.ballenthin@mandiant.com
- persistence/iis/persist-via-isapi-extension william.ballenthin@mandiant.com
- targeting/language/identify-system-language-via-api william.ballenthin@mandiant.com
## Removed rules (2)
- load-code/pe/parse-pe-exports: too many false positives in unrelated structure accesses
- anti-analysis/anti-vm/vm-detection/execute-anti-vm-instructions: too many false positives in junk code
### Bug Fixes
- update references from FireEye to Mandiant
### Raw diffs
- [capa v3.0.2...v3.0.3](https://github.com/fireeye/capa/compare/v3.0.2...v3.0.3)
- [capa-rules v3.0.2...v3.0.3](https://github.com/fireeye/capa-rules/compare/v3.0.2...v3.0.3)
## v3.0.2 (2021-09-28)
This release fixes an issue with the standalone executables built with PyInstaller when running capa against ELF files.
### Bug Fixes
- fix bug in PyInstaller config preventing ELF analysis #795 @mr-tz
### Raw diffs
- [capa v3.0.1...v3.0.2](https://github.com/fireeye/capa/compare/v3.0.1...v3.0.2)
- [capa-rules v3.0.1...v3.0.2](https://github.com/fireeye/capa-rules/compare/v3.0.1...v3.0.2)
## v3.0.1 (2021-09-27)
@@ -70,29 +197,29 @@ Also, welcome first time contributors:
### New Rules (80)
- collection/webcam/capture-webcam-image @johnk3r
- nursery/list-drag-and-drop-files michael.hunhoff@fireeye.com
- nursery/monitor-clipboard-content michael.hunhoff@fireeye.com
- nursery/monitor-local-ipv4-address-changes michael.hunhoff@fireeye.com
- nursery/load-windows-common-language-runtime michael.hunhoff@fireeye.com
- nursery/resize-volume-shadow-copy-storage michael.hunhoff@fireeye.com
- nursery/add-user-account-group michael.hunhoff@fireeye.com
- nursery/add-user-account-to-group michael.hunhoff@fireeye.com
- nursery/add-user-account michael.hunhoff@fireeye.com
- nursery/change-user-account-password michael.hunhoff@fireeye.com
- nursery/delete-user-account-from-group michael.hunhoff@fireeye.com
- nursery/delete-user-account-group michael.hunhoff@fireeye.com
- nursery/delete-user-account michael.hunhoff@fireeye.com
- nursery/list-domain-servers michael.hunhoff@fireeye.com
- nursery/list-groups-for-user-account michael.hunhoff@fireeye.com
- nursery/list-user-account-groups michael.hunhoff@fireeye.com
- nursery/list-user-accounts-for-group michael.hunhoff@fireeye.com
- nursery/list-user-accounts michael.hunhoff@fireeye.com
- nursery/parse-url michael.hunhoff@fireeye.com
- nursery/register-raw-input-devices michael.hunhoff@fireeye.com
- anti-analysis/packer/gopacker/packed-with-gopacker jared.wilson@fireeye.com
- nursery/list-drag-and-drop-files michael.hunhoff@mandiant.com
- nursery/monitor-clipboard-content michael.hunhoff@mandiant.com
- nursery/monitor-local-ipv4-address-changes michael.hunhoff@mandiant.com
- nursery/load-windows-common-language-runtime michael.hunhoff@mandiant.com
- nursery/resize-volume-shadow-copy-storage michael.hunhoff@mandiant.com
- nursery/add-user-account-group michael.hunhoff@mandiant.com
- nursery/add-user-account-to-group michael.hunhoff@mandiant.com
- nursery/add-user-account michael.hunhoff@mandiant.com
- nursery/change-user-account-password michael.hunhoff@mandiant.com
- nursery/delete-user-account-from-group michael.hunhoff@mandiant.com
- nursery/delete-user-account-group michael.hunhoff@mandiant.com
- nursery/delete-user-account michael.hunhoff@mandiant.com
- nursery/list-domain-servers michael.hunhoff@mandiant.com
- nursery/list-groups-for-user-account michael.hunhoff@mandiant.com
- nursery/list-user-account-groups michael.hunhoff@mandiant.com
- nursery/list-user-accounts-for-group michael.hunhoff@mandiant.com
- nursery/list-user-accounts michael.hunhoff@mandiant.com
- nursery/parse-url michael.hunhoff@mandiant.com
- nursery/register-raw-input-devices michael.hunhoff@mandiant.com
- anti-analysis/packer/gopacker/packed-with-gopacker jared.wilson@mandiant.com
- host-interaction/driver/create-device-object @mr-tz
- host-interaction/process/create/execute-command @mr-tz
- data-manipulation/encryption/create-new-key-via-cryptacquirecontext chuong.dong@fireeye.com
- data-manipulation/encryption/create-new-key-via-cryptacquirecontext chuong.dong@mandiant.com
- host-interaction/log/clfs/append-data-to-clfs-log-container blaine.stancill@mandiant.com
- host-interaction/log/clfs/read-data-from-clfs-log-container blaine.stancill@mandiant.com
- data-manipulation/encryption/hc-128/encrypt-data-using-hc-128-via-wolfssl blaine.stancill@mandiant.com
@@ -114,15 +241,15 @@ Also, welcome first time contributors:
- persistence/persist-via-shell-profile-or-rc-file joakim@intezer.com
- persistence/service/persist-via-rc-script joakim@intezer.com
- collection/get-current-user-on-linux joakim@intezer.com
- collection/network/get-mac-address-on-windows moritz.raabe@fireeye.com
- host-interaction/file-system/read/read-file-on-linux moritz.raabe@fireeye.com joakim@intezer.com
- host-interaction/file-system/read/read-file-on-windows moritz.raabe@fireeye.com
- host-interaction/file-system/write/write-file-on-windows william.ballenthin@fireeye.com
- host-interaction/os/info/get-system-information-on-windows moritz.raabe@fireeye.com joakim@intezer.com
- host-interaction/process/create/create-process-on-windows moritz.raabe@fireeye.com
- linking/runtime-linking/link-function-at-runtime-on-windows moritz.raabe@fireeye.com
- collection/network/get-mac-address-on-windows moritz.raabe@mandiant.com
- host-interaction/file-system/read/read-file-on-linux moritz.raabe@mandiant.com joakim@intezer.com
- host-interaction/file-system/read/read-file-on-windows moritz.raabe@mandiant.com
- host-interaction/file-system/write/write-file-on-windows william.ballenthin@mandiant.com
- host-interaction/os/info/get-system-information-on-windows moritz.raabe@mandiant.com joakim@intezer.com
- host-interaction/process/create/create-process-on-windows moritz.raabe@mandiant.com
- linking/runtime-linking/link-function-at-runtime-on-windows moritz.raabe@mandiant.com
- nursery/create-process-on-linux joakim@intezer.com
- nursery/enumerate-files-on-linux william.ballenthin@fireeye.com
- nursery/enumerate-files-on-linux william.ballenthin@mandiant.com
- nursery/get-mac-address-on-linux joakim@intezer.com
- nursery/get-system-information-on-linux joakim@intezer.com
- nursery/link-function-at-runtime-on-linux joakim@intezer.com
@@ -154,8 +281,8 @@ Also, welcome first time contributors:
### Development
### Raw diffs
- [capa v2.0.0...v3.0.0](https://github.com/fireeye/capa/compare/v2.0.0...v3.0.0)
- [capa-rules v2.0.0...v3.0.0](https://github.com/fireeye/capa-rules/compare/v2.0.0...v3.0.0)
- [capa v2.0.0...v3.0.0](https://github.com/mandiant/capa/compare/v2.0.0...v3.0.0)
- [capa-rules v2.0.0...v3.0.0](https://github.com/mandiant/capa-rules/compare/v2.0.0...v3.0.0)
## v2.0.0 (2021-07-19)
@@ -174,7 +301,7 @@ A huge thanks to everyone who submitted issues, provided feedback, and contribut
### New Features
- rules: update ATT&CK and MBC mappings https://github.com/fireeye/capa-rules/pull/317 @williballenthin
- rules: update ATT&CK and MBC mappings https://github.com/mandiant/capa-rules/pull/317 @williballenthin
- main: use FLIRT signatures to identify and ignore library code #446 @williballenthin
- tests: update test cases and caching #545 @mr-tz
- scripts: capa2yara.py convert capa rules to YARA rules #561 @ruppde
@@ -262,34 +389,34 @@ A huge thanks to everyone who submitted issues, provided feedback, and contribut
- nursery/run-in-container @williballenthin
- persistence/registry/appinitdlls/disable-appinit_dlls-code-signature-enforcement @williballenthin
- collection/password-manager/steal-keepass-passwords-using-keefarce @Ana06
- host-interaction/network/connectivity/check-internet-connectivity-via-wininet matthew.williams@fireeye.com michael.hunhoff@fireeye.com
- host-interaction/network/connectivity/check-internet-connectivity-via-wininet matthew.williams@mandiant.com michael.hunhoff@mandiant.com
- nursery/create-bits-job @mr-tz
- nursery/execute-syscall-instruction @kulinacs @mr-tz
- nursery/connect-to-wmi-namespace-via-wbemlocator michael.hunhoff@fireeye.com
- nursery/connect-to-wmi-namespace-via-wbemlocator michael.hunhoff@mandiant.com
- anti-analysis/obfuscation/obfuscated-with-callobfuscator johnk3r
- executable/installer/inno-setup/packaged-as-an-inno-setup-installer awillia2@cisco.com
- data-manipulation/hashing/djb2/hash-data-using-djb2 awillia2@cisco.com
- data-manipulation/encoding/base64/decode-data-using-base64-via-dword-translation-table gilbert.elliot@fireeye.com
- nursery/list-tcp-connections-and-listeners michael.hunhoff@fireeye.com
- nursery/list-udp-connections-and-listeners michael.hunhoff@fireeye.com
- nursery/log-keystrokes-via-raw-input-data michael.hunhoff@fireeye.com
- nursery/register-http-server-url michael.hunhoff@fireeye.com
- internal/limitation/file/internal-autoit-file-limitation.yml william.ballenthin@fireeye.com
- internal/limitation/file/internal-dotnet-file-limitation.yml william.ballenthin@fireeye.com
- internal/limitation/file/internal-installer-file-limitation.yml william.ballenthin@fireeye.com
- internal/limitation/file/internal-packer-file-limitation.yml william.ballenthin@fireeye.com
- data-manipulation/encoding/base64/decode-data-using-base64-via-dword-translation-table gilbert.elliot@mandiant.com
- nursery/list-tcp-connections-and-listeners michael.hunhoff@mandiant.com
- nursery/list-udp-connections-and-listeners michael.hunhoff@mandiant.com
- nursery/log-keystrokes-via-raw-input-data michael.hunhoff@mandiant.com
- nursery/register-http-server-url michael.hunhoff@mandiant.com
- internal/limitation/file/internal-autoit-file-limitation.yml william.ballenthin@mandiant.com
- internal/limitation/file/internal-dotnet-file-limitation.yml william.ballenthin@mandiant.com
- internal/limitation/file/internal-installer-file-limitation.yml william.ballenthin@mandiant.com
- internal/limitation/file/internal-packer-file-limitation.yml william.ballenthin@mandiant.com
- host-interaction/network/domain/enumerate-domain-computers-via-ldap awillia2@cisco.com
- host-interaction/network/domain/get-domain-controller-name awillia2@cisco.com
- internal/limitation/file/internal-visual-basic-file-limitation @mr-tz
- data-manipulation/hashing/md5/hash-data-with-md5 moritz.raabe@fireeye.com
- data-manipulation/hashing/md5/hash-data-with-md5 moritz.raabe@mandiant.com
- compiler/autohotkey/compiled-with-autohotkey awillia2@cisco.com
- internal/limitation/file/internal-autohotkey-file-limitation @mr-tz
- host-interaction/process/dump/create-process-memory-minidump michael.hunhoff@fireeye.com
- nursery/get-storage-device-properties michael.hunhoff@fireeye.com
- nursery/execute-shell-command-via-windows-remote-management michael.hunhoff@fireeye.com
- nursery/get-token-privileges michael.hunhoff@fireeye.com
- nursery/prompt-user-for-credentials michael.hunhoff@fireeye.com
- nursery/spoof-parent-pid michael.hunhoff@fireeye.com
- host-interaction/process/dump/create-process-memory-minidump michael.hunhoff@mandiant.com
- nursery/get-storage-device-properties michael.hunhoff@mandiant.com
- nursery/execute-shell-command-via-windows-remote-management michael.hunhoff@mandiant.com
- nursery/get-token-privileges michael.hunhoff@mandiant.com
- nursery/prompt-user-for-credentials michael.hunhoff@mandiant.com
- nursery/spoof-parent-pid michael.hunhoff@mandiant.com
### Bug Fixes
@@ -309,8 +436,8 @@ A huge thanks to everyone who submitted issues, provided feedback, and contribut
### Development
- ci: add capa release link to capa-rules tag #517 @Ana06
- ci, changelog: update `New Rules` section in CHANGELOG automatically https://github.com/fireeye/capa-rules/pull/374 #549 #604 @Ana06
- ci, changelog: support multiple author in sync GH https://github.com/fireeye/capa-rules/pull/378 @Ana06
- ci, changelog: update `New Rules` section in CHANGELOG automatically https://github.com/mandiant/capa-rules/pull/374 #549 #604 @Ana06
- ci, changelog: support multiple author in sync GH https://github.com/mandiant/capa-rules/pull/378 @Ana06
- ci, lint: check statements for single child statements #563 @mr-tz
- ci: reject PRs without CHANGELOG update to ensure CHANGELOG is kept up-to-date #584 @Ana06
- ci: test that scripts run #660 @mr-tz
@@ -318,8 +445,8 @@ A huge thanks to everyone who submitted issues, provided feedback, and contribut
### Raw diffs
<!-- The diff uses v1.6.1 because master doesn't include v1.6.2 and v1.6.3 -->
- [capa v1.6.1...v2.0.0](https://github.com/fireeye/capa/compare/v1.6.1...v2.0.0)
- [capa-rules v1.6.1...v2.0.0](https://github.com/fireeye/capa-rules/compare/v1.6.1...v2.0.0)
- [capa v1.6.1...v2.0.0](https://github.com/mandiant/capa/compare/v1.6.1...v2.0.0)
- [capa-rules v1.6.1...v2.0.0](https://github.com/mandiant/capa-rules/compare/v1.6.1...v2.0.0)
## v1.6.3 (2021-04-29)
@@ -332,7 +459,7 @@ This release adds IDA 7.6 support to capa.
### Raw diffs
- [capa v1.6.2...v1.6.3](https://github.com/fireeye/capa/compare/v1.6.2...v1.6.3)
- [capa v1.6.2...v1.6.3](https://github.com/mandiant/capa/compare/v1.6.2...v1.6.3)
## v1.6.2 (2021-04-13)
@@ -345,7 +472,7 @@ This release backports a fix to capa 1.6: The Windows binary was built with Pyth
### Raw diffs
- [capa v1.6.1...v1.6.2](https://github.com/fireeye/capa/compare/v1.6.1...v1.6.2)
- [capa v1.6.1...v1.6.2](https://github.com/mandiant/capa/compare/v1.6.1...v1.6.2)
## v1.6.1 (2021-04-07)
@@ -413,8 +540,8 @@ This release includes several bug fixes, such as a vivisect issue that prevented
### Raw diffs
- [capa v1.6.0...v1.6.1](https://github.com/fireeye/capa/compare/v1.6.0...v1.6.1)
- [capa-rules v1.6.0...v1.6.1](https://github.com/fireeye/capa-rules/compare/v1.6.0...v1.6.1)
- [capa v1.6.0...v1.6.1](https://github.com/mandiant/capa/compare/v1.6.0...v1.6.1)
- [capa-rules v1.6.0...v1.6.1](https://github.com/mandiant/capa-rules/compare/v1.6.0...v1.6.1)
## v1.6.0 (2021-03-09)
@@ -423,7 +550,7 @@ This release adds the capa explorer rule generator plugin for IDA Pro, vivisect
### Rule Generator IDA Plugin
The capa explorer IDA plugin now helps you quickly build new capa rules using features extracted directly from your IDA database. Without leaving the plugin interface you can use the features extracted by capa explorer to develop and test new rules and save your work directly to your capa rules directory. To get started select the new `Rule Generator` tab, navigate to a function in the IDA `Disassembly` view, and click `Analyze`. For more information check out the capa explorer [readme](https://github.com/fireeye/capa/blob/master/capa/ida/plugin/README.md).
The capa explorer IDA plugin now helps you quickly build new capa rules using features extracted directly from your IDA database. Without leaving the plugin interface you can use the features extracted by capa explorer to develop and test new rules and save your work directly to your capa rules directory. To get started select the new `Rule Generator` tab, navigate to a function in the IDA `Disassembly` view, and click `Analyze`. For more information check out the capa explorer [readme](https://github.com/mandiant/capa/blob/master/capa/ida/plugin/README.md).
![](doc/img/rulegen_expanded.png)
@@ -485,8 +612,8 @@ If you have workflows that rely on the Python 2 version and need future maintena
### Raw diffs
- [capa v1.5.1...v1.6.0](https://github.com/fireeye/capa/compare/v1.5.1...v1.6.0)
- [capa-rules v1.5.1...v1.6.0](https://github.com/fireeye/capa-rules/compare/v1.5.1...v1.6.0)
- [capa v1.5.1...v1.6.0](https://github.com/mandiant/capa/compare/v1.5.1...v1.6.0)
- [capa-rules v1.5.1...v1.6.0](https://github.com/mandiant/capa-rules/compare/v1.5.1...v1.6.0)
## v1.5.1 (2021-02-09)
@@ -499,8 +626,8 @@ This release fixes the version number that we forgot to update for v1.5.0 (there
### Raw diffs
- [capa v1.5.0...v1.5.1](https://github.com/fireeye/capa/compare/v1.5.1...v1.6.0)
- [capa-rules v1.5.0...v1.5.1](https://github.com/fireeye/capa-rules/compare/v1.5.1...v1.6.0)
- [capa v1.5.0...v1.5.1](https://github.com/mandiant/capa/compare/v1.5.1...v1.6.0)
- [capa-rules v1.5.0...v1.5.1](https://github.com/mandiant/capa-rules/compare/v1.5.1...v1.6.0)
## v1.5.0 (2021-02-05)
@@ -515,7 +642,7 @@ This release brings support for running capa under Python 3 via [SMDA](https://g
@dzbeck also added [Malware Behavior Catalog](https://github.com/MBCProject/mbc-markdown) (MBC) and ATT&CK mappings for many rules.
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/fireeye/capa/). Report issues on our [issue tracker](https://github.com/fireeye/capa/issues) and contribute new rules at [capa-rules](https://github.com/fireeye/capa-rules/).
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/mandiant/capa/). Report issues on our [issue tracker](https://github.com/mandiant/capa/issues) and contribute new rules at [capa-rules](https://github.com/mandiant/capa-rules/).
### New Features
@@ -598,8 +725,8 @@ Download a standalone binary below and checkout the readme [here on GitHub](http
### Raw diffs
- [capa v1.4.1...v1.5.0](https://github.com/fireeye/capa/compare/v1.4.1...v1.5.0)
- [capa-rules v1.4.0...v1.5.0](https://github.com/fireeye/capa-rules/compare/v1.4.0...v1.5.0)
- [capa v1.4.1...v1.5.0](https://github.com/mandiant/capa/compare/v1.4.1...v1.5.0)
- [capa-rules v1.4.0...v1.5.0](https://github.com/mandiant/capa-rules/compare/v1.4.0...v1.5.0)
## v1.4.1 (2020-10-23)
@@ -611,8 +738,8 @@ This release fixes an issue building capa on our CI server, which prevented us f
### Raw diffs
- [capa v1.4.0...v1.4.1](https://github.com/fireeye/capa/compare/v1.4.0...v1.4.1)
- [capa-rules v1.4.0...v1.4.1](https://github.com/fireeye/capa-rules/compare/v1.4.0...v1.4.1)
- [capa v1.4.0...v1.4.1](https://github.com/mandiant/capa/compare/v1.4.0...v1.4.1)
- [capa-rules v1.4.0...v1.4.1](https://github.com/mandiant/capa-rules/compare/v1.4.0...v1.4.1)
## v1.4.0 (2020-10-23)
@@ -623,7 +750,7 @@ This capa release includes changes to the rule parsing, enhanced feature extract
@dzbeck added [Malware Behavior Catalog](https://github.com/MBCProject/mbc-markdown) (MBC) and ATT&CK mappings for 86 rules.
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/fireeye/capa/). Report issues on our [issue tracker](https://github.com/fireeye/capa/issues) and contribute new rules at [capa-rules](https://github.com/fireeye/capa-rules/).
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/mandiant/capa/). Report issues on our [issue tracker](https://github.com/mandiant/capa/issues) and contribute new rules at [capa-rules](https://github.com/mandiant/capa-rules/).
### New features
@@ -726,8 +853,8 @@ Download a standalone binary below and checkout the readme [here on GitHub](http
### Raw diffs
- [capa v1.3.0...v1.4.0](https://github.com/fireeye/capa/compare/v1.3.0...v1.4.0)
- [capa-rules v1.3.0...v1.4.0](https://github.com/fireeye/capa-rules/compare/v1.3.0...v1.4.0)
- [capa v1.3.0...v1.4.0](https://github.com/mandiant/capa/compare/v1.3.0...v1.4.0)
- [capa-rules v1.3.0...v1.4.0](https://github.com/mandiant/capa-rules/compare/v1.3.0...v1.4.0)
## v1.3.0 (2020-09-14)
@@ -741,7 +868,7 @@ This release brings newly updated mappings to the [Malware Behavior Catalog vers
- @weslambert
- @stevemk14ebr
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/fireeye/capa/). Report issues on our [issue tracker](https://github.com/fireeye/capa/issues) and contribute new rules at [capa-rules](https://github.com/fireeye/capa-rules/).
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/mandiant/capa/). Report issues on our [issue tracker](https://github.com/mandiant/capa/issues) and contribute new rules at [capa-rules](https://github.com/mandiant/capa-rules/).
### Key changes to IDA Plugin
@@ -751,9 +878,9 @@ The IDA Pro integration is now distributed as a real plugin, instead of a script
- updates distributed PyPI/`pip install --upgrade` without touching your `%IDADIR%`
- generally doing thing the "right way"
How to get this new version? Its easy: download [capa_explorer.py](https://raw.githubusercontent.com/fireeye/capa/master/capa/ida/plugin/capa_explorer.py) to your IDA plugins directory and update your capa installation (incidentally, this is a good opportunity to migrate to `pip install flare-capa` instead of git checkouts). Now you should see the plugin listed in the `Edit > Plugins > FLARE capa explorer` menu in IDA.
How to get this new version? Its easy: download [capa_explorer.py](https://raw.githubusercontent.com/mandiant/capa/master/capa/ida/plugin/capa_explorer.py) to your IDA plugins directory and update your capa installation (incidentally, this is a good opportunity to migrate to `pip install flare-capa` instead of git checkouts). Now you should see the plugin listed in the `Edit > Plugins > FLARE capa explorer` menu in IDA.
Please refer to the plugin [readme](https://github.com/fireeye/capa/blob/master/capa/ida/plugin/README.md) for additional information on installing and using the IDA Pro plugin.
Please refer to the plugin [readme](https://github.com/mandiant/capa/blob/master/capa/ida/plugin/README.md) for additional information on installing and using the IDA Pro plugin.
Please open an issue in this repository if you notice anything weird.
@@ -798,8 +925,8 @@ Please open an issue in this repository if you notice anything weird.
### Raw diffs
- [capa v1.2.0...v1.3.0](https://github.com/fireeye/capa/compare/v1.2.0...v1.3.0)
- [capa-rules v1.2.0...v1.3.0](https://github.com/fireeye/capa-rules/compare/v1.2.0...v1.3.0)
- [capa v1.2.0...v1.3.0](https://github.com/mandiant/capa/compare/v1.2.0...v1.3.0)
- [capa-rules v1.2.0...v1.3.0](https://github.com/mandiant/capa-rules/compare/v1.2.0...v1.3.0)
## v1.2.0 (2020-08-31)
@@ -815,9 +942,9 @@ We received contributions from ten reverse engineers, including five new ones:
- @edeca
- @winniepe
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/fireeye/capa/).
Report issues on our [issue tracker](https://github.com/fireeye/capa/issues)
and contribute new rules at [capa-rules](https://github.com/fireeye/capa-rules/).
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/mandiant/capa/).
Report issues on our [issue tracker](https://github.com/mandiant/capa/issues)
and contribute new rules at [capa-rules](https://github.com/mandiant/capa-rules/).
### New features
@@ -896,8 +1023,8 @@ and contribute new rules at [capa-rules](https://github.com/fireeye/capa-rules/)
### Raw diffs
- [capa v1.1.0...v1.2.0](https://github.com/fireeye/capa/compare/v1.1.0...v1.2.0)
- [capa-rules v1.1.0...v1.2.0](https://github.com/fireeye/capa-rules/compare/v1.1.0...v1.2.0)
- [capa v1.1.0...v1.2.0](https://github.com/mandiant/capa/compare/v1.1.0...v1.2.0)
- [capa-rules v1.1.0...v1.2.0](https://github.com/mandiant/capa-rules/compare/v1.1.0...v1.2.0)
## v1.1.0 (2020-08-05)
@@ -910,7 +1037,7 @@ We received contributions from eight reverse engineers, including four new ones:
- @bitsofbinary
- @threathive
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/fireeye/capa/). Report issues on our [issue tracker](https://github.com/fireeye/capa/issues) and contribute new rules at [capa-rules](https://github.com/fireeye/capa-rules/).
Download a standalone binary below and checkout the readme [here on GitHub](https://github.com/mandiant/capa/). Report issues on our [issue tracker](https://github.com/mandiant/capa/issues) and contribute new rules at [capa-rules](https://github.com/mandiant/capa-rules/).
### New features
@@ -983,5 +1110,5 @@ Download a standalone binary below and checkout the readme [here on GitHub](http
### Raw diffs
- [capa v1.0.0...v1.1.0](https://github.com/fireeye/capa/compare/v1.0.0...v1.1.0)
- [capa-rules v1.0.0...v1.1.0](https://github.com/fireeye/capa-rules/compare/v1.0.0...v1.1.0)
- [capa v1.0.0...v1.1.0](https://github.com/mandiant/capa/compare/v1.0.0...v1.1.0)
- [capa-rules v1.0.0...v1.1.0](https://github.com/mandiant/capa-rules/compare/v1.0.0...v1.1.0)

View File

@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (C) 2020 FireEye, Inc.
Copyright (C) 2020 Mandiant, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,10 +1,10 @@
![capa](https://github.com/fireeye/capa/blob/master/.github/logo.png)
![capa](https://github.com/mandiant/capa/blob/master/.github/logo.png)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/flare-capa)](https://pypi.org/project/flare-capa)
[![Last release](https://img.shields.io/github/v/release/fireeye/capa)](https://github.com/fireeye/capa/releases)
[![Number of rules](https://img.shields.io/badge/rules-633-blue.svg)](https://github.com/fireeye/capa-rules)
[![CI status](https://github.com/fireeye/capa/workflows/CI/badge.svg)](https://github.com/fireeye/capa/actions?query=workflow%3ACI+event%3Apush+branch%3Amaster)
[![Downloads](https://img.shields.io/github/downloads/fireeye/capa/total)](https://github.com/fireeye/capa/releases)
[![Last release](https://img.shields.io/github/v/release/mandiant/capa)](https://github.com/mandiant/capa/releases)
[![Number of rules](https://img.shields.io/badge/rules-658-blue.svg)](https://github.com/mandiant/capa-rules)
[![CI status](https://github.com/mandiant/capa/workflows/CI/badge.svg)](https://github.com/mandiant/capa/actions?query=workflow%3ACI+event%3Apush+branch%3Amaster)
[![Downloads](https://img.shields.io/github/downloads/mandiant/capa/total)](https://github.com/mandiant/capa/releases)
[![License](https://img.shields.io/badge/license-Apache--2.0-green.svg)](LICENSE.txt)
capa detects capabilities in executable files.
@@ -66,11 +66,11 @@ $ capa.exe suspicious.exe
# download and usage
Download stable releases of the standalone capa binaries [here](https://github.com/fireeye/capa/releases). You can run the standalone binaries without installation. capa is a command line tool that should be run from the terminal.
Download stable releases of the standalone capa binaries [here](https://github.com/mandiant/capa/releases). You can run the standalone binaries without installation. capa is a command line tool that should be run from the terminal.
To use capa as a library or integrate with another tool, see [doc/installation.md](https://github.com/fireeye/capa/blob/master/doc/installation.md) for further setup instructions.
To use capa as a library or integrate with another tool, see [doc/installation.md](https://github.com/mandiant/capa/blob/master/doc/installation.md) for further setup instructions.
For more information about how to use capa, see [doc/usage.md](https://github.com/fireeye/capa/blob/master/doc/usage.md).
For more information about how to use capa, see [doc/usage.md](https://github.com/mandiant/capa/blob/master/doc/usage.md).
# example
@@ -91,7 +91,7 @@ $ capa.exe suspicious.exe -vv
...
execute shell command and capture output
namespace c2/shell
author matthew.williams@fireeye.com
author matthew.williams@mandiant.com
scope function
att&ck Execution::Command and Scripting Interpreter::Windows Command Shell [T1059.003]
references https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
@@ -127,7 +127,7 @@ rule:
meta:
name: hash data with CRC32
namespace: data-manipulation/checksum/crc32
author: moritz.raabe@fireeye.com
author: moritz.raabe@mandiant.com
scope: function
examples:
- 2D3EDC218A90F03089CC01715A9F047F:0x403CBD
@@ -142,24 +142,24 @@ rule:
- api: RtlComputeCrc32
```
The [github.com/fireeye/capa-rules](https://github.com/fireeye/capa-rules) repository contains hundreds of standard library rules that are distributed with capa.
The [github.com/mandiant/capa-rules](https://github.com/mandiant/capa-rules) repository contains hundreds of standard library rules that are distributed with capa.
Please learn to write rules and contribute new entries as you find interesting techniques in malware.
If you use IDA Pro, then you can use the [capa explorer](https://github.com/fireeye/capa/tree/master/capa/ida/plugin) plugin.
If you use IDA Pro, then you can use the [capa explorer](https://github.com/mandiant/capa/tree/master/capa/ida/plugin) plugin.
capa explorer helps you identify interesting areas of a program and build new capa rules using features extracted directly from your IDA Pro database.
![capa + IDA Pro integration](https://github.com/fireeye/capa/blob/master/doc/img/explorer_expanded.png)
![capa + IDA Pro integration](https://github.com/mandiant/capa/blob/master/doc/img/explorer_expanded.png)
# further information
## capa
- [Installation](https://github.com/fireeye/capa/blob/master/doc/installation.md)
- [Usage](https://github.com/fireeye/capa/blob/master/doc/usage.md)
- [Limitations](https://github.com/fireeye/capa/blob/master/doc/limitations.md)
- [Contributing Guide](https://github.com/fireeye/capa/blob/master/.github/CONTRIBUTING.md)
- [Installation](https://github.com/mandiant/capa/blob/master/doc/installation.md)
- [Usage](https://github.com/mandiant/capa/blob/master/doc/usage.md)
- [Limitations](https://github.com/mandiant/capa/blob/master/doc/limitations.md)
- [Contributing Guide](https://github.com/mandiant/capa/blob/master/.github/CONTRIBUTING.md)
## capa rules
- [capa-rules repository](https://github.com/fireeye/capa-rules)
- [capa-rules rule format](https://github.com/fireeye/capa-rules/blob/master/doc/format.md)
- [capa-rules repository](https://github.com/mandiant/capa-rules)
- [capa-rules rule format](https://github.com/mandiant/capa-rules/blob/master/doc/format.md)
## capa testfiles
The [capa-testfiles repository](https://github.com/fireeye/capa-testfiles) contains the data we use to test capa's code and rules
The [capa-testfiles repository](https://github.com/mandiant/capa-testfiles) contains the data we use to test capa's code and rules

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -8,11 +8,16 @@
import copy
import collections
from typing import Set, Dict, List, Tuple, Union, Mapping, Iterable
from typing import TYPE_CHECKING, Set, Dict, List, Tuple, Mapping, Iterable
import capa.rules
import capa.perf
import capa.features.common
from capa.features.common import Feature
from capa.features.common import Result, Feature
if TYPE_CHECKING:
# circular import, otherwise
import capa.rules
# a collection of features and the locations at which they are found.
#
@@ -45,15 +50,12 @@ class Statement:
def __repr__(self):
return str(self)
def evaluate(self, features: FeatureSet) -> "Result":
def evaluate(self, features: FeatureSet, short_circuit=True) -> Result:
"""
classes that inherit `Statement` must implement `evaluate`
args:
ctx (defaultdict[Feature, set[VA]])
returns:
Result
short_circuit (bool): if true, then statements like and/or/some may short circuit.
"""
raise NotImplementedError()
@@ -77,70 +79,70 @@ class Statement:
children[i] = new
class Result:
"""
represents the results of an evaluation of statements against features.
instances of this class should behave like a bool,
e.g. `assert Result(True, ...) == True`
instances track additional metadata about evaluation results.
they contain references to the statement node (e.g. an And statement),
as well as the children Result instances.
we need this so that we can render the tree of expressions and their results.
"""
def __init__(self, success: bool, statement: Union[Statement, Feature], children: List["Result"], locations=None):
"""
args:
success (bool)
statement (capa.engine.Statement or capa.features.Feature)
children (list[Result])
locations (iterable[VA])
"""
super(Result, self).__init__()
self.success = success
self.statement = statement
self.children = children
self.locations = locations if locations is not None else ()
def __eq__(self, other):
if isinstance(other, bool):
return self.success == other
return False
def __bool__(self):
return self.success
def __nonzero__(self):
return self.success
class And(Statement):
"""match if all of the children evaluate to True."""
"""
match if all of the children evaluate to True.
the order of evaluation is dictated by the property
`And.children` (type: List[Statement|Feature]).
a query optimizer may safely manipulate the order of these children.
"""
def __init__(self, children, description=None):
super(And, self).__init__(description=description)
self.children = children
def evaluate(self, ctx):
results = [child.evaluate(ctx) for child in self.children]
success = all(results)
return Result(success, self, results)
def evaluate(self, ctx, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.and"] += 1
if short_circuit:
results = []
for child in self.children:
result = child.evaluate(ctx, short_circuit=short_circuit)
results.append(result)
if not result:
# short circuit
return Result(False, self, results)
return Result(True, self, results)
else:
results = [child.evaluate(ctx, short_circuit=short_circuit) for child in self.children]
success = all(results)
return Result(success, self, results)
class Or(Statement):
"""match if any of the children evaluate to True."""
"""
match if any of the children evaluate to True.
the order of evaluation is dictated by the property
`Or.children` (type: List[Statement|Feature]).
a query optimizer may safely manipulate the order of these children.
"""
def __init__(self, children, description=None):
super(Or, self).__init__(description=description)
self.children = children
def evaluate(self, ctx):
results = [child.evaluate(ctx) for child in self.children]
success = any(results)
return Result(success, self, results)
def evaluate(self, ctx, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.or"] += 1
if short_circuit:
results = []
for child in self.children:
result = child.evaluate(ctx, short_circuit=short_circuit)
results.append(result)
if result:
# short circuit as soon as we hit one match
return Result(True, self, results)
return Result(False, self, results)
else:
results = [child.evaluate(ctx, short_circuit=short_circuit) for child in self.children]
success = any(results)
return Result(success, self, results)
class Not(Statement):
@@ -150,28 +152,55 @@ class Not(Statement):
super(Not, self).__init__(description=description)
self.child = child
def evaluate(self, ctx):
results = [self.child.evaluate(ctx)]
def evaluate(self, ctx, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.not"] += 1
results = [self.child.evaluate(ctx, short_circuit=short_circuit)]
success = not results[0]
return Result(success, self, results)
class Some(Statement):
"""match if at least N of the children evaluate to True."""
"""
match if at least N of the children evaluate to True.
the order of evaluation is dictated by the property
`Some.children` (type: List[Statement|Feature]).
a query optimizer may safely manipulate the order of these children.
"""
def __init__(self, count, children, description=None):
super(Some, self).__init__(description=description)
self.count = count
self.children = children
def evaluate(self, ctx):
results = [child.evaluate(ctx) for child in self.children]
# note that here we cast the child result as a bool
# because we've overridden `__bool__` above.
#
# we can't use `if child is True` because the instance is not True.
success = sum([1 for child in results if bool(child) is True]) >= self.count
return Result(success, self, results)
def evaluate(self, ctx, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.some"] += 1
if short_circuit:
results = []
satisfied_children_count = 0
for child in self.children:
result = child.evaluate(ctx, short_circuit=short_circuit)
results.append(result)
if result:
satisfied_children_count += 1
if satisfied_children_count >= self.count:
# short circuit as soon as we hit the threshold
return Result(True, self, results)
return Result(False, self, results)
else:
results = [child.evaluate(ctx, short_circuit=short_circuit) for child in self.children]
# note that here we cast the child result as a bool
# because we've overridden `__bool__` above.
#
# we can't use `if child is True` because the instance is not True.
success = sum([1 for child in results if bool(child) is True]) >= self.count
return Result(success, self, results)
class Range(Statement):
@@ -183,7 +212,10 @@ class Range(Statement):
self.min = min if min is not None else 0
self.max = max if max is not None else (1 << 64 - 1)
def evaluate(self, ctx):
def evaluate(self, ctx, **kwargs):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.range"] += 1
count = len(ctx.get(self.child, []))
if self.min == 0 and count == 0:
return Result(True, self, [])
@@ -208,7 +240,7 @@ class Subscope(Statement):
self.scope = scope
self.child = child
def evaluate(self, ctx):
def evaluate(self, ctx, **kwargs):
raise ValueError("cannot evaluate a subscope directly!")
@@ -247,15 +279,20 @@ def index_rule_matches(features: FeatureSet, rule: "capa.rules.Rule", locations:
def match(rules: List["capa.rules.Rule"], features: FeatureSet, va: int) -> Tuple[FeatureSet, MatchResults]:
"""
Args:
rules (List[capa.rules.Rule]): these must already be ordered topologically by dependency.
features (Mapping[capa.features.Feature, int]):
va (int): location of the features
match the given rules against the given features,
returning an updated set of features and the matches.
Returns:
Tuple[FeatureSet, MatchResults]: two-tuple with entries:
- set of features used for matching (which may be a superset of the given `features` argument, due to rule match features), and
- mapping from rule name to [(location of match, result object)]
the updated features are just like the input,
but extended to include the match features (e.g. names of rules that matched).
the given feature set is not modified; an updated copy is returned.
the given list of rules must be ordered topologically by dependency,
or else `match` statements will not be handled correctly.
this routine should be fairly optimized, but is not guaranteed to be the fastest matcher possible.
it has a particularly convenient signature: (rules, features) -> matches
other strategies can be imagined that match differently; implement these elsewhere.
specifically, this routine does "top down" matching of the given rules against the feature set.
"""
results = collections.defaultdict(list) # type: MatchResults
@@ -266,8 +303,18 @@ def match(rules: List["capa.rules.Rule"], features: FeatureSet, va: int) -> Tupl
features = collections.defaultdict(set, copy.copy(features))
for rule in rules:
res = rule.evaluate(features)
res = rule.evaluate(features, short_circuit=True)
if res:
# we first matched the rule with short circuiting enabled.
# this is much faster than without short circuiting.
# however, we want to collect all results thoroughly,
# so once we've found a match quickly,
# go back and capture results without short circuiting.
res = rule.evaluate(features, short_circuit=False)
# sanity check
assert bool(res) is True
results[rule.name].append((va, res))
# we need to update the current `features`
# because subsequent iterations of this loop may use newly added features,

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -10,9 +10,13 @@ import re
import codecs
import logging
import collections
from typing import Set, Dict, Union
from typing import TYPE_CHECKING, Set, Dict, List, Union
import capa.engine
if TYPE_CHECKING:
# circular import, otherwise
import capa.engine
import capa.perf
import capa.features
import capa.features.extractors.elf
@@ -46,6 +50,52 @@ def escape_string(s: str) -> str:
return s
class Result:
"""
represents the results of an evaluation of statements against features.
instances of this class should behave like a bool,
e.g. `assert Result(True, ...) == True`
instances track additional metadata about evaluation results.
they contain references to the statement node (e.g. an And statement),
as well as the children Result instances.
we need this so that we can render the tree of expressions and their results.
"""
def __init__(
self,
success: bool,
statement: Union["capa.engine.Statement", "Feature"],
children: List["Result"],
locations=None,
):
"""
args:
success (bool)
statement (capa.engine.Statement or capa.features.Feature)
children (list[Result])
locations (iterable[VA])
"""
super(Result, self).__init__()
self.success = success
self.statement = statement
self.children = children
self.locations = locations if locations is not None else ()
def __eq__(self, other):
if isinstance(other, bool):
return self.success == other
return False
def __bool__(self):
return self.success
def __nonzero__(self):
return self.success
class Feature:
def __init__(self, value: Union[str, int, bytes], bitness=None, description=None):
"""
@@ -96,8 +146,10 @@ class Feature:
def __repr__(self):
return str(self)
def evaluate(self, ctx: Dict["Feature", Set[int]]) -> "capa.engine.Result":
return capa.engine.Result(self in ctx, self, [], locations=ctx.get(self, []))
def evaluate(self, ctx: Dict["Feature", Set[int]], **kwargs) -> Result:
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature." + self.name] += 1
return Result(self in ctx, self, [], locations=ctx.get(self, []))
def freeze_serialize(self):
if self.bitness is not None:
@@ -140,7 +192,10 @@ class Substring(String):
super(Substring, self).__init__(value, description=description)
self.value = value
def evaluate(self, ctx):
def evaluate(self, ctx, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.substring"] += 1
# mapping from string value to list of locations.
# will unique the locations later on.
matches = collections.defaultdict(list)
@@ -155,6 +210,10 @@ class Substring(String):
if self.value in feature.value:
matches[feature.value].extend(locations)
if short_circuit:
# we found one matching string, thats sufficient to match.
# don't collect other matching strings in this mode.
break
if matches:
# finalize: defaultdict -> dict
@@ -170,9 +229,9 @@ class Substring(String):
# unlike other features, we cannot return put a reference to `self` directly in a `Result`.
# this is because `self` may match on many strings, so we can't stuff the matched value into it.
# instead, return a new instance that has a reference to both the substring and the matched values.
return capa.engine.Result(True, _MatchedSubstring(self, matches), [], locations=locations)
return Result(True, _MatchedSubstring(self, matches), [], locations=locations)
else:
return capa.engine.Result(False, _MatchedSubstring(self, None), [])
return Result(False, _MatchedSubstring(self, None), [])
def __str__(self):
return "substring(%s)" % self.value
@@ -225,7 +284,10 @@ class Regex(String):
"invalid regular expression: %s it should use Python syntax, try it at https://pythex.org" % value
)
def evaluate(self, ctx):
def evaluate(self, ctx, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.regex"] += 1
# mapping from string value to list of locations.
# will unique the locations later on.
matches = collections.defaultdict(list)
@@ -244,6 +306,10 @@ class Regex(String):
# so that they don't have to prefix/suffix their terms like: /.*foo.*/.
if self.re.search(feature.value):
matches[feature.value].extend(locations)
if short_circuit:
# we found one matching string, thats sufficient to match.
# don't collect other matching strings in this mode.
break
if matches:
# finalize: defaultdict -> dict
@@ -260,9 +326,9 @@ class Regex(String):
# this is because `self` may match on many strings, so we can't stuff the matched value into it.
# instead, return a new instance that has a reference to both the regex and the matched values.
# see #262.
return capa.engine.Result(True, _MatchedRegex(self, matches), [], locations=locations)
return Result(True, _MatchedRegex(self, matches), [], locations=locations)
else:
return capa.engine.Result(False, _MatchedRegex(self, None), [])
return Result(False, _MatchedRegex(self, None), [])
def __str__(self):
return "regex(string =~ %s)" % self.value
@@ -308,15 +374,18 @@ class Bytes(Feature):
super(Bytes, self).__init__(value, description=description)
self.value = value
def evaluate(self, ctx):
def evaluate(self, ctx, **kwargs):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.bytes"] += 1
for feature, locations in ctx.items():
if not isinstance(feature, (Bytes,)):
continue
if feature.value.startswith(self.value):
return capa.engine.Result(True, self, [], locations=locations)
return Result(True, self, [], locations=locations)
return capa.engine.Result(False, self, [])
return Result(False, self, [])
def get_value_str(self):
return hex_string(bytes_to_str(self.value))

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -40,11 +40,11 @@ def extract_os():
def extract_arch():
info = idaapi.get_inf_structure()
if info.procName == "metapc" and info.is_64bit():
if info.procname == "metapc" and info.is_64bit():
yield Arch(ARCH_AMD64), 0x0
elif info.procName == "metapc" and info.is_32bit():
elif info.procname == "metapc" and info.is_32bit():
yield Arch(ARCH_I386), 0x0
elif info.procName == "metapc":
elif info.procname == "metapc":
logger.debug("unsupported architecture: non-32-bit nor non-64-bit intel")
return
else:
@@ -52,5 +52,5 @@ def extract_arch():
# 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", info.procName)
logger.debug("unsupported architecture: %s", info.procname)
return

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -84,8 +84,12 @@ def extract_insn_number_features(f, bb, insn):
return
for operand in operands:
try:
yield Number(int(operand, 16)), insn.offset
yield Number(int(operand, 16), bitness=get_bitness(f.smda_report)), insn.offset
# The result of bitwise operations is calculated as though carried out
# in twos complement with an infinite number of sign bits
value = int(operand, 16) & ((1 << f.smda_report.bitness) - 1)
yield Number(value), insn.offset
yield Number(value, bitness=get_bitness(f.smda_report)), insn.offset
except:
continue

View File

@@ -1,6 +1,6 @@
# strings code from FLOSS, https://github.com/fireeye/flare-floss
# strings code from FLOSS, https://github.com/mandiant/flare-floss
#
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -8,12 +8,7 @@ json format:
'base address': int(base address),
'functions': {
int(function va): {
'basic blocks': {
int(basic block va): {
'instructions': [instruction va, ...]
},
...
},
int(basic block va): [int(instruction va), ...]
...
},
...
@@ -45,7 +40,7 @@ json format:
}
}
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -7,6 +7,7 @@
# See the License for the specific language governing permissions and limitations under the License.
import os
from typing import NoReturn
_hex = hex
@@ -30,3 +31,7 @@ def is_runtime_ida():
return False
else:
return True
def assert_never(value: NoReturn) -> NoReturn:
assert False, f"Unhandled value: {value} ({type(value).__name__})"

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -21,13 +21,6 @@ import capa.features.common
logger = logging.getLogger("capa")
# IDA version as returned by idaapi.get_kernel_version()
SUPPORTED_IDA_VERSIONS = (
"7.4",
"7.5",
"7.6",
)
# file type as returned by idainfo.file_type
SUPPORTED_FILE_TYPES = (
idaapi.f_PE,
@@ -45,13 +38,11 @@ def inform_user_ida_ui(message):
def is_supported_ida_version():
version = idaapi.get_kernel_version()
if version not in SUPPORTED_IDA_VERSIONS:
version = float(idaapi.get_kernel_version())
if version < 7.4 or version >= 8:
warning_msg = "This plugin does not support your IDA Pro version"
logger.warning(warning_msg)
logger.warning(
"Your IDA Pro version is: %s. Supported versions are: %s." % (version, ", ".join(SUPPORTED_IDA_VERSIONS))
)
logger.warning("Your IDA Pro version is: %s. Supported versions are: IDA >= 7.4 and IDA < 8.0." % version)
return False
return True
@@ -134,6 +125,12 @@ def collect_metadata():
"format": idaapi.get_file_type_name(),
"extractor": "ida",
"base_address": idaapi.get_imagebase(),
"layout": {
# this is updated after capabilities have been collected.
# will look like:
#
# "functions": { 0x401000: { "matched_basic_blocks": [ 0x401000, 0x401005, ... ] }, ... }
},
},
"version": capa.version.__version__,
}

View File

@@ -39,6 +39,7 @@ capa explorer supports Python versions >= 3.6.x and the following IDA Pro versio
* IDA 7.4
* IDA 7.5
* IDA 7.6 (caveat below)
* IDA 7.7
capa explorer is however limited to the Python versions supported by your IDA installation (which may not include all Python versions >= 3.6.x). Based on our testing the following matrix shows the Python versions supported
by each supported IDA version:
@@ -54,7 +55,7 @@ To use capa explorer with IDA 7.4 and Python 3.8.x you must follow the instructi
To use capa explorer with IDA 7.5 and Python 3.9.x you must follow the instructions provided by hex-rays [here](https://hex-rays.com/blog/python-3-9-support-for-ida-7-5/).
If you encounter issues with your specific setup, please open a new [Issue](https://github.com/fireeye/capa/issues).
If you encounter issues with your specific setup, please open a new [Issue](https://github.com/mandiant/capa/issues).
#### IDA 7.6 caveat: IDA 7.6sp1 or patch required
@@ -86,8 +87,8 @@ You can install capa explorer using the following steps:
```
$ pip install flare-capa
```
3. Download the [standard collection of capa rules](https://github.com/fireeye/capa-rules) (capa explorer needs capa rules to analyze a database)
4. Copy [capa_explorer.py](https://raw.githubusercontent.com/fireeye/capa/master/capa/ida/plugin/capa_explorer.py) to your IDA plugins directory
3. Download the [standard collection of capa rules](https://github.com/mandiant/capa-rules) (capa explorer needs capa rules to analyze a database)
4. Copy [capa_explorer.py](https://raw.githubusercontent.com/mandiant/capa/master/capa/ida/plugin/capa_explorer.py) to your IDA plugins directory
### Usage
@@ -99,7 +100,7 @@ You can install capa explorer using the following steps:
When running capa explorer for the first time you are prompted to select a file directory containing capa rules. The plugin conveniently
remembers your selection for future runs; you can change this selection and other default settings by clicking `Settings`. We recommend
downloading and using the [standard collection of capa rules](https://github.com/fireeye/capa-rules) when getting started with the plugin.
downloading and using the [standard collection of capa rules](https://github.com/mandiant/capa-rules) when getting started with the plugin.
#### Tips for Program Analysis
@@ -125,15 +126,15 @@ downloading and using the [standard collection of capa rules](https://github.com
## Development
capa explorer is packaged with capa so you will need to install capa locally for development. You can install capa locally by following the steps outlined in `Method 3: Inspecting the capa source code` of the [capa
installation guide](https://github.com/fireeye/capa/blob/master/doc/installation.md#method-3-inspecting-the-capa-source-code). Once installed, copy [capa_explorer.py](https://raw.githubusercontent.com/fireeye/capa/master/capa/ida/plugin/capa_explorer.py)
installation guide](https://github.com/mandiant/capa/blob/master/doc/installation.md#method-3-inspecting-the-capa-source-code). Once installed, copy [capa_explorer.py](https://raw.githubusercontent.com/mandiant/capa/master/capa/ida/plugin/capa_explorer.py)
to your plugins directory to install capa explorer in IDA.
### Components
capa explorer consists of two main components:
* An [feature extractor](https://github.com/fireeye/capa/tree/master/capa/features/extractors/ida) built on top of IDA's binary analysis engine
* This component uses IDAPython to extract [capa features](https://github.com/fireeye/capa-rules/blob/master/doc/format.md#extracted-features) from your IDBs such as strings,
* An [feature extractor](https://github.com/mandiant/capa/tree/master/capa/features/extractors/ida) built on top of IDA's binary analysis engine
* This component uses IDAPython to extract [capa features](https://github.com/mandiant/capa-rules/blob/master/doc/format.md#extracted-features) from your IDBs such as strings,
disassembly, and control flow; these extracted features are used by capa to find feature combinations that result in a rule match
* An [interactive user interface](https://github.com/fireeye/capa/tree/master/capa/ida/plugin) for displaying and exploring capa rule matches
* An [interactive user interface](https://github.com/mandiant/capa/tree/master/capa/ida/plugin) for displaying and exploring capa rule matches
* This component integrates the feature extractor and capa, providing an interactive user interface to dissect rule matches found by capa using features extracted directly from your IDBs

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -27,8 +27,8 @@ class CapaExplorerPlugin(idaapi.plugin_t):
wanted_name = PLUGIN_NAME
wanted_hotkey = "ALT-F5"
comment = "IDA Pro plugin for the FLARE team's capa tool to identify capabilities in executable files."
website = "https://github.com/fireeye/capa"
help = "See https://github.com/fireeye/capa/blob/master/doc/usage.md"
website = "https://github.com/mandiant/capa"
help = "See https://github.com/mandiant/capa/blob/master/doc/usage.md"
version = ""
flags = 0

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -627,7 +627,7 @@ class CapaExplorerForm(idaapi.PluginForm):
path = self.ask_user_directory()
if not path:
logger.warning(
"You must select a file directory containing capa rules before analysis can be run. The standard collection of capa rules can be downloaded from https://github.com/fireeye/capa-rules."
"You must select a file directory containing capa rules before analysis can be run. The standard collection of capa rules can be downloaded from https://github.com/mandiant/capa-rules."
)
return False
settings.user[CAPA_SETTINGS_RULE_PATH] = path
@@ -694,7 +694,7 @@ class CapaExplorerForm(idaapi.PluginForm):
)
logger.error("Failed to load rules from %s (error: %s).", settings.user[CAPA_SETTINGS_RULE_PATH], e)
logger.error(
"Make sure your file directory contains properly formatted capa rules. You can download the standard collection of capa rules from https://github.com/fireeye/capa-rules."
"Make sure your file directory contains properly formatted capa rules. You can download the standard collection of capa rules from https://github.com/mandiant/capa-rules."
)
settings.user[CAPA_SETTINGS_RULE_PATH] = ""
return False
@@ -751,6 +751,7 @@ class CapaExplorerForm(idaapi.PluginForm):
meta = capa.ida.helpers.collect_metadata()
capabilities, counts = capa.main.find_capabilities(self.ruleset_cache, extractor, disable_progress=True)
meta["analysis"].update(counts)
meta["analysis"]["layout"] = capa.main.compute_layout(self.ruleset_cache, extractor, capabilities)
except UserCancelledError:
logger.info("User cancelled analysis.")
return False

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -10,6 +10,7 @@ See the License for the specific language governing permissions and limitations
"""
import os
import sys
import time
import hashlib
import logging
import os.path
@@ -17,6 +18,7 @@ import argparse
import datetime
import textwrap
import itertools
import contextlib
import collections
from typing import Any, Dict, List, Tuple
@@ -26,6 +28,7 @@ import colorama
from pefile import PEFormatError
from elftools.common.exceptions import ELFError
import capa.perf
import capa.rules
import capa.engine
import capa.version
@@ -39,7 +42,7 @@ import capa.features.extractors
import capa.features.extractors.common
import capa.features.extractors.pefile
import capa.features.extractors.elffile
from capa.rules import Rule, RuleSet
from capa.rules import Rule, Scope, RuleSet
from capa.engine import FeatureSet, MatchResults
from capa.helpers import get_file_taste
from capa.features.extractors.base_extractor import FunctionHandle, FeatureExtractor
@@ -51,10 +54,28 @@ BACKEND_SMDA = "smda"
EXTENSIONS_SHELLCODE_32 = ("sc32", "raw32")
EXTENSIONS_SHELLCODE_64 = ("sc64", "raw64")
E_MISSING_RULES = -10
E_MISSING_FILE = -11
E_INVALID_RULE = -12
E_CORRUPT_FILE = -13
E_FILE_LIMITATION = -14
E_INVALID_SIG = -15
E_INVALID_FILE_TYPE = -16
E_INVALID_FILE_ARCH = -17
E_INVALID_FILE_OS = -18
E_UNSUPPORTED_IDA_VERSION = -19
logger = logging.getLogger("capa")
@contextlib.contextmanager
def timing(msg: str):
t0 = time.time()
yield
t1 = time.time()
logger.debug("perf: %s: %0.2fs", msg, t1 - t0)
def set_vivisect_log_level(level):
logging.getLogger("vivisect").setLevel(level)
logging.getLogger("vivisect.base").setLevel(level)
@@ -93,7 +114,7 @@ def find_function_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, f:
bb_features[feature].add(va)
function_features[feature].add(va)
_, matches = capa.engine.match(ruleset.basic_block_rules, bb_features, int(bb))
_, matches = ruleset.match(Scope.BASIC_BLOCK, bb_features, int(bb))
for rule_name, res in matches.items():
bb_matches[rule_name].extend(res)
@@ -101,7 +122,7 @@ def find_function_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, f:
for va, _ in res:
capa.engine.index_rule_matches(function_features, rule, [va])
_, function_matches = capa.engine.match(ruleset.function_rules, function_features, int(f))
_, function_matches = ruleset.match(Scope.FUNCTION, function_features, int(f))
return function_matches, bb_matches, len(function_features)
@@ -122,7 +143,7 @@ def find_file_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, functi
file_features.update(function_features)
_, matches = capa.engine.match(ruleset.file_rules, file_features, 0x0)
_, matches = ruleset.match(Scope.FILE, file_features, 0x0)
return matches, len(file_features)
@@ -582,10 +603,55 @@ def collect_metadata(argv, sample_path, rules_path, extractor):
"extractor": extractor.__class__.__name__,
"rules": rules_path,
"base_address": extractor.get_base_address(),
"layout": {
# this is updated after capabilities have been collected.
# will look like:
#
# "functions": { 0x401000: { "matched_basic_blocks": [ 0x401000, 0x401005, ... ] }, ... }
},
},
}
def compute_layout(rules, extractor, capabilities):
"""
compute a metadata structure that links basic blocks
to the functions in which they're found.
only collect the basic blocks at which some rule matched.
otherwise, we may pollute the json document with
a large amount of un-referenced data.
"""
functions_by_bb = {}
bbs_by_function = {}
for f in extractor.get_functions():
bbs_by_function[int(f)] = []
for bb in extractor.get_basic_blocks(f):
functions_by_bb[int(bb)] = int(f)
bbs_by_function[int(f)].append(int(bb))
matched_bbs = set()
for rule_name, matches in capabilities.items():
rule = rules[rule_name]
if rule.meta.get("scope") == capa.rules.BASIC_BLOCK_SCOPE:
for (addr, match) in matches:
assert addr in functions_by_bb
matched_bbs.add(addr)
layout = {
"functions": {
f: {
"matched_basic_blocks": [bb for bb in bbs if bb in matched_bbs]
# this object is open to extension in the future,
# such as with the function name, etc.
}
for f, bbs in bbs_by_function.items()
}
}
return layout
def install_common_args(parser, wanted=None):
"""
register a common set of command line arguments for re-use by main & scripts.
@@ -744,7 +810,7 @@ def handle_common_args(args):
logger.debug(" Using default embedded rules.")
logger.debug(" To provide your own rules, use the form `capa.exe -r ./path/to/rules/ /path/to/mal.exe`.")
logger.debug(" You can see the current default rule set here:")
logger.debug(" https://github.com/fireeye/capa-rules")
logger.debug(" https://github.com/mandiant/capa-rules")
logger.debug("-" * 80)
rules_path = os.path.join(get_default_root(), "rules")
@@ -756,7 +822,7 @@ def handle_common_args(args):
# so in this case, we require the user to use -r to specify the rule directory.
logger.error("default embedded rules not found! (maybe you installed capa as a library?)")
logger.error("provide your own rule set via the `-r` option.")
return -1
return E_MISSING_RULES
else:
rules_path = args.rules
logger.debug("using rules path: %s", rules_path)
@@ -792,7 +858,7 @@ def main(argv=None):
"""
By default, capa uses a default set of embedded rules.
You can see the rule set here:
https://github.com/fireeye/capa-rules
https://github.com/mandiant/capa-rules
To provide your own rule set, use the `-r` flag:
capa --rules /path/to/rules suspicious.exe
@@ -822,7 +888,9 @@ def main(argv=None):
install_common_args(parser, {"sample", "format", "backend", "signatures", "rules", "tag"})
parser.add_argument("-j", "--json", action="store_true", help="emit JSON instead of text")
args = parser.parse_args(args=argv)
handle_common_args(args)
ret = handle_common_args(args)
if ret is not None and ret != 0:
return ret
try:
taste = get_file_taste(args.sample)
@@ -830,11 +898,12 @@ def main(argv=None):
# per our research there's not a programmatic way to render the IOError with non-ASCII filename unless we
# handle the IOError separately and reach into the args
logger.error("%s", e.args[0])
return -1
return E_MISSING_FILE
try:
rules = get_rules(args.rules, disable_progress=args.quiet)
rules = capa.rules.RuleSet(rules)
logger.debug(
"successfully loaded %s rules",
# during the load of the RuleSet, we extract subscope statements into their own rules
@@ -850,7 +919,7 @@ def main(argv=None):
logger.debug(" %d. %s", i, r)
except (IOError, capa.rules.InvalidRule, capa.rules.InvalidRuleSet) as e:
logger.error("%s", str(e))
return -1
return E_INVALID_RULE
file_extractor = None
if args.format == "pe" or (args.format == "auto" and taste.startswith(b"MZ")):
@@ -862,24 +931,24 @@ def main(argv=None):
file_extractor = capa.features.extractors.pefile.PefileFeatureExtractor(args.sample)
except PEFormatError as e:
logger.error("Input file '%s' is not a valid PE file: %s", args.sample, str(e))
return -1
return E_CORRUPT_FILE
elif args.format == "elf" or (args.format == "auto" and taste.startswith(b"\x7fELF")):
try:
file_extractor = capa.features.extractors.elffile.ElfFeatureExtractor(args.sample)
except (ELFError, OverflowError) as e:
logger.error("Input file '%s' is not a valid ELF file: %s", args.sample, str(e))
return -1
return E_CORRUPT_FILE
if file_extractor:
try:
pure_file_capabilities, _ = find_file_capabilities(rules, file_extractor, {})
except PEFormatError as e:
logger.error("Input file '%s' is not a valid PE file: %s", args.sample, str(e))
return -1
return E_CORRUPT_FILE
except (ELFError, OverflowError) as e:
logger.error("Input file '%s' is not a valid ELF file: %s", args.sample, str(e))
return -1
return E_CORRUPT_FILE
# file limitations that rely on non-file scope won't be detected here.
# nor on FunctionName features, because pefile doesn't support this.
@@ -888,7 +957,7 @@ def main(argv=None):
# do show the output in verbose mode, though.
if not (args.verbose or args.vverbose or args.json):
logger.debug("file limitation short circuit, won't analyze fully.")
return -1
return E_FILE_LIMITATION
try:
if args.format == "pe" or (args.format == "auto" and taste.startswith(b"MZ")):
@@ -898,7 +967,7 @@ def main(argv=None):
logger.debug("skipping library code matching: only have PE signatures")
except (IOError) as e:
logger.error("%s", str(e))
return -1
return E_INVALID_SIG
if (args.format == "freeze") or (args.format == "auto" and capa.features.freeze.is_freeze(taste)):
format = "freeze"
@@ -926,14 +995,14 @@ def main(argv=None):
)
logger.error(" If you don't know the input file type, you can try using the `file` utility to guess it.")
logger.error("-" * 80)
return -1
return E_INVALID_FILE_TYPE
except UnsupportedArchError:
logger.error("-" * 80)
logger.error(" Input file does not appear to target a supported architecture.")
logger.error(" ")
logger.error(" capa currently only supports analyzing x86 (32- and 64-bit).")
logger.error("-" * 80)
return -1
return E_INVALID_FILE_ARCH
except UnsupportedOSError:
logger.error("-" * 80)
logger.error(" Input file does not appear to target a supported OS.")
@@ -942,18 +1011,19 @@ def main(argv=None):
" capa currently only supports analyzing executables for some operating systems (including Windows and Linux)."
)
logger.error("-" * 80)
return -1
return E_INVALID_FILE_OS
meta = collect_metadata(argv, args.sample, args.rules, extractor)
capabilities, counts = find_capabilities(rules, extractor, disable_progress=args.quiet)
meta["analysis"].update(counts)
meta["analysis"]["layout"] = compute_layout(rules, extractor, capabilities)
if has_file_limitation(rules, capabilities):
# bail if capa encountered file limitation e.g. a packed binary
# do show the output in verbose mode, though.
if not (args.verbose or args.vverbose or args.json):
return -1
return E_FILE_LIMITATION
if args.json:
print(capa.render.json.render(meta, rules, capabilities))
@@ -980,16 +1050,16 @@ def ida_main():
logging.getLogger().setLevel(logging.INFO)
if not capa.ida.helpers.is_supported_ida_version():
return -1
return E_UNSUPPORTED_IDA_VERSION
if not capa.ida.helpers.is_supported_file_type():
return -1
return E_INVALID_FILE_TYPE
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/fireeye/capa-rules")
logger.debug(" https://github.com/mandiant/capa-rules")
logger.debug("-" * 80)
rules_path = os.path.join(get_default_root(), "rules")

70
capa/optimizer.py Normal file
View File

@@ -0,0 +1,70 @@
import logging
import capa.engine as ceng
import capa.features.common
logger = logging.getLogger(__name__)
def get_node_cost(node):
if isinstance(node, (capa.features.common.OS, capa.features.common.Arch, capa.features.common.Format)):
# we assume these are the most restrictive features:
# authors commonly use them at the start of rules to restrict the category of samples to inspect
return 0
# elif "everything else":
# return 1
#
# this should be all hash-lookup features.
# see below.
elif isinstance(node, (capa.features.common.Substring, capa.features.common.Regex, capa.features.common.Bytes)):
# substring and regex features require a full scan of each string
# which we anticipate is more expensive then a hash lookup feature (e.g. mnemonic or count).
#
# TODO: compute the average cost of these feature relative to hash feature
# and adjust the factor accordingly.
return 2
elif isinstance(node, (ceng.Not, ceng.Range)):
# the cost of these nodes are defined by the complexity of their single child.
return 1 + get_node_cost(node.child)
elif isinstance(node, (ceng.And, ceng.Or, ceng.Some)):
# the cost of these nodes is the full cost of their children
# as this is the worst-case scenario.
return 1 + sum(map(get_node_cost, node.children))
else:
# this should be all hash-lookup features.
# we give this a arbitrary weight of 1.
# the only thing more "important" than this is checking OS/Arch/Format.
return 1
def optimize_statement(statement):
# this routine operates in-place
if isinstance(statement, (ceng.And, ceng.Or, ceng.Some)):
# has .children
statement.children = sorted(statement.children, key=lambda n: get_node_cost(n))
return
elif isinstance(statement, (ceng.Not, ceng.Range)):
# has .child
optimize_statement(statement.child)
return
else:
# appears to be "simple"
return
def optimize_rule(rule):
# this routine operates in-place
optimize_statement(rule.statement)
def optimize_rules(rules):
logger.debug("optimizing %d rules", len(rules))
for rule in rules:
optimize_rule(rule)
return rules

10
capa/perf.py Normal file
View File

@@ -0,0 +1,10 @@
import collections
from typing import Dict
# this structure is unstable and may change before the next major release.
counters: Dict[str, int] = collections.Counter()
def reset():
global counters
counters = collections.Counter()

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -60,6 +60,8 @@ def capability_rules(doc):
continue
if rule["meta"].get("maec/analysis-conclusion-ov"):
continue
if rule["meta"].get("maec/malware-family"):
continue
if rule["meta"].get("maec/malware-category"):
continue
if rule["meta"].get("maec/malware-category-ov"):

View File

@@ -3,7 +3,7 @@ example::
send data
namespace communication
author william.ballenthin@fireeye.com
author william.ballenthin@mandiant.com
description all known techniques for sending data to a potential C2 server
scope function
examples BFB9B5391A13D0AFD787E87AB90F14F5:0x13145D60
@@ -14,7 +14,7 @@ example::
0x10003415
0x10003797
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -193,7 +193,7 @@ def render_rules(ostream, doc):
## rules
check for OutputDebugString error
namespace anti-analysis/anti-debugging/debugger-detection
author michael.hunhoff@fireeye.com
author michael.hunhoff@mandiant.com
scope function
mbc Anti-Behavioral Analysis::Detect Debugger::OutputDebugString
examples Practical Malware Analysis Lab 16-02.exe_:0x401020
@@ -203,6 +203,11 @@ def render_rules(ostream, doc):
api: kernel32.GetLastError @ 0x10004A87
api: kernel32.OutputDebugString @ 0x10004767, 0x10004787, 0x10004816, 0x10004895
"""
functions_by_bb = {}
for function, info in doc["meta"]["analysis"]["layout"]["functions"].items():
for bb in info["matched_basic_blocks"]:
functions_by_bb[bb] = function
had_match = False
for rule in rutils.capability_rules(doc):
count = len(rule["matches"])
@@ -247,7 +252,12 @@ def render_rules(ostream, doc):
for location, match in sorted(doc["rules"][rule["meta"]["name"]]["matches"].items()):
ostream.write(rule["meta"]["scope"])
ostream.write(" @ ")
ostream.writeln(rutils.hex(location))
ostream.write(rutils.hex(location))
if rule["meta"]["scope"] == capa.rules.BASIC_BLOCK_SCOPE:
ostream.write(" in function " + rutils.hex(functions_by_bb[location]))
ostream.write("\n")
render_match(ostream, match, indent=1)
ostream.write("\n")

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -14,6 +14,9 @@ import logging
import binascii
import functools
import collections
from enum import Enum
from capa.helpers import assert_never
try:
from functools import lru_cache
@@ -22,13 +25,15 @@ except ImportError:
# https://github.com/python/mypy/issues/1153
from backports.functools_lru_cache import lru_cache # type: ignore
from typing import Any, Dict, List, Union, Iterator
from typing import Any, Set, Dict, List, Tuple, Union, Iterator
import yaml
import ruamel.yaml
import capa.perf
import capa.engine as ceng
import capa.features
import capa.optimizer
import capa.features.file
import capa.features.insn
import capa.features.common
@@ -46,6 +51,7 @@ META_KEYS = (
"rule-category",
"maec/analysis-conclusion",
"maec/analysis-conclusion-ov",
"maec/malware-family",
"maec/malware-category",
"maec/malware-category-ov",
"author",
@@ -64,9 +70,15 @@ META_KEYS = (
HIDDEN_META_KEYS = ("capa/nursery", "capa/path")
FILE_SCOPE = "file"
FUNCTION_SCOPE = "function"
BASIC_BLOCK_SCOPE = "basic block"
class Scope(str, Enum):
FILE = "file"
FUNCTION = "function"
BASIC_BLOCK = "basic block"
FILE_SCOPE = Scope.FILE.value
FUNCTION_SCOPE = Scope.FUNCTION.value
BASIC_BLOCK_SCOPE = Scope.BASIC_BLOCK.value
SUPPORTED_FEATURES = {
@@ -619,8 +631,10 @@ class Rule:
for new_rule in self._extract_subscope_rules_rec(self.statement):
yield new_rule
def evaluate(self, features: FeatureSet):
return self.statement.evaluate(features)
def evaluate(self, features: FeatureSet, short_circuit=True):
capa.perf.counters["evaluate.feature"] += 1
capa.perf.counters["evaluate.feature.rule"] += 1
return self.statement.evaluate(features, short_circuit=short_circuit)
@classmethod
def from_dict(cls, d, definition):
@@ -958,12 +972,23 @@ class RuleSet:
if len(rules) == 0:
raise InvalidRuleSet("no rules selected")
rules = capa.optimizer.optimize_rules(rules)
self.file_rules = self._get_rules_for_scope(rules, FILE_SCOPE)
self.function_rules = self._get_rules_for_scope(rules, FUNCTION_SCOPE)
self.basic_block_rules = self._get_rules_for_scope(rules, BASIC_BLOCK_SCOPE)
self.rules = {rule.name: rule for rule in rules}
self.rules_by_namespace = index_rules_by_namespace(rules)
# unstable
(self._easy_file_rules_by_feature, self._hard_file_rules) = self._index_rules_by_feature(self.file_rules)
(self._easy_function_rules_by_feature, self._hard_function_rules) = self._index_rules_by_feature(
self.function_rules
)
(self._easy_basic_block_rules_by_feature, self._hard_basic_block_rules) = self._index_rules_by_feature(
self.basic_block_rules
)
def __len__(self):
return len(self.rules)
@@ -973,6 +998,141 @@ class RuleSet:
def __contains__(self, rulename):
return rulename in self.rules
@staticmethod
def _index_rules_by_feature(rules) -> Tuple[Dict[Feature, Set[str]], List[str]]:
"""
split the given rules into two structures:
- "easy rules" are indexed by feature,
such that you can quickly find the rules that contain a given feature.
- "hard rules" are those that contain substring/regex/bytes features or match statements.
these continue to be ordered topologically.
a rule evaluator can use the "easy rule" index to restrict the
candidate rules that might match a given set of features.
at this time, a rule evaluator can't do anything special with
the "hard rules". it must still do a full top-down match of each
rule, in topological order.
"""
# we'll do a couple phases:
#
# 1. recursively visit all nodes in all rules,
# a. indexing all features
# b. recording the types of features found per rule
# 2. compute the easy and hard rule sets
# 3. remove hard rules from the rules-by-feature index
# 4. construct the topologically ordered list of hard rules
rules_with_easy_features: Set[str] = set()
rules_with_hard_features: Set[str] = set()
rules_by_feature: Dict[Feature, Set[str]] = collections.defaultdict(set)
def rec(rule_name: str, node: Union[Feature, Statement]):
"""
walk through a rule's logic tree, indexing the easy and hard rules,
and the features referenced by easy rules.
"""
if isinstance(
node,
(
# these are the "hard features"
# substring: scanning feature
capa.features.common.Substring,
# regex: scanning feature
capa.features.common.Regex,
# bytes: scanning feature
capa.features.common.Bytes,
# match: dependency on another rule,
# which we have to evaluate first,
# and is therefore tricky.
capa.features.common.MatchedRule,
),
):
# hard feature: requires scan or match lookup
rules_with_hard_features.add(rule_name)
elif isinstance(node, capa.features.common.Feature):
# easy feature: hash lookup
rules_with_easy_features.add(rule_name)
rules_by_feature[node].add(rule_name)
elif isinstance(node, (ceng.Not)):
# `not:` statements are tricky to deal with.
#
# first, features found under a `not:` should not be indexed,
# because they're not wanted to be found.
# second, `not:` can be nested under another `not:`, or two, etc.
# third, `not:` at the root or directly under an `or:`
# means the rule will match against *anything* not specified there,
# which is a difficult set of things to compute and index.
#
# so, if a rule has a `not:` statement, its hard.
# as of writing, this is an uncommon statement, with only 6 instances in 740 rules.
rules_with_hard_features.add(rule_name)
elif isinstance(node, (ceng.Some)) and node.count == 0:
# `optional:` and `0 or more:` are tricky to deal with.
#
# when a subtree is optional, it may match, but not matching
# doesn't have any impact either.
# now, our rule authors *should* not put this under `or:`
# and this is checked by the linter,
# but this could still happen (e.g. private rule set without linting)
# and would be hard to trace down.
#
# so better to be safe than sorry and consider this a hard case.
rules_with_hard_features.add(rule_name)
elif isinstance(node, (ceng.Range)) and node.min == 0:
# `count(foo): 0 or more` are tricky to deal with.
# because the min is 0,
# this subtree *can* match just about any feature
# (except the given one)
# which is a difficult set of things to compute and index.
rules_with_hard_features.add(rule_name)
elif isinstance(node, (ceng.Range)):
rec(rule_name, node.child)
elif isinstance(node, (ceng.And, ceng.Or, ceng.Some)):
for child in node.children:
rec(rule_name, child)
elif isinstance(node, ceng.Statement):
# unhandled type of statement.
# this should only happen if a new subtype of `Statement`
# has since been added to capa.
#
# ideally, we'd like to use mypy for exhaustiveness checking
# for all the subtypes of `Statement`.
# but, as far as i can tell, mypy does not support this type
# of checking.
#
# in a way, this makes some intuitive sense:
# the set of subtypes of type A is unbounded,
# because any user might come along and create a new subtype B,
# so mypy can't reason about this set of types.
assert False, f"Unhandled value: {node} ({type(node).__name__})"
else:
# programming error
assert_never(node)
for rule in rules:
rule_name = rule.meta["name"]
root = rule.statement
rec(rule_name, root)
# if a rule has a hard feature,
# dont consider it easy, and therefore,
# don't index any of its features.
#
# otherwise, its an easy rule, and index its features
for rules_with_feature in rules_by_feature.values():
rules_with_feature.difference_update(rules_with_hard_features)
easy_rules_by_feature = rules_by_feature
# `rules` is already topologically ordered,
# so extract our hard set into the topological ordering.
hard_rules = []
for rule in rules:
if rule.meta["name"] in rules_with_hard_features:
hard_rules.append(rule.meta["name"])
return (easy_rules_by_feature, hard_rules)
@staticmethod
def _get_rules_for_scope(rules, scope):
"""
@@ -1035,3 +1195,66 @@ class RuleSet:
rules_filtered.update(set(capa.rules.get_rules_and_dependencies(rules, rule.name)))
break
return RuleSet(list(rules_filtered))
def match(self, scope: Scope, features: FeatureSet, va: int) -> Tuple[FeatureSet, ceng.MatchResults]:
"""
match rules from this ruleset at the given scope against the given features.
this routine should act just like `capa.engine.match`,
except that it may be more performant.
"""
easy_rules_by_feature = {}
if scope is Scope.FILE:
easy_rules_by_feature = self._easy_file_rules_by_feature
hard_rule_names = self._hard_file_rules
elif scope is Scope.FUNCTION:
easy_rules_by_feature = self._easy_function_rules_by_feature
hard_rule_names = self._hard_function_rules
elif scope is Scope.BASIC_BLOCK:
easy_rules_by_feature = self._easy_basic_block_rules_by_feature
hard_rule_names = self._hard_basic_block_rules
else:
assert_never(scope)
candidate_rule_names = set()
for feature in features:
easy_rule_names = easy_rules_by_feature.get(feature)
if easy_rule_names:
candidate_rule_names.update(easy_rule_names)
# first, match against the set of rules that have at least one
# feature shared with our feature set.
candidate_rules = [self.rules[name] for name in candidate_rule_names]
features2, easy_matches = ceng.match(candidate_rules, features, va)
# note that we've stored the updated feature set in `features2`.
# this contains a superset of the features in `features`;
# it contains additional features for any easy rule matches.
# we'll pass this feature set to hard rule matching, since one
# of those rules might rely on an easy rule match.
#
# the updated feature set from hard matching will go into `features3`.
# this is a superset of `features2` is a superset of `features`.
# ultimately, this is what we'll return to the caller.
#
# in each case, we could have assigned the updated feature set back to `features`,
# but this is slightly more explicit how we're tracking the data.
# now, match against (topologically ordered) list of rules
# that we can't really make any guesses about.
# these are rules with hard features, like substring/regex/bytes and match statements.
hard_rules = [self.rules[name] for name in hard_rule_names]
features3, hard_matches = ceng.match(hard_rules, features2, va)
# note that above, we probably are skipping matching a bunch of
# rules that definitely would never hit.
# specifically, "easy rules" that don't share any features with
# feature set.
# MatchResults doesn't technically have an .update() method
# but a dict does.
matches = {} # type: ignore
matches.update(easy_matches)
matches.update(hard_matches)
return (features3, matches)

View File

@@ -1 +1 @@
__version__ = "3.0.1"
__version__ = "3.1.0"

View File

@@ -1,8 +1,8 @@
# Installation
You can install capa in a few different ways. First, if you simply want to use capa, just download the [standalone binary](https://github.com/fireeye/capa/releases). If you want to use capa as a Python library, you can install the package directly from GitHub using `pip`. If you'd like to contribute patches or features to capa, you can work with a local copy of the source code.
You can install capa in a few different ways. First, if you simply want to use capa, just download the [standalone binary](https://github.com/mandiant/capa/releases). If you want to use capa as a Python library, you can install the package directly from GitHub using `pip`. If you'd like to contribute patches or features to capa, you can work with a local copy of the source code.
## Method 1: Standalone installation
If you simply want to use capa, use the standalone binaries we host on GitHub: https://github.com/fireeye/capa/releases. These binary executable files contain all the source code, Python interpreter, and associated resources needed to make capa run. This means you can run it without any installation! Just invoke the file using your terminal shell to see the help documentation.
If you simply want to use capa, use the standalone binaries we host on GitHub: https://github.com/mandiant/capa/releases. These binary executable files contain all the source code, Python interpreter, and associated resources needed to make capa run. This means you can run it without any installation! Just invoke the file using your terminal shell to see the help documentation.
We use PyInstaller to create these packages.
@@ -26,8 +26,8 @@ To install capa as a Python library use `pip` to fetch the `flare-capa` module.
#### *Note*:
This method is appropriate for integrating capa in an existing project.
This technique doesn't pull the default rule set, so you should check it out separately from [capa-rules](https://github.com/fireeye/capa-rules/) and pass the directory to the entrypoint using `-r` or set the rules path in the IDA Pro plugin.
This technique also doesn't set up the default library identification [signatures](https://github.com/fireeye/capa/tree/master/sigs). You can pass the signature directory using the `-s` argument.
This technique doesn't pull the default rule set, so you should check it out separately from [capa-rules](https://github.com/mandiant/capa-rules/) and pass the directory to the entrypoint using `-r` or set the rules path in the IDA Pro plugin.
This technique also doesn't set up the default library identification [signatures](https://github.com/mandiant/capa/tree/master/sigs). You can pass the signature directory using the `-s` argument.
For example, to run capa with both a rule path and a signature path:
capa -r /path/to/capa-rules -s /path/to/capa-sigs suspicious.exe
@@ -44,16 +44,16 @@ If you'd like to review and modify the capa source code, you'll need to check it
### 1. Check out source code
Next, clone the capa git repository.
We use submodules to separate [code](https://github.com/fireeye/capa), [rules](https://github.com/fireeye/capa-rules), and [test data](https://github.com/fireeye/capa-testfiles).
We use submodules to separate [code](https://github.com/mandiant/capa), [rules](https://github.com/mandiant/capa-rules), and [test data](https://github.com/mandiant/capa-testfiles).
To clone everything use the `--recurse-submodules` option:
- CAUTION: The capa testfiles repository contains many malware samples. If you pull down everything using this method, you may want to install to a directory that won't trigger your anti-virus software.
- `$ git clone --recurse-submodules https://github.com/fireeye/capa.git /local/path/to/src` (HTTPS)
- `$ git clone --recurse-submodules git@github.com:fireeye/capa.git /local/path/to/src` (SSH)
- `$ git clone --recurse-submodules https://github.com/mandiant/capa.git /local/path/to/src` (HTTPS)
- `$ git clone --recurse-submodules git@github.com:mandiant/capa.git /local/path/to/src` (SSH)
To only get the source code and our provided rules (common), follow these steps:
- clone repository
- `$ git clone https://github.com/fireeye/capa.git /local/path/to/src` (HTTPS)
- `$ git clone git@github.com:fireeye/capa.git /local/path/to/src` (SSH)
- `$ git clone https://github.com/mandiant/capa.git /local/path/to/src` (HTTPS)
- `$ git clone git@github.com:mandiant/capa.git /local/path/to/src` (SSH)
- `$ cd /local/path/to/src`
- `$ git submodule update --init rules`
@@ -87,7 +87,7 @@ We use the following tools to ensure consistent code style and formatting:
- [black](https://github.com/psf/black) code formatter, with `-l 120`
- [isort 5](https://pypi.org/project/isort/) code formatter, with `--profile black --length-sort --line-width 120`
- [dos2unix](https://linux.die.net/man/1/dos2unix) for UNIX-style LF newlines
- [capafmt](https://github.com/fireeye/capa/blob/master/scripts/capafmt.py) rule formatter
- [capafmt](https://github.com/mandiant/capa/blob/master/scripts/capafmt.py) rule formatter
To install these development dependencies, run:

View File

@@ -46,6 +46,6 @@ We need more practical use cases and test samples to justify the additional work
# ATT&CK, MAEC, MBC, and other capability tagging
capa uses namespaces to group capabilities (see https://github.com/fireeye/capa-rules/tree/master#namespace-organization).
capa uses namespaces to group capabilities (see https://github.com/mandiant/capa-rules/tree/master#namespace-organization).
The `rule.meta` field also supports `att&ck`, `mbc`, and `maec` fields to associate rules with the respective taxonomy (see https://github.com/fireeye/capa-rules/blob/master/doc/format.md#meta-block).
The `rule.meta` field also supports `att&ck`, `mbc`, and `maec` fields to associate rules with the respective taxonomy (see https://github.com/mandiant/capa-rules/blob/master/doc/format.md#meta-block).

View File

@@ -1,13 +1,13 @@
# Release checklist
- [ ] Ensure all [milestoned issues/PRs](https://github.com/fireeye/capa/milestones) are addressed, or reassign to a new milestone.
- [ ] Add the `dont merge` label to all PRs that are close to be ready to merge (or merge them if they are ready) in [capa](https://github.com/fireeye/capa/pulls) and [capa-rules](https://github.com/fireeye/capa-rules/pulls).
- [ ] Ensure the [CI workflow succeeds in master](https://github.com/fireeye/capa/actions/workflows/tests.yml?query=branch%3Amaster).
- [ ] Ensure all [milestoned issues/PRs](https://github.com/mandiant/capa/milestones) are addressed, or reassign to a new milestone.
- [ ] Add the `dont merge` label to all PRs that are close to be ready to merge (or merge them if they are ready) in [capa](https://github.com/mandiant/capa/pulls) and [capa-rules](https://github.com/mandiant/capa-rules/pulls).
- [ ] Ensure the [CI workflow succeeds in master](https://github.com/mandiant/capa/actions/workflows/tests.yml?query=branch%3Amaster).
- [ ] Ensure that `python scripts/lint.py rules/ --thorough` succeeds (only `missing examples` offenses are allowed in the nursery).
- [ ] Review changes
- capa https://github.com/fireeye/capa/compare/\<last-release\>...master
- capa-rules https://github.com/fireeye/capa-rules/compare/\<last-release>\...master
- [ ] Update [CHANGELOG.md](https://github.com/fireeye/capa/blob/master/CHANGELOG.md)
- capa https://github.com/mandiant/capa/compare/\<last-release\>...master
- capa-rules https://github.com/mandiant/capa-rules/compare/\<last-release>\...master
- [ ] Update [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md)
- Do not forget to add a nice introduction thanking contributors
- Remember that we need a major release if we introduce breaking changes
- Sections: see template below
@@ -31,13 +31,13 @@
### Development
### Raw diffs
- [capa <release>...master](https://github.com/fireeye/capa/compare/<release>...master)
- [capa-rules <release>...master](https://github.com/fireeye/capa-rules/compare/<release>...master)
- [capa <release>...master](https://github.com/mandiant/capa/compare/<release>...master)
- [capa-rules <release>...master](https://github.com/mandiant/capa-rules/compare/<release>...master)
```
- [ ] Update [capa/version.py](https://github.com/fireeye/capa/blob/master/capa/version.py)
- [ ] Create a PR with the updated [CHANGELOG.md](https://github.com/fireeye/capa/blob/master/CHANGELOG.md) and [capa/version.py](https://github.com/fireeye/capa/blob/master/capa/version.py). Copy this checklist in the PR description.
- [ ] After PR review, merge the PR and [create the release in GH](https://github.com/fireeye/capa/releases/new) using text from the [CHANGELOG.md](https://github.com/fireeye/capa/blob/master/CHANGELOG.md).
- [ ] Verify GH actions [upload artifacts](https://github.com/fireeye/capa/releases), [publish to PyPI](https://pypi.org/project/flare-capa) and [create a tag in capa rules](https://github.com/fireeye/capa-rules/tags) upon completion.
- [ ] Update [capa/version.py](https://github.com/mandiant/capa/blob/master/capa/version.py)
- [ ] Create a PR with the updated [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md) and [capa/version.py](https://github.com/mandiant/capa/blob/master/capa/version.py). Copy this checklist in the PR description.
- [ ] After PR review, merge the PR and [create the release in GH](https://github.com/mandiant/capa/releases/new) using text from the [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md).
- [ ] Verify GH actions [upload artifacts](https://github.com/mandiant/capa/releases), [publish to PyPI](https://pypi.org/project/flare-capa) and [create a tag in capa rules](https://github.com/mandiant/capa-rules/tags) upon completion.
- [ ] [Spread the word](https://twitter.com)
- [ ] Update internal service

2
rules

Submodule rules updated: f04491001d...954f22acd8

View File

@@ -47,7 +47,7 @@ usage:
parallelism factor
--no-mp disable subprocesses
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -129,6 +129,7 @@ def get_capa_results(args):
meta = capa.main.collect_metadata("", path, "", extractor)
capabilities, counts = capa.main.find_capabilities(rules, extractor, disable_progress=True)
meta["analysis"].update(counts)
meta["analysis"]["layout"] = capa.main.compute_layout(rules, extractor, capabilities)
return {
"path": path,

View File

@@ -21,7 +21,7 @@ optional arguments:
-t TAG, --tag TAG filter on rule meta field values
Copyright (C) 2020, 2021 Arnim Rupp (@ruppde) and FireEye, Inc. All Rights Reserved.
Copyright (C) 2020, 2021 Arnim Rupp (@ruppde) and Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -57,8 +57,8 @@ var_names = ["".join(letters) for letters in itertools.product(string.ascii_lowe
unsupported = ["characteristic", "mnemonic", "offset", "subscope", "Range"]
# TODO shorten this list, possible stuff:
# - 2 or more strings: e.g.
# -- https://github.com/fireeye/capa-rules/blob/master/collection/file-managers/gather-direct-ftp-information.yml
# -- https://github.com/fireeye/capa-rules/blob/master/collection/browser/gather-firefox-profile-information.yml
# -- https://github.com/mandiant/capa-rules/blob/master/collection/file-managers/gather-direct-ftp-information.yml
# -- https://github.com/mandiant/capa-rules/blob/master/collection/browser/gather-firefox-profile-information.yml
# - count(string (1 rule: /executable/subfile/pe/contain-an-embedded-pe-file.yml)
# - count(match( could be done by creating the referenced rule a 2nd time with the condition, that it hits x times (only 1 rule: ./anti-analysis/anti-disasm/contain-anti-disasm-techniques.yml)
# - it would be technically possible to get the "basic blocks" working, but the rules contain mostly other non supported statements in there => not worth the effort.
@@ -104,7 +104,7 @@ def check_feature(statement, rulename):
def get_rule_url(path):
path = re.sub(r"\.\.\/", "", path)
path = re.sub(r"capa-rules\/", "", path)
return "https://github.com/fireeye/capa-rules/blob/master/" + path
return "https://github.com/mandiant/capa-rules/blob/master/" + path
def convert_capa_number_to_yara_bytes(number):
@@ -176,7 +176,7 @@ def convert_rule(rule, rulename, cround, depth):
elif s_type == "api" or s_type == "import":
# TODO: is it possible in YARA to make a difference between api & import?
# https://github.com/fireeye/capa-rules/blob/master/doc/format.md#api
# https://github.com/mandiant/capa-rules/blob/master/doc/format.md#api
api = kid.value
logger.info("doing api: " + repr(api))
@@ -208,7 +208,7 @@ def convert_rule(rule, rulename, cround, depth):
yara_condition += '\tpe.exports("' + export + '") '
elif s_type == "section":
# https://github.com/fireeye/capa-rules/blob/master/doc/format.md#section
# https://github.com/mandiant/capa-rules/blob/master/doc/format.md#section
section = kid.value
logger.info("doing section: " + repr(section))
@@ -220,7 +220,7 @@ def convert_rule(rule, rulename, cround, depth):
)
elif s_type == "match":
# https://github.com/fireeye/capa-rules/blob/master/doc/format.md#matching-prior-rule-matches-and-namespaces
# https://github.com/mandiant/capa-rules/blob/master/doc/format.md#matching-prior-rule-matches-and-namespaces
match = kid.value
logger.info("doing match: " + repr(match))
@@ -717,7 +717,7 @@ def main(argv=None):
return -1
output_yar(
"// Rules from FireEye's https://github.com/fireeye/capa-rules converted to YARA using https://github.com/fireeye/capa/blob/master/scripts/capa2yara.py by Arnim Rupp"
"// Rules from Mandiant's https://github.com/mandiant/capa-rules converted to YARA using https://github.com/mandiant/capa/blob/master/scripts/capa2yara.py by Arnim Rupp"
)
output_yar(
"// Beware: These are less rules than capa (because not all fit into YARA, stats at EOF) and is less precise because e.g. capas function scopes are applied to the whole file"

View File

@@ -163,14 +163,15 @@ def render_dictionary(doc):
# ==== render dictionary helpers
def capa_details(file_path, output_format="dictionary"):
# collect metadata (used only to make rendering more complete)
meta = capa.main.collect_metadata("", file_path, RULES_PATH, extractor)
# extract features and find capabilities
extractor = capa.main.get_extractor(file_path, "auto", capa.main.BACKEND_VIV, [], False, disable_progress=True)
capabilities, counts = capa.main.find_capabilities(rules, extractor, disable_progress=True)
# collect metadata (used only to make rendering more complete)
meta = capa.main.collect_metadata("", file_path, RULES_PATH, extractor)
meta["analysis"].update(counts)
meta["analysis"]["layout"] = capa.main.compute_layout(rules, extractor, capabilities)
capa_output = False
if output_format == "dictionary":

View File

@@ -6,7 +6,7 @@ Usage:
$ python capafmt.py -i foo.yml
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python2
"""
Copyright (C) 2021 FireEye, Inc. All Rights Reserved.
Copyright (C) 2021 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -20,7 +20,7 @@ Adapted for Binary Ninja by @psifertex
This script will verify that the report matches the workspace.
Check the log window for any errors, and/or the summary of changes.
Derived from: https://github.com/fireeye/capa/blob/master/scripts/import-to-ida.py
Derived from: https://github.com/mandiant/capa/blob/master/scripts/import-to-ida.py
"""
import os
import json

View File

@@ -20,7 +20,7 @@ and then select the existing capa report from the file system.
This script will verify that the report matches the workspace.
Check the output window for any errors, and/or the summary of changes.
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -5,7 +5,7 @@ Usage:
$ python scripts/lint.py rules/
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -230,9 +230,16 @@ def get_sample_capabilities(ctx: Context, path: Path) -> Set[str]:
logger.debug("found cached results: %s: %d capabilities", nice_path, len(ctx.capabilities_by_sample[path]))
return ctx.capabilities_by_sample[path]
if nice_path.endswith(capa.main.EXTENSIONS_SHELLCODE_32):
format = "sc32"
elif nice_path.endswith(capa.main.EXTENSIONS_SHELLCODE_64):
format = "sc64"
else:
format = "auto"
logger.debug("analyzing sample: %s", nice_path)
extractor = capa.main.get_extractor(
nice_path, "auto", capa.main.BACKEND_VIV, DEFAULT_SIGNATURES, False, disable_progress=True
nice_path, format, capa.main.BACKEND_VIV, DEFAULT_SIGNATURES, False, disable_progress=True
)
capabilities, _ = capa.main.find_capabilities(ctx.rules, extractor, disable_progress=True)
@@ -332,6 +339,52 @@ class OrStatementWithAlwaysTrueChild(Lint):
return self.violation
class NotNotUnderAnd(Lint):
name = "rule contains a `not` statement that's not found under an `and` statement"
recommendation = "clarify the rule logic and ensure `not` is always found under `and`"
violation = False
def check_rule(self, ctx: Context, rule: Rule):
self.violation = False
def rec(statement):
if isinstance(statement, capa.engine.Statement):
if not isinstance(statement, capa.engine.And):
for child in statement.get_children():
if isinstance(child, capa.engine.Not):
self.violation = True
for child in statement.get_children():
rec(child)
rec(rule.statement)
return self.violation
class OptionalNotUnderAnd(Lint):
name = "rule contains an `optional` or `0 or more` statement that's not found under an `and` statement"
recommendation = "clarify the rule logic and ensure `optional` and `0 or more` is always found under `and`"
violation = False
def check_rule(self, ctx: Context, rule: Rule):
self.violation = False
def rec(statement):
if isinstance(statement, capa.engine.Statement):
if not isinstance(statement, capa.engine.And):
for child in statement.get_children():
if isinstance(child, capa.engine.Some) and child.count == 0:
self.violation = True
for child in statement.get_children():
rec(child)
rec(rule.statement)
return self.violation
class UnusualMetaField(Lint):
name = "unusual meta field"
recommendation = "Remove the meta field"
@@ -653,6 +706,8 @@ LOGIC_LINTS = (
DoesntMatchExample(),
StatementWithSingleChildStatement(),
OrStatementWithAlwaysTrueChild(),
NotNotUnderAnd(),
OptionalNotUnderAnd(),
)

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Copyright (C) 2021 FireEye, Inc. All Rights Reserved.
Copyright (C) 2021 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt

150
scripts/profile-time.py Normal file
View File

@@ -0,0 +1,150 @@
"""
Invoke capa multiple times and record profiling informations.
Use the --number and --repeat options to change the number of iterations.
By default, the script will emit a markdown table with a label pulled from git.
Note: you can run this script against pre-generated .frz files to reduce the startup time.
usage:
usage: profile-time.py [--number NUMBER] [--repeat REPEAT] [--label LABEL] sample
Profile capa performance
positional arguments:
sample path to sample to analyze
optional arguments:
--number NUMBER batch size of profile collection
--repeat REPEAT batch count of profile collection
--label LABEL description of the profile collection
example:
$ python profile-time.py ./tests/data/kernel32.dll_.frz --number 1 --repeat 2
| label | count(evaluations) | avg(time) | min(time) | max(time) |
|--------------------------------------|----------------------|-------------|-------------|-------------|
| 18c30e4 main: remove perf debug msgs | 66,561,622 | 132.13s | 125.14s | 139.12s |
^^^ --label or git hash
"""
import sys
import timeit
import logging
import argparse
import subprocess
import tqdm
import tabulate
import capa.main
import capa.perf
import capa.rules
import capa.engine
import capa.helpers
import capa.features
import capa.features.common
import capa.features.freeze
logger = logging.getLogger("capa.profile")
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
label = subprocess.run(
"git show --pretty=oneline --abbrev-commit | head -n 1", shell=True, capture_output=True, text=True
).stdout.strip()
is_dirty = (
subprocess.run(
"git status | grep 'modified: ' | grep -v 'rules' | grep -v 'tests/data'",
shell=True,
capture_output=True,
text=True,
).stdout
!= ""
)
if is_dirty:
label += " (dirty)"
parser = argparse.ArgumentParser(description="Profile capa performance")
capa.main.install_common_args(parser, wanted={"format", "sample", "signatures", "rules"})
parser.add_argument("--number", type=int, default=3, help="batch size of profile collection")
parser.add_argument("--repeat", type=int, default=30, help="batch count of profile collection")
parser.add_argument("--label", type=str, default=label, help="description of the profile collection")
args = parser.parse_args(args=argv)
capa.main.handle_common_args(args)
try:
taste = capa.helpers.get_file_taste(args.sample)
except IOError as e:
logger.error("%s", str(e))
return -1
try:
with capa.main.timing("load rules"):
rules = capa.rules.RuleSet(capa.main.get_rules(args.rules, disable_progress=True))
except (IOError) as e:
logger.error("%s", str(e))
return -1
try:
sig_paths = capa.main.get_signatures(args.signatures)
except (IOError) as e:
logger.error("%s", str(e))
return -1
if (args.format == "freeze") or (args.format == "auto" and capa.features.freeze.is_freeze(taste)):
with open(args.sample, "rb") as f:
extractor = capa.features.freeze.load(f.read())
else:
extractor = capa.main.get_extractor(
args.sample, args.format, capa.main.BACKEND_VIV, sig_paths, should_save_workspace=False
)
with tqdm.tqdm(total=args.number * args.repeat) as pbar:
def do_iteration():
capa.perf.reset()
capa.main.find_capabilities(rules, extractor, disable_progress=True)
pbar.update(1)
samples = timeit.repeat(do_iteration, number=args.number, repeat=args.repeat)
logger.debug("perf: find capabilities: min: %0.2fs" % (min(samples) / float(args.number)))
logger.debug("perf: find capabilities: avg: %0.2fs" % (sum(samples) / float(args.repeat) / float(args.number)))
logger.debug("perf: find capabilities: max: %0.2fs" % (max(samples) / float(args.number)))
for (counter, count) in capa.perf.counters.most_common():
logger.debug("perf: counter: {:}: {:,}".format(counter, count))
print(
tabulate.tabulate(
[
(
args.label,
"{:,}".format(capa.perf.counters["evaluate.feature"]),
# python documentation indicates that min(samples) should be preferred,
# so lets put that first.
#
# https://docs.python.org/3/library/timeit.html#timeit.Timer.repeat
"%0.2fs" % (min(samples) / float(args.number)),
"%0.2fs" % (sum(samples) / float(args.repeat) / float(args.number)),
"%0.2fs" % (max(samples) / float(args.number)),
)
],
headers=["label", "count(evaluations)", "min(time)", "avg(time)", "max(time)"],
tablefmt="github",
)
)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt

View File

@@ -40,7 +40,7 @@ Example::
- connect TCP socket
...
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -87,22 +87,34 @@ def render_matches_by_function(doc):
- send HTTP request
- connect to HTTP server
"""
functions_by_bb = {}
for function, info in doc["meta"]["analysis"]["layout"]["functions"].items():
for bb in info["matched_basic_blocks"]:
functions_by_bb[bb] = function
ostream = rutils.StringIO()
matches_by_function = collections.defaultdict(set)
for rule in rutils.capability_rules(doc):
for va in rule["matches"].keys():
matches_by_function[va].add(rule["meta"]["name"])
if rule["meta"]["scope"] == capa.rules.FUNCTION_SCOPE:
for va in rule["matches"].keys():
matches_by_function[va].add(rule["meta"]["name"])
elif rule["meta"]["scope"] == capa.rules.BASIC_BLOCK_SCOPE:
for va in rule["matches"].keys():
function = functions_by_bb[va]
matches_by_function[function].add(rule["meta"]["name"])
else:
# file scope
pass
for va, feature_count in sorted(doc["meta"]["analysis"]["feature_counts"]["functions"].items()):
va = int(va)
if not matches_by_function.get(va, {}):
continue
ostream.writeln("function at 0x%X with %d features: " % (va, feature_count))
for rule_name in matches_by_function[va]:
for rule_name in sorted(matches_by_function[va]):
ostream.writeln(" - " + rule_name)
ostream.write("\n")
return ostream.getvalue()
@@ -174,6 +186,7 @@ def main(argv=None):
meta = capa.main.collect_metadata(argv, args.sample, args.rules, extractor)
capabilities, counts = capa.main.find_capabilities(rules, extractor)
meta["analysis"].update(counts)
meta["analysis"]["layout"] = capa.main.compute_layout(rules, extractor, capabilities)
if capa.main.has_file_limitation(rules, capabilities):
# bail if capa encountered file limitation e.g. a packed binary
@@ -190,8 +203,6 @@ def main(argv=None):
print(render_matches_by_function(doc))
colorama.deinit()
logger.info("done.")
return 0

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python2
"""
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -86,7 +86,7 @@ def main(argv=None):
argv = sys.argv[1:]
parser = argparse.ArgumentParser(description="Show the features that capa extracts from the given sample")
capa.main.install_common_args(parser, wanted={"format", "sample", "signatures"})
capa.main.install_common_args(parser, wanted={"format", "sample", "signatures", "backend"})
parser.add_argument("-F", "--function", type=lambda x: int(x, 0x10), help="Show features for specific function")
args = parser.parse_args(args=argv)
@@ -111,7 +111,7 @@ def main(argv=None):
should_save_workspace = os.environ.get("CAPA_SAVE_WORKSPACE") not in ("0", "no", "NO", "n", None)
try:
extractor = capa.main.get_extractor(
args.sample, args.format, capa.main.BACKEND_VIV, sig_paths, should_save_workspace
args.sample, args.format, args.backend, sig_paths, should_save_workspace
)
except capa.main.UnsupportedFormatError:
logger.error("-" * 80)

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
@@ -12,16 +12,16 @@ import setuptools
requirements = [
"tqdm==4.62.3",
"pyyaml==5.4.1",
"pyyaml==6.0",
"tabulate==0.8.9",
"colorama==0.4.4",
"termcolor==1.1.0",
"wcwidth==0.2.5",
"ida-settings==2.1.0",
"viv-utils[flirt]==0.6.6",
"viv-utils[flirt]==0.6.9",
"halo==0.0.31",
"networkx==2.5.1",
"ruamel.yaml==0.17.16",
"ruamel.yaml==0.17.20",
"vivisect==1.0.5",
"smda==1.6.2",
"pefile==2021.9.3",
@@ -50,11 +50,11 @@ setuptools.setup(
long_description_content_type="text/markdown",
author="Willi Ballenthin, Moritz Raabe",
author_email="william.ballenthin@mandiant.com, moritz.raabe@mandiant.com",
url="https://www.github.com/fireeye/capa",
url="https://www.github.com/mandiant/capa",
project_urls={
"Documentation": "https://github.com/fireeye/capa/tree/master/doc",
"Rules": "https://github.com/fireeye/capa-rules",
"Rules Documentation": "https://github.com/fireeye/capa-rules/tree/master/doc",
"Documentation": "https://github.com/mandiant/capa/tree/master/doc",
"Rules": "https://github.com/mandiant/capa-rules",
"Rules Documentation": "https://github.com/mandiant/capa-rules/tree/master/doc",
},
packages=setuptools.find_packages(exclude=["tests"]),
package_dir={"capa": "capa"},
@@ -70,19 +70,19 @@ setuptools.setup(
"pytest==6.2.5",
"pytest-sugar==0.9.4",
"pytest-instafail==0.4.2",
"pytest-cov==2.12.1",
"pycodestyle==2.7.0",
"black==21.9b0",
"isort==5.9.3",
"mypy==0.910",
"psutil==5.8.0",
"pytest-cov==3.0.0",
"pycodestyle==2.8.0",
"black==21.12b0",
"isort==5.10.1",
"mypy==0.931",
"psutil==5.9.0",
# type stubs for mypy
"types-backports==0.1.3",
"types-colorama==0.4.3",
"types-PyYAML==5.4.10",
"types-tabulate==0.8.2",
"types-termcolor==1.1.1",
"types-psutil==5.8.8",
"types-colorama==0.4.5",
"types-PyYAML==6.0.3",
"types-tabulate==0.8.5",
"types-termcolor==1.1.2",
"types-psutil==5.8.19",
],
},
zip_safe=False,

View File

@@ -3,7 +3,7 @@
This directory contains FLIRT signatures that capa uses to identify library functions.
Typically, capa will ignore library functions, which reduces false positives and improves runtime.
These FLIRT signatures were generated by FireEye using the Hex-Rays FLAIR tools such as `pcf` and `sigmake`.
FireEye generated the signatures from source data that they collected; these signatures are not derived from the FLIRT signatures distributed with IDA Pro.
These FLIRT signatures were generated by Mandiant using the Hex-Rays FLAIR tools such as `pcf` and `sigmake`.
Mandiant generated the signatures from source data that they collected; these signatures are not derived from the FLIRT signatures distributed with IDA Pro.
The signatures in this directory have the same license as capa: Apache 2.0.

View File

@@ -182,6 +182,8 @@ def get_data_path_by_name(name):
return os.path.join(CD, "data", "kernel32.dll_")
elif name == "kernel32-64":
return os.path.join(CD, "data", "kernel32-64.dll_")
elif name == "pma01-01":
return os.path.join(CD, "data", "Practical Malware Analysis Lab 01-01.dll_")
elif name == "pma12-04":
return os.path.join(CD, "data", "Practical Malware Analysis Lab 12-04.exe_")
elif name == "pma16-01":
@@ -234,6 +236,8 @@ def get_sample_md5_by_name(name):
return "56bed8249e7c2982a90e54e1e55391a2"
elif name == "pma16-01":
return "7faafc7e4a5c736ebfee6abbbc812d80"
elif name == "pma01-01":
return "290934c61de9176ad682ffdd65f0a669"
elif name == "pma21-01":
return "c8403fb05244e23a7931c766409b5e22"
elif name == "al-khaser x86":
@@ -409,6 +413,7 @@ FEATURE_PRESENCE_TESTS = sorted(
# insn/number
("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF), True),
("mimikatz", "function=0x40105D", capa.features.insn.Number(0x3136B0), True),
("mimikatz", "function=0x401000", capa.features.insn.Number(0x0), True),
# insn/number: stack adjustments
("mimikatz", "function=0x40105D", capa.features.insn.Number(0xC), False),
("mimikatz", "function=0x40105D", capa.features.insn.Number(0x10), False),
@@ -416,6 +421,9 @@ FEATURE_PRESENCE_TESTS = sorted(
("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF), True),
("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF, bitness=BITNESS_X32), True),
("mimikatz", "function=0x40105D", capa.features.insn.Number(0xFF, bitness=BITNESS_X64), False),
# insn/number: negative
("mimikatz", "function=0x401553", capa.features.insn.Number(0xFFFFFFFF), True),
("mimikatz", "function=0x43e543", capa.features.insn.Number(0xFFFFFFF0), True),
# insn/offset
("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x0), True),
("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x4), True),

View File

@@ -5,13 +5,6 @@
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
import textwrap
import capa.rules
import capa.engine
import capa.features.insn
import capa.features.common
from capa.engine import *
from capa.features import *
from capa.features.insn import *
@@ -117,419 +110,27 @@ def test_range():
assert Range(Number(1), min=1, max=3).evaluate({Number(1): {1, 2, 3, 4}}) == False
def test_range_exact():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): 2
"""
)
r = capa.rules.Rule.from_yaml(rule)
def test_short_circuit():
assert Or([Number(1), Number(2)]).evaluate({Number(1): {1}}) == True
# just enough matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches
# not enough matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" not in matches
# too many matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1, 2, 3}}, 0x0)
assert "test rule" not in matches
# with short circuiting, only the children up until the first satisfied child are captured.
assert len(Or([Number(1), Number(2)]).evaluate({Number(1): {1}}, short_circuit=True).children) == 1
assert len(Or([Number(1), Number(2)]).evaluate({Number(1): {1}}, short_circuit=False).children) == 2
def test_range_range():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): (2, 3)
"""
)
r = capa.rules.Rule.from_yaml(rule)
def test_eval_order():
# base cases.
assert Or([Number(1), Number(2)]).evaluate({Number(1): {1}}) == True
assert Or([Number(1), Number(2)]).evaluate({Number(2): {1}}) == True
# just enough matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches
# with short circuiting, only the children up until the first satisfied child are captured.
assert len(Or([Number(1), Number(2)]).evaluate({Number(1): {1}}).children) == 1
assert len(Or([Number(1), Number(2)]).evaluate({Number(2): {1}}).children) == 2
assert len(Or([Number(1), Number(2)]).evaluate({Number(1): {1}, Number(2): {1}}).children) == 1
# enough matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1, 2, 3}}, 0x0)
assert "test rule" in matches
# and its guaranteed that children are evaluated in order.
assert Or([Number(1), Number(2)]).evaluate({Number(1): {1}}).children[0].statement == Number(1)
assert Or([Number(1), Number(2)]).evaluate({Number(1): {1}}).children[0].statement != Number(2)
# not enough matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" not in matches
# too many matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1, 2, 3, 4}}, 0x0)
assert "test rule" not in matches
def test_range_exact_zero():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): 0
"""
)
r = capa.rules.Rule.from_yaml(rule)
# feature isn't indexed - good.
features, matches = capa.engine.match([r], {}, 0x0)
assert "test rule" in matches
# feature is indexed, but no matches.
# i don't think we should ever really have this case, but good to check anyways.
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {}}, 0x0)
assert "test rule" in matches
# too many matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" not in matches
def test_range_with_zero():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): (0, 1)
"""
)
r = capa.rules.Rule.from_yaml(rule)
# ok
features, matches = capa.engine.match([r], {}, 0x0)
assert "test rule" in matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {}}, 0x0)
assert "test rule" in matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" in matches
# too many matches
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" not in matches
def test_match_adds_matched_rule_feature():
"""show that using `match` adds a feature for matched rules."""
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- number: 100
"""
)
r = capa.rules.Rule.from_yaml(rule)
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_matched_rules():
"""show that using `match` adds a feature for matched rules."""
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule1
features:
- number: 100
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule2
features:
- match: test rule1
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule1") in features
assert capa.features.common.MatchedRule("test rule2") in features
# the ordering of the rules must not matter,
# the engine should match rules in an appropriate order.
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(reversed(rules)),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule1") in features
assert capa.features.common.MatchedRule("test rule2") in features
def test_substring():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- substring: abc
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aaaa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("abc"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("111abc222"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("111abc"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("abc222"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_regex():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule with implied wildcards
features:
- and:
- string: /bbbb/
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule with anchor
features:
- and:
- string: /^bbbb/
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aaaa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aBBBBa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("abbbba"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
assert capa.features.common.MatchedRule("rule with implied wildcards") in features
assert capa.features.common.MatchedRule("rule with anchor") not in features
def test_regex_ignorecase():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/i
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aBBBBa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_regex_complex():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
r"""
rule:
meta:
name: test rule
features:
- or:
- string: /.*HARDWARE\\Key\\key with spaces\\.*/i
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String(r"Hardware\Key\key with spaces\some value"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_namespace():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: CreateFile API
namespace: file/create/CreateFile
features:
- api: CreateFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: WriteFile API
namespace: file/write
features:
- api: WriteFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: file-create
features:
- match: file/create
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: filesystem-any
features:
- match: file
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.API("CreateFile"): {1}},
0x0,
)
assert "CreateFile API" in matches
assert "file-create" in matches
assert "filesystem-any" in matches
assert capa.features.common.MatchedRule("file") in features
assert capa.features.common.MatchedRule("file/create") in features
assert capa.features.common.MatchedRule("file/create/CreateFile") in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.API("WriteFile"): {1}},
0x0,
)
assert "WriteFile API" in matches
assert "file-create" not in matches
assert "filesystem-any" in matches
def test_render_number():
assert str(capa.features.insn.Number(1)) == "number(0x1)"
assert str(capa.features.insn.Number(1, bitness=capa.features.common.BITNESS_X32)) == "number/x32(0x1)"
assert str(capa.features.insn.Number(1, bitness=capa.features.common.BITNESS_X64)) == "number/x64(0x1)"
def test_render_offset():
assert str(capa.features.insn.Offset(1)) == "offset(0x1)"
assert str(capa.features.insn.Offset(1, bitness=capa.features.common.BITNESS_X32)) == "offset/x32(0x1)"
assert str(capa.features.insn.Offset(1, bitness=capa.features.common.BITNESS_X64)) == "offset/x64(0x1)"
assert Or([Number(1), Number(2)]).evaluate({Number(2): {1}}).children[1].statement == Number(2)
assert Or([Number(1), Number(2)]).evaluate({Number(2): {1}}).children[1].statement != Number(1)

View File

@@ -70,7 +70,7 @@ def test_main_non_ascii_filename(pingtaest_extractor, tmpdir, capsys):
def test_main_non_ascii_filename_nonexistent(tmpdir, caplog):
NON_ASCII_FILENAME = "täst_not_there.exe"
assert capa.main.main(["-q", NON_ASCII_FILENAME]) == -1
assert capa.main.main(["-q", NON_ASCII_FILENAME]) == capa.main.E_MISSING_FILE
assert NON_ASCII_FILENAME in caplog.text
@@ -375,3 +375,13 @@ def test_backend_option(capsys):
std_json = json.loads(std.out)
assert std_json["meta"]["analysis"]["extractor"] == "SmdaFeatureExtractor"
assert len(std_json["rules"]) > 0
def test_json_meta(capsys):
path = fixtures.get_data_path_by_name("pma01-01")
assert capa.main.main([path, "-j"]) == 0
std = capsys.readouterr()
std_json = json.loads(std.out)
# remember: json can't have integer keys :-(
assert str(0x10001010) in std_json["meta"]["analysis"]["layout"]["functions"]
assert 0x10001179 in std_json["meta"]["analysis"]["layout"]["functions"][str(0x10001010)]["matched_basic_blocks"]

533
tests/test_match.py Normal file
View File

@@ -0,0 +1,533 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
import textwrap
import capa.rules
import capa.engine
import capa.features.insn
import capa.features.common
from capa.rules import Scope
from capa.features import *
from capa.features.insn import *
from capa.features.common import *
def match(rules, features, va, scope=Scope.FUNCTION):
"""
use all matching algorithms and verify that they compute the same result.
then, return those results to the caller so they can make their asserts.
"""
features1, matches1 = capa.engine.match(rules, features, va)
ruleset = capa.rules.RuleSet(rules)
features2, matches2 = ruleset.match(scope, features, va)
for feature, locations in features1.items():
assert feature in features2
assert locations == features2[feature]
for rulename, results in matches1.items():
assert rulename in matches2
assert len(results) == len(matches2[rulename])
return features1, matches1
def test_match_simple():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
namespace: testns1/testns2
features:
- number: 100
"""
)
r = capa.rules.Rule.from_yaml(rule)
features, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches
assert MatchedRule("test rule") in features
assert MatchedRule("testns1") in features
assert MatchedRule("testns1/testns2") in features
def test_match_range_exact():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): 2
"""
)
r = capa.rules.Rule.from_yaml(rule)
# just enough matches
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches
# not enough matches
_, matches = match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" not in matches
# too many matches
_, matches = match([r], {capa.features.insn.Number(100): {1, 2, 3}}, 0x0)
assert "test rule" not in matches
def test_match_range_range():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): (2, 3)
"""
)
r = capa.rules.Rule.from_yaml(rule)
# just enough matches
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches
# enough matches
_, matches = match([r], {capa.features.insn.Number(100): {1, 2, 3}}, 0x0)
assert "test rule" in matches
# not enough matches
_, matches = match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" not in matches
# too many matches
_, matches = match([r], {capa.features.insn.Number(100): {1, 2, 3, 4}}, 0x0)
assert "test rule" not in matches
def test_match_range_exact_zero():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): 0
"""
)
r = capa.rules.Rule.from_yaml(rule)
# feature isn't indexed - good.
_, matches = match([r], {}, 0x0)
assert "test rule" in matches
# feature is indexed, but no matches.
# i don't think we should ever really have this case, but good to check anyways.
_, matches = match([r], {capa.features.insn.Number(100): {}}, 0x0)
assert "test rule" in matches
# too many matches
_, matches = match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" not in matches
def test_match_range_with_zero():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): (0, 1)
"""
)
r = capa.rules.Rule.from_yaml(rule)
# ok
_, matches = match([r], {}, 0x0)
assert "test rule" in matches
_, matches = match([r], {capa.features.insn.Number(100): {}}, 0x0)
assert "test rule" in matches
_, matches = match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert "test rule" in matches
# too many matches
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" not in matches
def test_match_adds_matched_rule_feature():
"""show that using `match` adds a feature for matched rules."""
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- number: 100
"""
)
r = capa.rules.Rule.from_yaml(rule)
features, _ = match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_matched_rules():
"""show that using `match` adds a feature for matched rules."""
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule1
features:
- number: 100
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule2
features:
- match: test rule1
"""
)
),
]
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule1") in features
assert capa.features.common.MatchedRule("test rule2") in features
# the ordering of the rules must not matter,
# the engine should match rules in an appropriate order.
features, _ = match(
capa.rules.topologically_order_rules(reversed(rules)),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule1") in features
assert capa.features.common.MatchedRule("test rule2") in features
def test_match_namespace():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: CreateFile API
namespace: file/create/CreateFile
features:
- api: CreateFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: WriteFile API
namespace: file/write
features:
- api: WriteFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: file-create
features:
- match: file/create
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: filesystem-any
features:
- match: file
"""
)
),
]
features, matches = match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.API("CreateFile"): {1}},
0x0,
)
assert "CreateFile API" in matches
assert "file-create" in matches
assert "filesystem-any" in matches
assert capa.features.common.MatchedRule("file") in features
assert capa.features.common.MatchedRule("file/create") in features
assert capa.features.common.MatchedRule("file/create/CreateFile") in features
features, matches = match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.API("WriteFile"): {1}},
0x0,
)
assert "WriteFile API" in matches
assert "file-create" not in matches
assert "filesystem-any" in matches
def test_match_substring():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- substring: abc
"""
)
),
]
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aaaa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("abc"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("111abc222"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("111abc"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("abc222"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_regex():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule with implied wildcards
features:
- and:
- string: /bbbb/
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule with anchor
features:
- and:
- string: /^bbbb/
"""
)
),
]
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aaaa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aBBBBa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") not in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("abbbba"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
assert capa.features.common.MatchedRule("rule with implied wildcards") in features
assert capa.features.common.MatchedRule("rule with anchor") not in features
def test_match_regex_ignorecase():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/i
"""
)
),
]
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("aBBBBa"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_regex_complex():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
r"""
rule:
meta:
name: test rule
features:
- or:
- string: /.*HARDWARE\\Key\\key with spaces\\.*/i
"""
)
),
]
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String(r"Hardware\Key\key with spaces\some value"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_regex_values_always_string():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- or:
- string: /123/
- string: /0x123/
"""
)
),
]
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("123"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, _ = match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("0x123"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_match_not():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
namespace: testns1/testns2
features:
- not:
- number: 99
"""
)
r = capa.rules.Rule.from_yaml(rule)
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches
def test_match_not_not():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
namespace: testns1/testns2
features:
- not:
- not:
- number: 100
"""
)
r = capa.rules.Rule.from_yaml(rule)
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
assert "test rule" in matches

65
tests/test_optimizer.py Normal file
View File

@@ -0,0 +1,65 @@
# Copyright (C) 2021 FireEye, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
import textwrap
import pytest
import capa.rules
import capa.engine
import capa.optimizer
import capa.features.common
from capa.engine import Or, And
from capa.features.insn import Mnemonic
from capa.features.common import Arch, Bytes, Substring
def test_optimizer_order():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: function
features:
- and:
- substring: "foo"
- arch: amd64
- mnemonic: cmp
- and:
- bytes: 3
- offset: 2
- or:
- number: 1
- offset: 4
"""
)
r = capa.rules.Rule.from_yaml(rule)
# before optimization
children = list(r.statement.get_children())
assert isinstance(children[0], Substring)
assert isinstance(children[1], Arch)
assert isinstance(children[2], Mnemonic)
assert isinstance(children[3], And)
assert isinstance(children[4], Or)
# after optimization
capa.optimizer.optimize_rules([r])
children = list(r.statement.get_children())
# cost: 0
assert isinstance(children[0], Arch)
# cost: 1
assert isinstance(children[1], Mnemonic)
# cost: 2
assert isinstance(children[2], Substring)
# cost: 3
assert isinstance(children[3], Or)
# cost: 4
assert isinstance(children[4], And)

View File

@@ -2,9 +2,23 @@ import textwrap
import capa.rules
import capa.render.utils
import capa.features.insn
import capa.features.common
import capa.render.result_document
def test_render_number():
assert str(capa.features.insn.Number(1)) == "number(0x1)"
assert str(capa.features.insn.Number(1, bitness=capa.features.common.BITNESS_X32)) == "number/x32(0x1)"
assert str(capa.features.insn.Number(1, bitness=capa.features.common.BITNESS_X64)) == "number/x64(0x1)"
def test_render_offset():
assert str(capa.features.insn.Offset(1)) == "offset(0x1)"
assert str(capa.features.insn.Offset(1, bitness=capa.features.common.BITNESS_X32)) == "offset/x32(0x1)"
assert str(capa.features.insn.Offset(1, bitness=capa.features.common.BITNESS_X64)) == "offset/x64(0x1)"
def test_render_meta_attack():
# Persistence::Boot or Logon Autostart Execution::Registry Run Keys / Startup Folder [T1547.001]
id = "T1543.003"

View File

@@ -785,37 +785,6 @@ def test_substring_description():
assert (Substring("abc") in children) == True
def test_regex_values_always_string():
rules = [
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- or:
- string: /123/
- string: /0x123/
"""
)
),
]
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("123"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
features, matches = capa.engine.match(
capa.rules.topologically_order_rules(rules),
{capa.features.common.String("0x123"): {1}},
0x0,
)
assert capa.features.common.MatchedRule("test rule") in features
def test_filter_rules():
rules = capa.rules.RuleSet(
[