Skip to content

Commit

Permalink
Merge branch 'master' into feat/1755
Browse files Browse the repository at this point in the history
  • Loading branch information
williballenthin authored Aug 22, 2024
2 parents 7123f1f + 6c883f3 commit eaa8945
Show file tree
Hide file tree
Showing 53 changed files with 2,003 additions and 441 deletions.
49 changes: 47 additions & 2 deletions .github/workflows/web-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: deploy web to GitHub Pages

on:
push:
branches: [ master, "wb/webui-actions-1" ]
branches: [ master ]
paths:
- 'web/**'

Expand All @@ -22,6 +22,7 @@ concurrency:

jobs:
build-landing-page:
name: Build landing page
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -32,6 +33,7 @@ jobs:
path: './web/public'

build-explorer:
name: Build capa explorer web
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down Expand Up @@ -63,12 +65,51 @@ jobs:
name: explorer
path: './web/explorer/dist'

build-rules:
name: Build rules site
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v4
with:
submodules: 'recursive'
# full depth so that capa-rules has a full history
# and we can construct a timeline of rule updates.
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: '3.12'
- uses: extractions/setup-just@v2
- name: Install pagefind
uses: supplypike/setup-bin@v4
with:
uri: "https://github.com/CloudCannon/pagefind/releases/download/v1.1.0/pagefind-v1.1.0-x86_64-unknown-linux-musl.tar.gz"
name: "pagefind"
version: "1.1.0"
- name: Install dependencies
working-directory: ./web/rules
run: pip install -r requirements.txt
- name: Build the website
working-directory: ./web/rules
run: just build
- name: Index the website
working-directory: ./web/rules
run: pagefind --site "public"
# upload the build website to artifacts
# so that we can download and inspect, if desired.
- uses: actions/upload-artifact@v4
with:
name: rules
path: './web/rules/public'

deploy:
name: Deploy site to GitHub Pages
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: [build-landing-page, build-explorer]
needs: [build-landing-page, build-explorer, build-rules]
steps:
- uses: actions/download-artifact@v4
with:
Expand All @@ -78,6 +119,10 @@ jobs:
with:
name: explorer
path: './public/explorer'
- uses: actions/download-artifact@v4
with:
name: rules
path: './public/rules'
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,4 @@ Pipfile.lock
.github/binja/binaryninja
.github/binja/download_headless.py
.github/binja/BinaryNinja-headless.zip
justfile
20 changes: 20 additions & 0 deletions .justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@isort:
pre-commit run isort --show-diff-on-failure --all-files

@black:
pre-commit run black --show-diff-on-failure --all-files

@ruff:
pre-commit run ruff --all-files

@flake8:
pre-commit run flake8 --hook-stage manual --all-files

@mypy:
pre-commit run mypy --hook-stage manual --all-files

@deptry:
pre-commit run deptry --hook-stage manual --all-files

lint: isort black ruff flake8 mypy deptry

5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ repos:
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

Expand All @@ -55,6 +56,7 @@ repos:
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

Expand All @@ -72,6 +74,7 @@ repos:
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

Expand All @@ -90,6 +93,7 @@ repos:
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

Expand All @@ -107,6 +111,7 @@ repos:
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

Expand Down
45 changes: 40 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,47 @@

### New Features

- add landing page https://mandiant.github.io/capa/ @williballenthin #2310
- add rules website https://mandiant.github.io/capa/rules @DeeyaSingh #2310
- add .justfile @williballenthin #2325
- support analyzing BinExport2 files generated by Ghidra #1950 @williballenthin @mehunhoff @mr-tz

### Breaking Changes

### New Rules (0)

-

### Bug Fixes

- fix duplicate features shown in vverbose mode @williballenthin #2323

### capa explorer IDA Pro plugin

### Development

