mirror of
https://github.com/mandiant/capa.git
synced 2025-12-12 07:40:38 -08:00
add Lancelot backend
This commit is contained in:
@@ -79,6 +79,7 @@ BACKEND_VMRAY = "vmray"
|
||||
BACKEND_FREEZE = "freeze"
|
||||
BACKEND_BINEXPORT2 = "binexport2"
|
||||
BACKEND_IDA = "ida"
|
||||
BACKEND_LANCELOT = "lancelot"
|
||||
|
||||
|
||||
class CorruptFile(ValueError):
|
||||
@@ -351,6 +352,18 @@ def get_extractor(
|
||||
|
||||
return capa.features.extractors.ida.extractor.IdaFeatureExtractor()
|
||||
|
||||
elif backend == BACKEND_LANCELOT:
|
||||
import lancelot
|
||||
|
||||
import capa.features.extractors.binexport2
|
||||
import capa.features.extractors.binexport2.extractor
|
||||
|
||||
buf = input_path.read_bytes()
|
||||
be2_buf: bytes = lancelot.binexport2_from_bytes(buf)
|
||||
be2 = capa.features.extractors.binexport2.get_binexport2_from_bytes(be2_buf)
|
||||
|
||||
return capa.features.extractors.binexport2.extractor.BinExport2FeatureExtractor(be2, buf)
|
||||
|
||||
else:
|
||||
raise ValueError("unexpected backend: " + backend)
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ from capa.loader import (
|
||||
BACKEND_FREEZE,
|
||||
BACKEND_PEFILE,
|
||||
BACKEND_DRAKVUF,
|
||||
BACKEND_LANCELOT,
|
||||
BACKEND_BINEXPORT2,
|
||||
)
|
||||
from capa.helpers import (
|
||||
@@ -298,6 +299,7 @@ def install_common_args(parser, wanted=None):
|
||||
(BACKEND_BINJA, "Binary Ninja"),
|
||||
(BACKEND_DOTNET, ".NET"),
|
||||
(BACKEND_BINEXPORT2, "BinExport2"),
|
||||
(BACKEND_LANCELOT, "Lancelot"),
|
||||
(BACKEND_FREEZE, "capa freeze"),
|
||||
(BACKEND_CAPE, "CAPE"),
|
||||
(BACKEND_DRAKVUF, "DRAKVUF"),
|
||||
|
||||
@@ -36,7 +36,7 @@ import capa.main
|
||||
|
||||
logger = logging.getLogger("capa.compare-backends")
|
||||
|
||||
BACKENDS = ("vivisect", "ida", "binja")
|
||||
BACKENDS = ("vivisect", "ida", "binja", "lancelot")
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -113,6 +113,9 @@ def collect(args):
|
||||
file.unlink()
|
||||
|
||||
doc = json.loads(results_path.read_text(encoding="utf-8"))
|
||||
for backend in BACKENDS:
|
||||
if backend not in doc:
|
||||
doc[backend] = {}
|
||||
|
||||
plan = []
|
||||
for file in sorted(p for p in testfiles.glob("*")):
|
||||
@@ -228,6 +231,7 @@ def report(args):
|
||||
t.add_column("viv")
|
||||
t.add_column("ida")
|
||||
t.add_column("bn")
|
||||
t.add_column("lan")
|
||||
t.add_column("rule")
|
||||
|
||||
for rule, _ in seen_rules.most_common():
|
||||
@@ -235,6 +239,7 @@ def report(args):
|
||||
"x" if rule in rules_by_backend["vivisect"] else " ",
|
||||
"x" if rule in rules_by_backend["ida"] else " ",
|
||||
"x" if rule in rules_by_backend["binja"] else " ",
|
||||
"x" if rule in rules_by_backend["lancelot"] else " ",
|
||||
rule,
|
||||
)
|
||||
|
||||
|
||||
@@ -134,6 +134,23 @@ def fixup_viv(path: Path, extractor):
|
||||
extractor.vw.makeFunction(0x404970)
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_lancelot_extractor(path: Path):
|
||||
import lancelot
|
||||
|
||||
import capa.features.extractors.binexport2
|
||||
import capa.features.extractors.binexport2.extractor
|
||||
|
||||
buf = path.read_bytes()
|
||||
be2_buf: bytes = lancelot.binexport2_from_bytes(buf)
|
||||
be2 = capa.features.extractors.binexport2.get_binexport2_from_bytes(be2_buf)
|
||||
|
||||
extractor = capa.features.extractors.binexport2.extractor.BinExport2FeatureExtractor(be2, buf)
|
||||
setattr(extractor, "path", path.as_posix())
|
||||
|
||||
return extractor
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_pefile_extractor(path: Path):
|
||||
import capa.features.extractors.pefile
|
||||
|
||||
32
tests/test_lancelot_features.py
Normal file
32
tests/test_lancelot_features.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2024 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
|
||||
# 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 pytest
|
||||
|
||||
import fixtures
|
||||
|
||||
|
||||
@fixtures.parametrize(
|
||||
"sample,scope,feature,expected",
|
||||
fixtures.FEATURE_PRESENCE_TESTS + fixtures.FEATURE_SYMTAB_FUNC_TESTS,
|
||||
indirect=["sample", "scope"],
|
||||
)
|
||||
def test_lancelot_features(sample, scope, feature, expected):
|
||||
if ".elf" in sample.name:
|
||||
pytest.xfail("lancelot doesn't handle ELF files")
|
||||
fixtures.do_test_feature_presence(fixtures.get_lancelot_extractor, sample, scope, feature, expected)
|
||||
|
||||
|
||||
@fixtures.parametrize(
|
||||
"sample,scope,feature,expected",
|
||||
fixtures.FEATURE_COUNT_TESTS,
|
||||
indirect=["sample", "scope"],
|
||||
)
|
||||
def test_lancelot_feature_counts(sample, scope, feature, expected):
|
||||
if ".elf" in sample.name:
|
||||
pytest.xfail("lancelot doesn't handle ELF files")
|
||||
fixtures.do_test_feature_count(fixtures.get_lancelot_extractor, sample, scope, feature, expected)
|
||||
Reference in New Issue
Block a user