mirror of
https://github.com/mandiant/capa.git
synced 2025-12-12 15:49:46 -08:00
features: add Arch feature at global scope
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
- explorer: add option to limit features to currently selected disassembly address #692 @mike-hunhoff
|
||||
- all: add support for ELF files #700 @Adir-Shemesh @TcM1911
|
||||
- rule format: add feature `format: ` for file format, like `format: pe` @williballenthin
|
||||
- rule format: add feature `arch: ` for architecture, like `arch: amd64` @williballenthin
|
||||
- rule format: add feature `os: ` for operating system, like `os: windows` #701 @williballenthin
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -14,6 +14,7 @@ import capa.ida.helpers
|
||||
import capa.features.extractors.elf
|
||||
import capa.features.extractors.ida.file
|
||||
import capa.features.extractors.ida.insn
|
||||
import capa.features.extractors.ida.global_
|
||||
import capa.features.extractors.ida.function
|
||||
import capa.features.extractors.ida.basicblock
|
||||
from capa.features.common import OS, OS_WINDOWS
|
||||
@@ -80,7 +81,7 @@ class IdaFeatureExtractor(FeatureExtractor):
|
||||
super(IdaFeatureExtractor, self).__init__()
|
||||
self.global_features = []
|
||||
self.global_features.extend(extract_os())
|
||||
self.global_features.extend(extract_format())
|
||||
self.global_features.extend(capa.features.extractors.ida.global_.extract_arch())
|
||||
|
||||
def get_base_address(self):
|
||||
return idaapi.get_imagebase()
|
||||
|
||||
@@ -17,8 +17,7 @@ import capa.features.extractors.helpers
|
||||
import capa.features.extractors.strings
|
||||
import capa.features.extractors.ida.helpers
|
||||
from capa.features.file import Export, Import, Section, FunctionName
|
||||
from capa.features.common import String, Characteristic
|
||||
from capa.features.common import OS, FORMAT_PE, FORMAT_ELF, OS_WINDOWS, Format
|
||||
from capa.features.common import OS, FORMAT_PE, FORMAT_ELF, OS_WINDOWS, Format, String, Characteristic
|
||||
|
||||
|
||||
def check_segment_for_pe(seg):
|
||||
|
||||
@@ -14,7 +14,7 @@ import capa.features.extractors
|
||||
import capa.features.extractors.helpers
|
||||
import capa.features.extractors.strings
|
||||
from capa.features.file import Export, Import, Section
|
||||
from capa.features.common import OS, Format, String, Characteristic, OS_WINDOWS, FORMAT_PE
|
||||
from capa.features.common import OS, FORMAT_PE, OS_WINDOWS, Format, String, Characteristic
|
||||
from capa.features.extractors.base_extractor import FeatureExtractor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -3,6 +3,7 @@ from smda.common.SmdaReport import SmdaReport
|
||||
import capa.features.extractors.common
|
||||
import capa.features.extractors.smda.file
|
||||
import capa.features.extractors.smda.insn
|
||||
import capa.features.extractors.smda.global_
|
||||
import capa.features.extractors.smda.function
|
||||
import capa.features.extractors.smda.basicblock
|
||||
from capa.features.extractors.base_extractor import FeatureExtractor
|
||||
@@ -16,8 +17,10 @@ class SmdaFeatureExtractor(FeatureExtractor):
|
||||
with open(self.path, "rb") as f:
|
||||
self.buf = f.read()
|
||||
|
||||
# pre-compute these because we'll yield them at *every* scope.
|
||||
self.global_features = []
|
||||
self.global_features.extend(capa.features.extractors.common.extract_os(self.buf))
|
||||
self.global_features.extend(capa.features.extractors.smda.global_.extract_arch(self.smda_report))
|
||||
|
||||
def get_base_address(self):
|
||||
return self.smda_report.base_addr
|
||||
|
||||
@@ -13,6 +13,7 @@ import viv_utils.flirt
|
||||
import capa.features.extractors.common
|
||||
import capa.features.extractors.viv.file
|
||||
import capa.features.extractors.viv.insn
|
||||
import capa.features.extractors.viv.global_
|
||||
import capa.features.extractors.viv.function
|
||||
import capa.features.extractors.viv.basicblock
|
||||
from capa.features.extractors.base_extractor import FeatureExtractor
|
||||
@@ -41,8 +42,10 @@ class VivisectFeatureExtractor(FeatureExtractor):
|
||||
with open(self.path, "rb") as f:
|
||||
self.buf = f.read()
|
||||
|
||||
# pre-compute these because we'll yield them at *every* scope.
|
||||
self.global_features = []
|
||||
self.global_features.extend(capa.features.extractors.common.extract_os(self.buf))
|
||||
self.global_features.extend(capa.features.extractors.viv.global_.extract_arch(self.vw))
|
||||
|
||||
def get_base_address(self):
|
||||
# assume there is only one file loaded into the vw
|
||||
|
||||
@@ -80,6 +80,7 @@ SUPPORTED_FEATURES = {
|
||||
capa.features.common.String,
|
||||
capa.features.common.Format,
|
||||
capa.features.common.OS,
|
||||
capa.features.common.Arch,
|
||||
},
|
||||
FUNCTION_SCOPE: {
|
||||
# plus basic block scope features, see below
|
||||
@@ -89,6 +90,7 @@ SUPPORTED_FEATURES = {
|
||||
capa.features.common.Characteristic("loop"),
|
||||
capa.features.common.Characteristic("recursive call"),
|
||||
capa.features.common.OS,
|
||||
capa.features.common.Arch,
|
||||
},
|
||||
BASIC_BLOCK_SCOPE: {
|
||||
capa.features.common.MatchedRule,
|
||||
@@ -107,6 +109,7 @@ SUPPORTED_FEATURES = {
|
||||
capa.features.common.Characteristic("stack string"),
|
||||
capa.features.common.Characteristic("indirect call"),
|
||||
capa.features.common.OS,
|
||||
capa.features.common.Arch,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -254,6 +257,8 @@ def parse_feature(key: str):
|
||||
return capa.features.common.OS
|
||||
elif key == "format":
|
||||
return capa.features.common.Format
|
||||
elif key == "arch":
|
||||
return capa.features.common.Arch
|
||||
else:
|
||||
raise InvalidRule("unexpected statement: %s" % key)
|
||||
|
||||
|
||||
@@ -22,14 +22,17 @@ import capa.features.insn
|
||||
import capa.features.common
|
||||
import capa.features.basicblock
|
||||
from capa.features.common import (
|
||||
BITNESS_X32,
|
||||
BITNESS_X64,
|
||||
FORMAT_ELF,
|
||||
FORMAT_PE,
|
||||
Format,
|
||||
OS,
|
||||
OS_LINUX,
|
||||
ARCH_I386,
|
||||
FORMAT_PE,
|
||||
ARCH_AMD64,
|
||||
FORMAT_ELF,
|
||||
OS_WINDOWS,
|
||||
BITNESS_X32,
|
||||
BITNESS_X64,
|
||||
Arch,
|
||||
Format,
|
||||
)
|
||||
|
||||
CD = os.path.dirname(__file__)
|
||||
@@ -512,11 +515,15 @@ FEATURE_PRESENCE_TESTS = sorted(
|
||||
("mimikatz", "function=0x456BB9", capa.features.common.Characteristic("calls to"), False),
|
||||
# file/function-name
|
||||
("pma16-01", "file", capa.features.file.FunctionName("__aulldiv"), True),
|
||||
# os & format
|
||||
# os & format & arch
|
||||
("pma16-01", "file", OS(OS_WINDOWS), True),
|
||||
("pma16-01", "file", OS(OS_LINUX), False),
|
||||
("pma16-01", "function=0x404356", OS(OS_WINDOWS), True),
|
||||
("pma16-01", "function=0x404356,bb=0x4043B9", OS(OS_WINDOWS), True),
|
||||
("pma16-01", "file", Arch(ARCH_I386), True),
|
||||
("pma16-01", "file", Arch(ARCH_AMD64), False),
|
||||
("pma16-01", "function=0x404356", Arch(ARCH_I386), True),
|
||||
("pma16-01", "function=0x404356,bb=0x4043B9", Arch(ARCH_I386), True),
|
||||
("pma16-01", "file", Format(FORMAT_PE), True),
|
||||
("pma16-01", "file", Format(FORMAT_ELF), False),
|
||||
# elf support
|
||||
@@ -524,6 +531,8 @@ FEATURE_PRESENCE_TESTS = sorted(
|
||||
("7351f.elf", "file", OS(OS_WINDOWS), False),
|
||||
("7351f.elf", "file", Format(FORMAT_ELF), True),
|
||||
("7351f.elf", "file", Format(FORMAT_PE), False),
|
||||
("7351f.elf", "file", Arch(ARCH_I386), False),
|
||||
("7351f.elf", "file", Arch(ARCH_AMD64), True),
|
||||
("7351f.elf", "function=0x408753", capa.features.common.String("/dev/null"), True),
|
||||
("7351f.elf", "function=0x408753,bb=0x408781", capa.features.insn.API("open"), True),
|
||||
],
|
||||
|
||||
@@ -16,15 +16,18 @@ import capa.features.common
|
||||
from capa.features.file import FunctionName
|
||||
from capa.features.insn import Number, Offset
|
||||
from capa.features.common import (
|
||||
OS,
|
||||
OS_LINUX,
|
||||
ARCH_I386,
|
||||
FORMAT_PE,
|
||||
ARCH_AMD64,
|
||||
FORMAT_ELF,
|
||||
OS_WINDOWS,
|
||||
OS_LINUX,
|
||||
BITNESS_X32,
|
||||
BITNESS_X64,
|
||||
Arch,
|
||||
Format,
|
||||
String,
|
||||
OS,
|
||||
Format
|
||||
)
|
||||
|
||||
|
||||
@@ -990,3 +993,21 @@ def test_format_features():
|
||||
children = list(r.statement.get_children())
|
||||
assert (Format(FORMAT_PE) in children) == True
|
||||
assert (Format(FORMAT_ELF) not in children) == True
|
||||
|
||||
|
||||
def test_arch_features():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
scope: file
|
||||
features:
|
||||
- and:
|
||||
- arch: amd64
|
||||
"""
|
||||
)
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
children = list(r.statement.get_children())
|
||||
assert (Arch(ARCH_AMD64) in children) == True
|
||||
assert (Arch(ARCH_I386) not in children) == True
|
||||
|
||||
Reference in New Issue
Block a user