### Raw diffs
- [capa v7.2.0...master](https://github.com/mandiant/capa/compare/v7.2.0...master)
- [capa-rules v7.2.0...master](https://github.com/mandiant/capa-rules/compare/v7.2.0...master)

### v7.2.0
capa v7.2.0 introduces a first version of capa explorer web: a web-based user interface to inspect capa results using your browser. Users can inspect capa result JSON documents in an online web instance or a standalone HTML page for offline usage. capa explorer supports interactive exploring of capa results to make it easier to understand them. Users can filter, sort, and see the details of all identified capabilities. capa explorer web was worked on by @s-ff as part of a [GSoC project](https://summerofcode.withgoogle.com/programs/2024/projects/cR3hjbsq), and it is available at https://mandiant.github.io/capa/explorer/#/.

This release also adds a feature extractor for output from the DRAKVUF sandbox. Now, analysts can pass the resulting `drakmon.log` file to capa and extract capabilities from the artifacts captured by the sandbox. This feature extractor will also be added to the DRAKVUF sandbox as a post-processing script, and it was worked on by @yelhamer as part of a [GSoC project](https://summerofcode.withgoogle.com/programs/2024/projects/fCnBGuEC).

Additionally, we fixed several bugs handling ELF files, and added the ability to filter capa analysis by functions or processes. We also added support to the IDA Pro extractor to leverage analyst recovered API names.

Special thanks to our repeat and new contributors:
* @lakshayletsgo for their first contribution in https://github.com/mandiant/capa/pull/2248
* @msm-cert for their first contribution in https://github.com/mandiant/capa/pull/2143
* @VascoSch92 for their first contribution in https://github.com/mandiant/capa/pull/2143

### New Features

- webui: explore capa analysis results in a web-based UI online and offline #2224 @s-ff
- support analyzing DRAKVUF traces #2143 @yelhamer
- IDA extractor: extract names from dynamically resolved APIs stored in renamed global variables #2201 @Ana06
- support analyzing BinExport2 files generated by Ghidra #1950 @williballenthin @mehunhoff @mr-tz

- cli: add the ability to select which specific functions or processes to analyze @yelhamer

### Breaking Changes

Expand All @@ -19,7 +55,6 @@
- communication/socket/attach-bpf-to-socket-on-linux [email protected]
- anti-analysis/anti-av/overwrite-dll-text-section-to-remove-hooks [email protected]
- nursery/delete-file-on-linux [email protected]
-

### Bug Fixes

Expand All @@ -35,8 +70,8 @@
- CI: update build.yml workflow to exclude web and documentation files #2270 @s-ff

### Raw diffs
- [capa v7.1.0...master](https://github.com/mandiant/capa/compare/v7.1.0...master)
- [capa-rules v7.1.0...master](https://github.com/mandiant/capa-rules/compare/v7.1.0...master)
- [capa v7.1.0...7.2.0](https://github.com/mandiant/capa/compare/v7.1.0...7.2.0)
- [capa-rules v7.1.0...7.2.0](https://github.com/mandiant/capa-rules/compare/v7.1.0...7.2.0)

## v7.1.0
The v7.1.0 release brings large performance improvements to capa's rule matching engine.
Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
![capa](https://github.com/mandiant/capa/blob/master/.github/logo.png)
<br />
<div align="center">
<a href="https://mandiant.github.io/capa/" target="_blank">
<img src="https://github.com/mandiant/capa/blob/master/.github/logo.png">
</a>
<p align="center">
<a href="https://mandiant.github.io/capa/" target="_blank">Website</a>
|
<a href="https://github.com/mandiant/capa/releases/latest" target="_blank">Download</a>
|
<a href="https://mandiant.github.io/capa/explorer/" target="_blank">Web Interface</a>
</p>
<div align="center">

[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/flare-capa)](https://pypi.org/project/flare-capa)
[![Last release](https://img.shields.io/github/v/release/mandiant/capa)](https://github.com/mandiant/capa/releases)
Expand All @@ -7,6 +19,11 @@
[![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)

</div>
</div>

---

capa detects capabilities in executable files.
You run it against a PE, ELF, .NET module, shellcode file, or a sandbox report and it tells you what it thinks the program can do.
For example, it might suggest that the file is a backdoor, is capable of installing services, or relies on HTTP to communicate.
Expand Down
12 changes: 12 additions & 0 deletions capa/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,15 @@ class UnsupportedOSError(ValueError):

class EmptyReportError(ValueError):
pass


class InvalidArgument(ValueError):
pass


class NonExistantFunctionError(ValueError):
pass


class NonExistantProcessError(ValueError):
pass
36 changes: 35 additions & 1 deletion capa/features/extractors/base_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import abc
import hashlib
import dataclasses
from typing import Any, Dict, Tuple, Union, Iterator
from copy import copy
from types import MethodType
from typing import Any, Set, Dict, Tuple, Union, Iterator
from dataclasses import dataclass

# TODO(williballenthin): use typing.TypeAlias directly when Python 3.9 is deprecated
Expand Down Expand Up @@ -296,6 +298,22 @@ def extract_insn_features(
raise NotImplementedError()


def FunctionFilter(extractor: StaticFeatureExtractor, functions: Set) -> StaticFeatureExtractor:
original_get_functions = extractor.get_functions

def filtered_get_functions(self):
yield from (f for f in original_get_functions() if f.address in functions)

# we make a copy of the original extractor object and then update its get_functions() method with the decorated filter one.
# this is in order to preserve the original extractor object's get_functions() method, in case it is used elsewhere in the code.
# an example where this is important is in our testfiles where we may use the same extractor object with different tests,
# with some of these tests needing to install a functions filter on the extractor object.
new_extractor = copy(extractor)
new_extractor.get_functions = MethodType(filtered_get_functions, extractor) # type: ignore

return new_extractor


@dataclass
class ProcessHandle:
"""
Expand Down Expand Up @@ -467,4 +485,20 @@ def get_call_name(self, ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) ->
raise NotImplementedError()


def ProcessFilter(extractor: DynamicFeatureExtractor, processes: Set) -> DynamicFeatureExtractor:
original_get_processes = extractor.get_processes

def filtered_get_processes(self):
yield from (f for f in original_get_processes() if f.address.pid in processes)

# we make a copy of the original extractor object and then update its get_processes() method with the decorated filter one.
# this is in order to preserve the original extractor object's get_processes() method, in case it is used elsewhere in the code.
# an example where this is important is in our testfiles where we may use the same extractor object with different tests,
# with some of these tests needing to install a processes filter on the extractor object.
new_extractor = copy(extractor)
new_extractor.get_processes = MethodType(filtered_get_processes, extractor) # type: ignore

return new_extractor


FeatureExtractor: TypeAlias = Union[StaticFeatureExtractor, DynamicFeatureExtractor]
Loading

0 comments on commit eaa8945

Please sign in to comment.