diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff4d3eb2..4e114026f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,18 @@ ### New Features - add lint for detecting duplicate features in capa-rules #2250 @v1bh475u +- add span-of-calls scope to match features against a across a sliding window of API calls within a thread @williballenthin #2532 - ### Breaking Changes -### New Rules (2) +- add span-of-calls scope to rule format +- capabilities functions return dataclasses instead of tuples + +### New Rules (3) - data-manipulation/encryption/rsa/encrypt-data-using-rsa-via-embedded-library Ana06 - data-manipulation/encryption/use-bigint-function Ana06 +- nursery/dynamic-add-veh wballenthin@google.com - ### Bug Fixes diff --git a/capa/capabilities/common.py b/capa/capabilities/common.py index 4dd1ee58f..9acbd68f5 100644 --- a/capa/capabilities/common.py +++ b/capa/capabilities/common.py @@ -16,17 +16,28 @@ import logging import itertools import collections -from typing import Any +from typing import Optional +from dataclasses import dataclass from capa.rules import Scope, RuleSet from capa.engine import FeatureSet, MatchResults from capa.features.address import NO_ADDRESS +from capa.render.result_document import LibraryFunction, StaticFeatureCounts, DynamicFeatureCounts from capa.features.extractors.base_extractor import FeatureExtractor, StaticFeatureExtractor, DynamicFeatureExtractor logger = logging.getLogger(__name__) -def find_file_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, function_features: FeatureSet): +@dataclass +class FileCapabilities: + features: FeatureSet + matches: MatchResults + feature_count: int + + +def find_file_capabilities( + ruleset: RuleSet, extractor: FeatureExtractor, function_features: FeatureSet +) -> FileCapabilities: file_features: FeatureSet = collections.defaultdict(set) for feature, va in itertools.chain(extractor.extract_file_features(), extractor.extract_global_features()): @@ -43,8 +54,8 @@ def find_file_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, functi file_features.update(function_features) - _, matches = ruleset.match(Scope.FILE, file_features, NO_ADDRESS) - return matches, len(file_features) + features, matches = ruleset.match(Scope.FILE, file_features, NO_ADDRESS) + return FileCapabilities(features, matches, len(file_features)) def has_file_limitation(rules: RuleSet, capabilities: MatchResults, is_standalone=True) -> bool: @@ -69,9 +80,14 @@ def has_file_limitation(rules: RuleSet, capabilities: MatchResults, is_standalon return False -def find_capabilities( - ruleset: RuleSet, extractor: FeatureExtractor, disable_progress=None, **kwargs -) -> tuple[MatchResults, Any]: +@dataclass +class Capabilities: + matches: MatchResults + feature_counts: StaticFeatureCounts | DynamicFeatureCounts + library_functions: Optional[tuple[LibraryFunction, ...]] = None + + +def find_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, disable_progress=None, **kwargs) -> Capabilities: from capa.capabilities.static import find_static_capabilities from capa.capabilities.dynamic import find_dynamic_capabilities diff --git a/capa/capabilities/dynamic.py b/capa/capabilities/dynamic.py index 566a8e597..a84f5d3f7 100644 --- a/capa/capabilities/dynamic.py +++ b/capa/capabilities/dynamic.py @@ -16,26 +16,40 @@ import logging import itertools import collections -from typing import Any +from dataclasses import dataclass import capa.perf +import capa.engine +import capa.helpers import capa.features.freeze as frz import capa.render.result_document as rdoc from capa.rules import Scope, RuleSet from capa.engine import FeatureSet, MatchResults -from capa.capabilities.common import find_file_capabilities +from capa.features.address import _NoAddress +from capa.capabilities.common import Capabilities, find_file_capabilities from capa.features.extractors.base_extractor import CallHandle, ThreadHandle, ProcessHandle, DynamicFeatureExtractor logger = logging.getLogger(__name__) +# The number of calls that make up a span of calls. +# +# The larger this is, the more calls are grouped together to match rule logic. +# This means a longer chain can be recognized; however, its a bit more expensive. +SPAN_SIZE = 20 + + +@dataclass +class CallCapabilities: + features: FeatureSet + matches: MatchResults + + def find_call_capabilities( ruleset: RuleSet, extractor: DynamicFeatureExtractor, ph: ProcessHandle, th: ThreadHandle, ch: CallHandle -) -> tuple[FeatureSet, MatchResults]: +) -> CallCapabilities: """ find matches for the given rules for the given call. - - returns: tuple containing (features for call, match results for call) """ # all features found for the call. features: FeatureSet = collections.defaultdict(set) @@ -53,16 +67,105 @@ def find_call_capabilities( for addr, _ in res: capa.engine.index_rule_matches(features, rule, [addr]) - return features, matches + return CallCapabilities(features, matches) + + +@dataclass +class ThreadCapabilities: + features: FeatureSet + thread_matches: MatchResults + span_matches: MatchResults + call_matches: MatchResults + + +class SpanOfCallsMatcher: + def __init__(self, ruleset: RuleSet): + super().__init__() + self.ruleset = ruleset + + # matches found at the span scope. + self.matches: MatchResults = collections.defaultdict(list) + + # We match spans as the sliding window of calls with size SPAN_SIZE. + # + # For each call, we consider the window of SPAN_SIZE calls leading up to it, + # merging all their features and doing a match. + # + # We track these features in two data structures: + # 1. a deque of those features found in the prior calls. + # We'll append to it, and as it grows larger than SPAN_SIZE, the oldest items are removed. + # 2. a live set of features seen in the span. + # As we pop from the deque, we remove features from the current set, + # and as we push to the deque, we insert features to the current set. + # With this approach, our algorithm performance is independent of SPAN_SIZE. + # The naive algorithm, of merging all the trailing feature sets at each call, is dependent upon SPAN_SIZE + # (that is, runtime gets slower the larger SPAN_SIZE is). + self.current_feature_sets: collections.deque[FeatureSet] = collections.deque(maxlen=SPAN_SIZE) + self.current_features: FeatureSet = collections.defaultdict(set) + + # the names of rules matched at the last span, + # so that we can deduplicate long strings of the same matches. + self.last_span_matches: set[str] = set() + + def next(self, ch: CallHandle, call_features: FeatureSet): + # As we add items to the end of the deque, overflow and drop the oldest items (at the left end). + # While we could rely on `deque.append` with `maxlen` set (which we provide above), + # we want to use the dropped item first, to remove the old features, so we manually pop it here. + if len(self.current_feature_sets) == SPAN_SIZE: + overflowing_feature_set = self.current_feature_sets.popleft() + + for feature, vas in overflowing_feature_set.items(): + if len(vas) == 1 and isinstance(next(iter(vas)), _NoAddress): + # `vas == { NO_ADDRESS }` without the garbage. + # + # ignore the common case of global features getting added/removed/trimmed repeatedly, + # like arch/os/format. + continue + + self.current_features[feature] -= vas + if not self.current_features[feature]: + del self.current_features[feature] + + # update the deque and set of features with the latest call's worth of features. + self.current_feature_sets.append(call_features) + for feature, vas in call_features.items(): + self.current_features[feature] |= vas + + _, matches = self.ruleset.match(Scope.SPAN_OF_CALLS, self.current_features, ch.address) + + newly_encountered_rules = set(matches.keys()) - self.last_span_matches + + # don't emit match results for rules seen during the immediately preceeding spans. + # + # This means that we won't emit duplicate matches when there are multiple spans + # that overlap a single matching event. + # It also handles the case of a tight loop containing matched logic; + # only the first match will be recorded. + # + # In theory, this means the result document doesn't have *every* possible match location, + # but in practice, humans will only be interested in the first handful anyways. + suppressed_rules = set(self.last_span_matches) + + # however, if a newly encountered rule depends on a suppressed rule, + # don't suppress that rule match, or we won't be able to reconstruct the vverbose output. + # see: https://github.com/mandiant/capa/pull/2532#issuecomment-2548508130 + for new_rule in newly_encountered_rules: + suppressed_rules -= set(self.ruleset.rules[new_rule].get_dependencies(self.ruleset.rules_by_namespace)) + + for rule_name, res in matches.items(): + if rule_name in suppressed_rules: + continue + self.matches[rule_name].extend(res) + + self.last_span_matches = set(matches.keys()) def find_thread_capabilities( ruleset: RuleSet, extractor: DynamicFeatureExtractor, ph: ProcessHandle, th: ThreadHandle -) -> tuple[FeatureSet, MatchResults, MatchResults]: +) -> ThreadCapabilities: """ - find matches for the given rules within the given thread. - - returns: tuple containing (features for thread, match results for thread, match results for calls) + find matches for the given rules within the given thread, + which includes matches for all the spans and calls within it. """ # all features found within this thread, # includes features found within calls. @@ -72,14 +175,19 @@ def find_thread_capabilities( # might be found at different calls, that's ok. call_matches: MatchResults = collections.defaultdict(list) - for ch in extractor.get_calls(ph, th): - ifeatures, imatches = find_call_capabilities(ruleset, extractor, ph, th, ch) - for feature, vas in ifeatures.items(): + span_matcher = SpanOfCallsMatcher(ruleset) + + call_count = 0 + for call_count, ch in enumerate(extractor.get_calls(ph, th)): # noqa: B007 + call_capabilities = find_call_capabilities(ruleset, extractor, ph, th, ch) + for feature, vas in call_capabilities.features.items(): features[feature].update(vas) - for rule_name, res in imatches.items(): + for rule_name, res in call_capabilities.matches.items(): call_matches[rule_name].extend(res) + span_matcher.next(ch, call_capabilities.features) + for feature, va in itertools.chain(extractor.extract_thread_features(ph, th), extractor.extract_global_features()): features[feature].add(va) @@ -91,16 +199,31 @@ def find_thread_capabilities( for va, _ in res: capa.engine.index_rule_matches(features, rule, [va]) - return features, matches, call_matches + logger.debug( + "analyzed thread %d[%d] with %d events, %d features, and %d matches", + th.address.process.pid, + th.address.tid, + call_count, + len(features), + len(matches) + len(span_matcher.matches) + len(call_matches), + ) + return ThreadCapabilities(features, matches, span_matcher.matches, call_matches) + + +@dataclass +class ProcessCapabilities: + process_matches: MatchResults + thread_matches: MatchResults + span_matches: MatchResults + call_matches: MatchResults + feature_count: int def find_process_capabilities( ruleset: RuleSet, extractor: DynamicFeatureExtractor, ph: ProcessHandle -) -> tuple[MatchResults, MatchResults, MatchResults, int]: +) -> ProcessCapabilities: """ find matches for the given rules within the given process. - - returns: tuple containing (match results for process, match results for threads, match results for calls, number of features) """ # all features found within this process, # includes features found within threads (and calls). @@ -110,33 +233,48 @@ def find_process_capabilities( # might be found at different threads, that's ok. thread_matches: MatchResults = collections.defaultdict(list) + # matches found at the span-of-calls scope. + # might be found at different spans, that's ok. + span_matches: MatchResults = collections.defaultdict(list) + # matches found at the call scope. # might be found at different calls, that's ok. call_matches: MatchResults = collections.defaultdict(list) for th in extractor.get_threads(ph): - features, tmatches, cmatches = find_thread_capabilities(ruleset, extractor, ph, th) - for feature, vas in features.items(): + thread_capabilities = find_thread_capabilities(ruleset, extractor, ph, th) + for feature, vas in thread_capabilities.features.items(): process_features[feature].update(vas) - for rule_name, res in tmatches.items(): + for rule_name, res in thread_capabilities.thread_matches.items(): thread_matches[rule_name].extend(res) - for rule_name, res in cmatches.items(): + for rule_name, res in thread_capabilities.span_matches.items(): + span_matches[rule_name].extend(res) + + for rule_name, res in thread_capabilities.call_matches.items(): call_matches[rule_name].extend(res) for feature, va in itertools.chain(extractor.extract_process_features(ph), extractor.extract_global_features()): process_features[feature].add(va) _, process_matches = ruleset.match(Scope.PROCESS, process_features, ph.address) - return process_matches, thread_matches, call_matches, len(process_features) + + logger.debug( + "analyzed process %d and extracted %d features with %d matches", + ph.address.pid, + len(process_features), + len(process_matches), + ) + return ProcessCapabilities(process_matches, thread_matches, span_matches, call_matches, len(process_features)) def find_dynamic_capabilities( - ruleset: RuleSet, extractor: DynamicFeatureExtractor, disable_progress=None -) -> tuple[MatchResults, Any]: + ruleset: RuleSet, extractor: DynamicFeatureExtractor, disable_progress: bool = False +) -> Capabilities: all_process_matches: MatchResults = collections.defaultdict(list) all_thread_matches: MatchResults = collections.defaultdict(list) + all_span_matches: MatchResults = collections.defaultdict(list) all_call_matches: MatchResults = collections.defaultdict(list) feature_counts = rdoc.DynamicFeatureCounts(file=0, processes=()) @@ -150,19 +288,20 @@ def find_dynamic_capabilities( ) as pbar: task = pbar.add_task("matching", total=n_processes, unit="processes") for p in processes: - process_matches, thread_matches, call_matches, feature_count = find_process_capabilities( - ruleset, extractor, p - ) + process_capabilities = find_process_capabilities(ruleset, extractor, p) feature_counts.processes += ( - rdoc.ProcessFeatureCount(address=frz.Address.from_capa(p.address), count=feature_count), + rdoc.ProcessFeatureCount( + address=frz.Address.from_capa(p.address), count=process_capabilities.feature_count + ), ) - logger.debug("analyzed %s and extracted %d features", p.address, feature_count) - for rule_name, res in process_matches.items(): + for rule_name, res in process_capabilities.process_matches.items(): all_process_matches[rule_name].extend(res) - for rule_name, res in thread_matches.items(): + for rule_name, res in process_capabilities.thread_matches.items(): all_thread_matches[rule_name].extend(res) - for rule_name, res in call_matches.items(): + for rule_name, res in process_capabilities.span_matches.items(): + all_span_matches[rule_name].extend(res) + for rule_name, res in process_capabilities.call_matches.items(): all_call_matches[rule_name].extend(res) pbar.advance(task) @@ -171,29 +310,26 @@ def find_dynamic_capabilities( # mapping from feature (matched rule) to set of addresses at which it matched. process_and_lower_features: FeatureSet = collections.defaultdict(set) for rule_name, results in itertools.chain( - all_process_matches.items(), all_thread_matches.items(), all_call_matches.items() + all_process_matches.items(), all_thread_matches.items(), all_span_matches.items(), all_call_matches.items() ): locations = {p[0] for p in results} rule = ruleset[rule_name] capa.engine.index_rule_matches(process_and_lower_features, rule, locations) - all_file_matches, feature_count = find_file_capabilities(ruleset, extractor, process_and_lower_features) - feature_counts.file = feature_count + all_file_capabilities = find_file_capabilities(ruleset, extractor, process_and_lower_features) + feature_counts.file = all_file_capabilities.feature_count matches = dict( itertools.chain( # each rule exists in exactly one scope, # so there won't be any overlap among these following MatchResults, # and we can merge the dictionaries naively. + all_call_matches.items(), + all_span_matches.items(), all_thread_matches.items(), all_process_matches.items(), - all_call_matches.items(), - all_file_matches.items(), + all_file_capabilities.matches.items(), ) ) - meta = { - "feature_counts": feature_counts, - } - - return matches, meta + return Capabilities(matches, feature_counts) diff --git a/capa/capabilities/static.py b/capa/capabilities/static.py index b39b7b2eb..d485aa48c 100644 --- a/capa/capabilities/static.py +++ b/capa/capabilities/static.py @@ -17,7 +17,7 @@ import logging import itertools import collections -from typing import Any +from dataclasses import dataclass import capa.perf import capa.helpers @@ -25,19 +25,23 @@ import capa.render.result_document as rdoc from capa.rules import Scope, RuleSet from capa.engine import FeatureSet, MatchResults -from capa.capabilities.common import find_file_capabilities +from capa.capabilities.common import Capabilities, find_file_capabilities from capa.features.extractors.base_extractor import BBHandle, InsnHandle, FunctionHandle, StaticFeatureExtractor logger = logging.getLogger(__name__) +@dataclass +class InstructionCapabilities: + features: FeatureSet + matches: MatchResults + + def find_instruction_capabilities( ruleset: RuleSet, extractor: StaticFeatureExtractor, f: FunctionHandle, bb: BBHandle, insn: InsnHandle -) -> tuple[FeatureSet, MatchResults]: +) -> InstructionCapabilities: """ find matches for the given rules for the given instruction. - - returns: tuple containing (features for instruction, match results for instruction) """ # all features found for the instruction. features: FeatureSet = collections.defaultdict(set) @@ -55,16 +59,21 @@ def find_instruction_capabilities( for addr, _ in res: capa.engine.index_rule_matches(features, rule, [addr]) - return features, matches + return InstructionCapabilities(features, matches) + + +@dataclass +class BasicBlockCapabilities: + features: FeatureSet + basic_block_matches: MatchResults + instruction_matches: MatchResults def find_basic_block_capabilities( ruleset: RuleSet, extractor: StaticFeatureExtractor, f: FunctionHandle, bb: BBHandle -) -> tuple[FeatureSet, MatchResults, MatchResults]: +) -> BasicBlockCapabilities: """ find matches for the given rules within the given basic block. - - returns: tuple containing (features for basic block, match results for basic block, match results for instructions) """ # all features found within this basic block, # includes features found within instructions. @@ -75,11 +84,11 @@ def find_basic_block_capabilities( insn_matches: MatchResults = collections.defaultdict(list) for insn in extractor.get_instructions(f, bb): - ifeatures, imatches = find_instruction_capabilities(ruleset, extractor, f, bb, insn) - for feature, vas in ifeatures.items(): + instruction_capabilities = find_instruction_capabilities(ruleset, extractor, f, bb, insn) + for feature, vas in instruction_capabilities.features.items(): features[feature].update(vas) - for rule_name, res in imatches.items(): + for rule_name, res in instruction_capabilities.matches.items(): insn_matches[rule_name].extend(res) for feature, va in itertools.chain( @@ -95,16 +104,20 @@ def find_basic_block_capabilities( for va, _ in res: capa.engine.index_rule_matches(features, rule, [va]) - return features, matches, insn_matches + return BasicBlockCapabilities(features, matches, insn_matches) + + +@dataclass +class CodeCapabilities: + function_matches: MatchResults + basic_block_matches: MatchResults + instruction_matches: MatchResults + feature_count: int -def find_code_capabilities( - ruleset: RuleSet, extractor: StaticFeatureExtractor, fh: FunctionHandle -) -> tuple[MatchResults, MatchResults, MatchResults, int]: +def find_code_capabilities(ruleset: RuleSet, extractor: StaticFeatureExtractor, fh: FunctionHandle) -> CodeCapabilities: """ find matches for the given rules within the given function. - - returns: tuple containing (match results for function, match results for basic blocks, match results for instructions, number of features) """ # all features found within this function, # includes features found within basic blocks (and instructions). @@ -119,26 +132,26 @@ def find_code_capabilities( insn_matches: MatchResults = collections.defaultdict(list) for bb in extractor.get_basic_blocks(fh): - features, bmatches, imatches = find_basic_block_capabilities(ruleset, extractor, fh, bb) - for feature, vas in features.items(): + basic_block_capabilities = find_basic_block_capabilities(ruleset, extractor, fh, bb) + for feature, vas in basic_block_capabilities.features.items(): function_features[feature].update(vas) - for rule_name, res in bmatches.items(): + for rule_name, res in basic_block_capabilities.basic_block_matches.items(): bb_matches[rule_name].extend(res) - for rule_name, res in imatches.items(): + for rule_name, res in basic_block_capabilities.instruction_matches.items(): insn_matches[rule_name].extend(res) for feature, va in itertools.chain(extractor.extract_function_features(fh), extractor.extract_global_features()): function_features[feature].add(va) _, function_matches = ruleset.match(Scope.FUNCTION, function_features, fh.address) - return function_matches, bb_matches, insn_matches, len(function_features) + return CodeCapabilities(function_matches, bb_matches, insn_matches, len(function_features)) def find_static_capabilities( ruleset: RuleSet, extractor: StaticFeatureExtractor, disable_progress=None -) -> tuple[MatchResults, Any]: +) -> Capabilities: all_function_matches: MatchResults = collections.defaultdict(list) all_bb_matches: MatchResults = collections.defaultdict(list) all_insn_matches: MatchResults = collections.defaultdict(list) @@ -172,30 +185,36 @@ def find_static_capabilities( pbar.advance(task) continue - function_matches, bb_matches, insn_matches, feature_count = find_code_capabilities(ruleset, extractor, f) + code_capabilities = find_code_capabilities(ruleset, extractor, f) feature_counts.functions += ( - rdoc.FunctionFeatureCount(address=frz.Address.from_capa(f.address), count=feature_count), + rdoc.FunctionFeatureCount( + address=frz.Address.from_capa(f.address), count=code_capabilities.feature_count + ), ) t1 = time.time() match_count = 0 - for name, matches_ in itertools.chain(function_matches.items(), bb_matches.items(), insn_matches.items()): + for name, matches_ in itertools.chain( + code_capabilities.function_matches.items(), + code_capabilities.basic_block_matches.items(), + code_capabilities.instruction_matches.items(), + ): if not ruleset.rules[name].is_subscope_rule(): match_count += len(matches_) logger.debug( "analyzed function 0x%x and extracted %d features, %d matches in %0.02fs", f.address, - feature_count, + code_capabilities.feature_count, match_count, t1 - t0, ) - for rule_name, res in function_matches.items(): + for rule_name, res in code_capabilities.function_matches.items(): all_function_matches[rule_name].extend(res) - for rule_name, res in bb_matches.items(): + for rule_name, res in code_capabilities.basic_block_matches.items(): all_bb_matches[rule_name].extend(res) - for rule_name, res in insn_matches.items(): + for rule_name, res in code_capabilities.instruction_matches.items(): all_insn_matches[rule_name].extend(res) pbar.advance(task) @@ -210,8 +229,8 @@ def find_static_capabilities( rule = ruleset[rule_name] capa.engine.index_rule_matches(function_and_lower_features, rule, locations) - all_file_matches, feature_count = find_file_capabilities(ruleset, extractor, function_and_lower_features) - feature_counts.file = feature_count + all_file_capabilities = find_file_capabilities(ruleset, extractor, function_and_lower_features) + feature_counts.file = all_file_capabilities.feature_count matches: MatchResults = dict( itertools.chain( @@ -221,13 +240,8 @@ def find_static_capabilities( all_insn_matches.items(), all_bb_matches.items(), all_function_matches.items(), - all_file_matches.items(), + all_file_capabilities.matches.items(), ) ) - meta = { - "feature_counts": feature_counts, - "library_functions": library_functions, - } - - return matches, meta + return Capabilities(matches, feature_counts, library_functions) diff --git a/capa/features/address.py b/capa/features/address.py index 447b936f6..eb708a3dc 100644 --- a/capa/features/address.py +++ b/capa/features/address.py @@ -114,8 +114,7 @@ def __hash__(self): return hash((self.thread, self.id)) def __eq__(self, other): - assert isinstance(other, DynamicCallAddress) - return (self.thread, self.id) == (other.thread, other.id) + return isinstance(other, DynamicCallAddress) and (self.thread, self.id) == (other.thread, other.id) def __lt__(self, other): assert isinstance(other, DynamicCallAddress) diff --git a/capa/features/common.py b/capa/features/common.py index 5be8ffca9..44d42cceb 100644 --- a/capa/features/common.py +++ b/capa/features/common.py @@ -92,7 +92,7 @@ def __init__( self.success = success self.statement = statement self.children = children - self.locations = locations if locations is not None else set() + self.locations = frozenset(locations) if locations is not None else frozenset() def __eq__(self, other): if isinstance(other, bool): @@ -105,6 +105,25 @@ def __bool__(self): def __nonzero__(self): return self.success + def __str__(self): + # as this object isn't user facing, this formatting is just to help with debugging + + lines: list[str] = [] + + def rec(m: "Result", indent: int): + if isinstance(m.statement, capa.engine.Statement): + line = (" " * indent) + str(m.statement.name) + " " + str(m.success) + else: + line = (" " * indent) + str(m.statement) + " " + str(m.success) + " " + str(m.locations) + + lines.append(line) + + for child in m.children: + rec(child, indent + 1) + + rec(self, 0) + return "\n".join(lines) + class Feature(abc.ABC): # noqa: B024 # this is an abstract class, since we don't want anyone to instantiate it directly, @@ -175,7 +194,11 @@ def __repr__(self): def evaluate(self, features: "capa.engine.FeatureSet", short_circuit=True) -> Result: capa.perf.counters["evaluate.feature"] += 1 capa.perf.counters["evaluate.feature." + self.name] += 1 - return Result(self in features, self, [], locations=features.get(self, set())) + success = self in features + if success: + return Result(True, self, [], locations=features[self]) + else: + return Result(False, self, [], locations=None) class MatchedRule(Feature): diff --git a/capa/features/extractors/base_extractor.py b/capa/features/extractors/base_extractor.py index ba9b905b8..1be52d06b 100644 --- a/capa/features/extractors/base_extractor.py +++ b/capa/features/extractors/base_extractor.py @@ -488,11 +488,11 @@ def get_call_name(self, ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) -> raise NotImplementedError() -def ProcessFilter(extractor: DynamicFeatureExtractor, processes: set) -> DynamicFeatureExtractor: +def ProcessFilter(extractor: DynamicFeatureExtractor, pids: set[int]) -> 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) + yield from (f for f in original_get_processes() if f.address.pid in pids) # 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. @@ -504,4 +504,16 @@ def filtered_get_processes(self): return new_extractor +def ThreadFilter(extractor: DynamicFeatureExtractor, threads: set[Address]) -> DynamicFeatureExtractor: + original_get_threads = extractor.get_threads + + def filtered_get_threads(self, ph: ProcessHandle): + yield from (t for t in original_get_threads(ph) if t.address in threads) + + new_extractor = copy(extractor) + new_extractor.get_threads = MethodType(filtered_get_threads, extractor) # type: ignore + + return new_extractor + + FeatureExtractor: TypeAlias = Union[StaticFeatureExtractor, DynamicFeatureExtractor] diff --git a/capa/features/freeze/__init__.py b/capa/features/freeze/__init__.py index 08612f8ea..2e12d2ffd 100644 --- a/capa/features/freeze/__init__.py +++ b/capa/features/freeze/__init__.py @@ -64,7 +64,14 @@ class AddressType(str, Enum): class Address(HashableModel): type: AddressType - value: Union[int, tuple[int, ...], None] = None # None default value to support deserialization of NO_ADDRESS + value: Union[ + # for absolute, relative, file + int, + # for DNToken, Process, Thread, Call + tuple[int, ...], + # for NO_ADDRESS, + None, + ] = None # None default value to support deserialization of NO_ADDRESS @classmethod def from_capa(cls, a: capa.features.address.Address) -> "Address": diff --git a/capa/ghidra/capa_explorer.py b/capa/ghidra/capa_explorer.py index 5bac2e316..b07b34ba8 100644 --- a/capa/ghidra/capa_explorer.py +++ b/capa/ghidra/capa_explorer.py @@ -245,13 +245,13 @@ def get_capabilities(): meta = capa.ghidra.helpers.collect_metadata([rules_path]) extractor = capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor() - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, True) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, True) - if capa.capabilities.common.has_file_limitation(rules, capabilities, is_standalone=False): + if capa.capabilities.common.has_file_limitation(rules, capabilities.matches, is_standalone=False): popup("capa explorer encountered warnings during analysis. Please check the console output for more information.") # type: ignore [name-defined] # noqa: F821 logger.info("capa encountered warnings during analysis") - return capa.render.json.render(meta, rules, capabilities) + return capa.render.json.render(meta, rules, capabilities.matches) def get_locations(match_dict): diff --git a/capa/ghidra/capa_ghidra.py b/capa/ghidra/capa_ghidra.py index 6f47d98c0..8d095347d 100644 --- a/capa/ghidra/capa_ghidra.py +++ b/capa/ghidra/capa_ghidra.py @@ -81,23 +81,23 @@ def run_headless(): meta = capa.ghidra.helpers.collect_metadata([rules_path]) extractor = capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor() - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, False) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, False) - meta.analysis.feature_counts = counts["feature_counts"] - meta.analysis.library_functions = counts["library_functions"] - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta.analysis.feature_counts = capabilities.feature_counts + meta.analysis.library_functions = capabilities.library_functions + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) - if capa.capabilities.common.has_file_limitation(rules, capabilities, is_standalone=True): + if capa.capabilities.common.has_file_limitation(rules, capabilities.matches, is_standalone=True): logger.info("capa encountered warnings during analysis") if args.json: - print(capa.render.json.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.json.render(meta, rules, capabilities.matches)) # noqa: T201 elif args.vverbose: - print(capa.render.vverbose.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.vverbose.render(meta, rules, capabilities.matches)) # noqa: T201 elif args.verbose: - print(capa.render.verbose.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.verbose.render(meta, rules, capabilities.matches)) # noqa: T201 else: - print(capa.render.default.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.default.render(meta, rules, capabilities.matches)) # noqa: T201 return 0 @@ -131,21 +131,21 @@ def run_ui(): meta = capa.ghidra.helpers.collect_metadata([rules_path]) extractor = capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor() - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, True) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, True) - meta.analysis.feature_counts = counts["feature_counts"] - meta.analysis.library_functions = counts["library_functions"] - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta.analysis.feature_counts = capabilities.feature_counts + meta.analysis.library_functions = capabilities.library_functions + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) - if capa.capabilities.common.has_file_limitation(rules, capabilities, is_standalone=False): + if capa.capabilities.common.has_file_limitation(rules, capabilities.matches, is_standalone=False): logger.info("capa encountered warnings during analysis") if verbose == "vverbose": - print(capa.render.vverbose.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.vverbose.render(meta, rules, capabilities.matches)) # noqa: T201 elif verbose == "verbose": - print(capa.render.verbose.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.verbose.render(meta, rules, capabilities.matches)) # noqa: T201 else: - print(capa.render.default.render(meta, rules, capabilities)) # noqa: T201 + print(capa.render.default.render(meta, rules, capabilities.matches)) # noqa: T201 return 0 diff --git a/capa/ida/plugin/form.py b/capa/ida/plugin/form.py index 2cb65de95..d82f76db5 100644 --- a/capa/ida/plugin/form.py +++ b/capa/ida/plugin/form.py @@ -776,13 +776,15 @@ def slot_progress_feature_extraction(text): try: meta = capa.ida.helpers.collect_metadata([Path(settings.user[CAPA_SETTINGS_RULE_PATH])]) - capabilities, counts = capa.capabilities.common.find_capabilities( + capabilities = capa.capabilities.common.find_capabilities( ruleset, self.feature_extractor, disable_progress=True ) - meta.analysis.feature_counts = counts["feature_counts"] - meta.analysis.library_functions = counts["library_functions"] - meta.analysis.layout = capa.loader.compute_layout(ruleset, self.feature_extractor, capabilities) + meta.analysis.feature_counts = capabilities.feature_counts + meta.analysis.library_functions = capabilities.library_functions + meta.analysis.layout = capa.loader.compute_layout( + ruleset, self.feature_extractor, capabilities.matches + ) except UserCancelledError: logger.info("User cancelled analysis.") return False @@ -818,7 +820,7 @@ def slot_progress_feature_extraction(text): capa.ida.helpers.inform_user_ida_ui("capa encountered file type warnings during analysis") - if capa.capabilities.common.has_file_limitation(ruleset, capabilities, is_standalone=False): + if capa.capabilities.common.has_file_limitation(ruleset, capabilities.matches, is_standalone=False): capa.ida.helpers.inform_user_ida_ui("capa encountered file limitation warnings during analysis") except Exception as e: logger.exception("Failed to check for file limitations (error: %s)", e) @@ -832,7 +834,7 @@ def slot_progress_feature_extraction(text): try: self.resdoc_cache = capa.render.result_document.ResultDocument.from_capa( - meta, ruleset, capabilities + meta, ruleset, capabilities.matches ) except Exception as e: logger.exception("Failed to collect results (error: %s)", e) diff --git a/capa/loader.py b/capa/loader.py index a8a7db9f3..ec0295ac8 100644 --- a/capa/loader.py +++ b/capa/loader.py @@ -59,6 +59,7 @@ FORMAT_BINEXPORT2, ) from capa.features.address import Address +from capa.capabilities.common import Capabilities from capa.features.extractors.base_extractor import ( SampleHashes, FeatureExtractor, @@ -450,7 +451,7 @@ def get_signatures(sigs_path: Path) -> list[Path]: return paths -def get_sample_analysis(format_, arch, os_, extractor, rules_path, counts): +def get_sample_analysis(format_, arch, os_, extractor, rules_path, feature_counts, library_functions): if isinstance(extractor, StaticFeatureExtractor): return rdoc.StaticAnalysis( format=format_, @@ -466,8 +467,8 @@ def get_sample_analysis(format_, arch, os_, extractor, rules_path, counts): # # "functions": { 0x401000: { "matched_basic_blocks": [ 0x401000, 0x401005, ... ] }, ... } ), - feature_counts=counts["feature_counts"], - library_functions=counts["library_functions"], + feature_counts=feature_counts, + library_functions=library_functions, ) elif isinstance(extractor, DynamicFeatureExtractor): return rdoc.DynamicAnalysis( @@ -479,7 +480,7 @@ def get_sample_analysis(format_, arch, os_, extractor, rules_path, counts): layout=rdoc.DynamicLayout( processes=(), ), - feature_counts=counts["feature_counts"], + feature_counts=feature_counts, ) else: raise ValueError("invalid extractor type") @@ -492,7 +493,7 @@ def collect_metadata( os_: str, rules_path: list[Path], extractor: FeatureExtractor, - counts: dict, + capabilities: Capabilities, ) -> rdoc.Metadata: # if it's a binary sample we hash it, if it's a report # we fetch the hashes from the report @@ -535,7 +536,8 @@ def collect_metadata( os_, extractor, rules, - counts, + capabilities.feature_counts, + capabilities.library_functions, ), ) diff --git a/capa/main.py b/capa/main.py index c8b24595c..f962e5708 100644 --- a/capa/main.py +++ b/capa/main.py @@ -22,7 +22,7 @@ import textwrap import contextlib from types import TracebackType -from typing import Any, Optional, TypedDict +from typing import Optional, TypedDict from pathlib import Path import colorama @@ -47,7 +47,6 @@ import capa.render.result_document as rdoc import capa.features.extractors.common from capa.rules import RuleSet -from capa.engine import MatchResults from capa.loader import ( BACKEND_IDA, BACKEND_VIV, @@ -100,7 +99,7 @@ FORMAT_BINJA_DB, FORMAT_BINEXPORT2, ) -from capa.capabilities.common import find_capabilities, has_file_limitation, find_file_capabilities +from capa.capabilities.common import Capabilities, find_capabilities, has_file_limitation, find_file_capabilities from capa.features.extractors.base_extractor import ( ProcessFilter, FunctionFilter, @@ -761,7 +760,7 @@ def find_file_limitations_from_cli(args, rules: RuleSet, file_extractors: list[F found_file_limitation = False for file_extractor in file_extractors: try: - pure_file_capabilities, _ = find_file_capabilities(rules, file_extractor, {}) + 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.input_file, str(e)) raise ShouldExitError(E_CORRUPT_FILE) from e @@ -771,7 +770,7 @@ def find_file_limitations_from_cli(args, rules: RuleSet, file_extractors: list[F # file limitations that rely on non-file scope won't be detected here. # nor on FunctionName features, because pefile doesn't support this. - found_file_limitation = has_file_limitation(rules, pure_file_capabilities) + found_file_limitation = has_file_limitation(rules, pure_file_capabilities.matches) if found_file_limitation: # bail if capa encountered file limitation e.g. a packed binary # do show the output in verbose mode, though. @@ -974,8 +973,7 @@ def main(argv: Optional[list[str]] = None): return e.status_code meta: rdoc.Metadata - capabilities: MatchResults - counts: dict[str, Any] + capabilities: Capabilities if input_format == FORMAT_RESULT: # result document directly parses into meta, capabilities @@ -997,10 +995,12 @@ def main(argv: Optional[list[str]] = None): except ShouldExitError as e: return e.status_code - capabilities, counts = find_capabilities(rules, extractor, disable_progress=args.quiet) + capabilities = find_capabilities(rules, extractor, disable_progress=args.quiet) - meta = capa.loader.collect_metadata(argv, args.input_file, input_format, os_, args.rules, extractor, counts) - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta = capa.loader.collect_metadata( + argv, args.input_file, input_format, os_, args.rules, extractor, capabilities + ) + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) if isinstance(extractor, StaticFeatureExtractor) and found_file_limitation: # bail if capa's static feature extractor encountered file limitation e.g. a packed binary @@ -1009,13 +1009,13 @@ def main(argv: Optional[list[str]] = None): return E_FILE_LIMITATION if args.json: - print(capa.render.json.render(meta, rules, capabilities)) + print(capa.render.json.render(meta, rules, capabilities.matches)) elif args.vverbose: - print(capa.render.vverbose.render(meta, rules, capabilities)) + print(capa.render.vverbose.render(meta, rules, capabilities.matches)) elif args.verbose: - print(capa.render.verbose.render(meta, rules, capabilities)) + print(capa.render.verbose.render(meta, rules, capabilities.matches)) else: - print(capa.render.default.render(meta, rules, capabilities)) + print(capa.render.default.render(meta, rules, capabilities.matches)) colorama.deinit() logger.debug("done.") @@ -1051,16 +1051,16 @@ def ida_main(): meta = capa.ida.helpers.collect_metadata([rules_path]) - capabilities, counts = find_capabilities(rules, capa.features.extractors.ida.extractor.IdaFeatureExtractor()) + capabilities = find_capabilities(rules, capa.features.extractors.ida.extractor.IdaFeatureExtractor()) - meta.analysis.feature_counts = counts["feature_counts"] - meta.analysis.library_functions = counts["library_functions"] + meta.analysis.feature_counts = capabilities.feature_counts + meta.analysis.library_functions = capabilities.library_functions - if has_file_limitation(rules, capabilities, is_standalone=False): + if has_file_limitation(rules, capabilities.matches, is_standalone=False): capa.ida.helpers.inform_user_ida_ui("capa encountered warnings during analysis") colorama.init(strip=True) - print(capa.render.default.render(meta, rules, capabilities)) + print(capa.render.default.render(meta, rules, capabilities.matches)) def ghidra_main(): @@ -1085,19 +1085,19 @@ def ghidra_main(): meta = capa.ghidra.helpers.collect_metadata([rules_path]) - capabilities, counts = find_capabilities( + capabilities = find_capabilities( rules, capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor(), not capa.ghidra.helpers.is_running_headless(), ) - meta.analysis.feature_counts = counts["feature_counts"] - meta.analysis.library_functions = counts["library_functions"] + meta.analysis.feature_counts = capabilities.feature_counts + meta.analysis.library_functions = capabilities.library_functions - if has_file_limitation(rules, capabilities, is_standalone=False): + if has_file_limitation(rules, capabilities.matches, is_standalone=False): logger.info("capa encountered warnings during analysis") - print(capa.render.default.render(meta, rules, capabilities)) + print(capa.render.default.render(meta, rules, capabilities.matches)) if __name__ == "__main__": diff --git a/capa/render/proto/__init__.py b/capa/render/proto/__init__.py index e72add4d5..31b272e52 100644 --- a/capa/render/proto/__init__.py +++ b/capa/render/proto/__init__.py @@ -163,6 +163,8 @@ def scope_to_pb2(scope: capa.rules.Scope) -> capa_pb2.Scope.ValueType: return capa_pb2.Scope.SCOPE_PROCESS elif scope == capa.rules.Scope.THREAD: return capa_pb2.Scope.SCOPE_THREAD + elif scope == capa.rules.Scope.SPAN_OF_CALLS: + return capa_pb2.Scope.SCOPE_SPAN_OF_CALLS elif scope == capa.rules.Scope.CALL: return capa_pb2.Scope.SCOPE_CALL else: @@ -655,6 +657,8 @@ def scope_from_pb2(scope: capa_pb2.Scope.ValueType) -> capa.rules.Scope: return capa.rules.Scope.PROCESS elif scope == capa_pb2.Scope.SCOPE_THREAD: return capa.rules.Scope.THREAD + elif scope == capa_pb2.Scope.SCOPE_SPAN_OF_CALLS: + return capa.rules.Scope.SPAN_OF_CALLS elif scope == capa_pb2.Scope.SCOPE_CALL: return capa.rules.Scope.CALL else: diff --git a/capa/render/proto/capa.proto b/capa/render/proto/capa.proto index 2c0964c7b..b07234748 100644 --- a/capa/render/proto/capa.proto +++ b/capa/render/proto/capa.proto @@ -378,6 +378,7 @@ enum Scope { SCOPE_PROCESS = 5; SCOPE_THREAD = 6; SCOPE_CALL = 7; + SCOPE_SPAN_OF_CALLS = 8; } message Scopes { diff --git a/capa/render/proto/capa_pb2.py b/capa/render/proto/capa_pb2.py index 8b55fed4f..b581c9ea7 100644 --- a/capa/render/proto/capa_pb2.py +++ b/capa/render/proto/capa_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: capa/render/proto/capa.proto +# Protobuf Python Version: 5.28.3 """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 28, + 3, + '', + 'capa/render/proto/capa.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -13,159 +24,159 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x63\x61pa/render/proto/capa.proto\x12\rmandiant.capa\"Q\n\nAPIFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03\x61pi\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xb3\x02\n\x07\x41\x64\x64ress\x12(\n\x04type\x18\x01 \x01(\x0e\x32\x1a.mandiant.capa.AddressType\x12#\n\x01v\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.IntegerH\x00\x12\x33\n\x0ctoken_offset\x18\x03 \x01(\x0b\x32\x1b.mandiant.capa.Token_OffsetH\x00\x12+\n\x08ppid_pid\x18\x04 \x01(\x0b\x32\x17.mandiant.capa.Ppid_PidH\x00\x12\x33\n\x0cppid_pid_tid\x18\x05 \x01(\x0b\x32\x1b.mandiant.capa.Ppid_Pid_TidH\x00\x12\x39\n\x0fppid_pid_tid_id\x18\x06 \x01(\x0b\x32\x1e.mandiant.capa.Ppid_Pid_Tid_IdH\x00\x42\x07\n\x05value\"\x9c\x02\n\x08\x41nalysis\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\n\n\x02os\x18\x03 \x01(\t\x12\x11\n\textractor\x18\x04 \x01(\t\x12\r\n\x05rules\x18\x05 \x03(\t\x12,\n\x0c\x62\x61se_address\x18\x06 \x01(\x0b\x32\x16.mandiant.capa.Address\x12%\n\x06layout\x18\x07 \x01(\x0b\x32\x15.mandiant.capa.Layout\x12\x34\n\x0e\x66\x65\x61ture_counts\x18\x08 \x01(\x0b\x32\x1c.mandiant.capa.FeatureCounts\x12\x39\n\x11library_functions\x18\t \x03(\x0b\x32\x1e.mandiant.capa.LibraryFunction\"S\n\x0b\x41rchFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"`\n\nAttackSpec\x12\r\n\x05parts\x18\x01 \x03(\t\x12\x0e\n\x06tactic\x18\x02 \x01(\t\x12\x11\n\ttechnique\x18\x03 \x01(\t\x12\x14\n\x0csubtechnique\x18\x04 \x01(\t\x12\n\n\x02id\x18\x05 \x01(\t\"K\n\x11\x42\x61sicBlockFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\";\n\x10\x42\x61sicBlockLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\"U\n\x0c\x42ytesFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05\x62ytes\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"g\n\x15\x43haracteristicFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x16\n\x0e\x63haracteristic\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"V\n\x0c\x43lassFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06\x63lass_\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"K\n\x11\x43ompoundStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xc8\x01\n\x0f\x44ynamicAnalysis\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\n\n\x02os\x18\x03 \x01(\t\x12\x11\n\textractor\x18\x04 \x01(\t\x12\r\n\x05rules\x18\x05 \x03(\t\x12,\n\x06layout\x18\x06 \x01(\x0b\x32\x1c.mandiant.capa.DynamicLayout\x12;\n\x0e\x66\x65\x61ture_counts\x18\x07 \x01(\x0b\x32#.mandiant.capa.DynamicFeatureCounts\"[\n\x14\x44ynamicFeatureCounts\x12\x0c\n\x04\x66ile\x18\x01 \x01(\x04\x12\x35\n\tprocesses\x18\x02 \x03(\x0b\x32\".mandiant.capa.ProcessFeatureCount\"@\n\rDynamicLayout\x12/\n\tprocesses\x18\x01 \x03(\x0b\x32\x1c.mandiant.capa.ProcessLayout\"W\n\rExportFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06\x65xport\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"U\n\rFeatureCounts\x12\x0c\n\x04\x66ile\x18\x01 \x01(\x04\x12\x36\n\tfunctions\x18\x02 \x03(\x0b\x32#.mandiant.capa.FunctionFeatureCount\"\xb9\t\n\x0b\x46\x65\x61tureNode\x12\x0c\n\x04type\x18\x01 \x01(\t\x12&\n\x02os\x18\x02 \x01(\x0b\x32\x18.mandiant.capa.OSFeatureH\x00\x12*\n\x04\x61rch\x18\x03 \x01(\x0b\x32\x1a.mandiant.capa.ArchFeatureH\x00\x12.\n\x06\x66ormat\x18\x04 \x01(\x0b\x32\x1c.mandiant.capa.FormatFeatureH\x00\x12,\n\x05match\x18\x05 \x01(\x0b\x32\x1b.mandiant.capa.MatchFeatureH\x00\x12>\n\x0e\x63haracteristic\x18\x06 \x01(\x0b\x32$.mandiant.capa.CharacteristicFeatureH\x00\x12.\n\x06\x65xport\x18\x07 \x01(\x0b\x32\x1c.mandiant.capa.ExportFeatureH\x00\x12/\n\x07import_\x18\x08 \x01(\x0b\x32\x1c.mandiant.capa.ImportFeatureH\x00\x12\x30\n\x07section\x18\t \x01(\x0b\x32\x1d.mandiant.capa.SectionFeatureH\x00\x12;\n\rfunction_name\x18\n \x01(\x0b\x32\".mandiant.capa.FunctionNameFeatureH\x00\x12\x34\n\tsubstring\x18\x0b \x01(\x0b\x32\x1f.mandiant.capa.SubstringFeatureH\x00\x12,\n\x05regex\x18\x0c \x01(\x0b\x32\x1b.mandiant.capa.RegexFeatureH\x00\x12.\n\x06string\x18\r \x01(\x0b\x32\x1c.mandiant.capa.StringFeatureH\x00\x12-\n\x06\x63lass_\x18\x0e \x01(\x0b\x32\x1b.mandiant.capa.ClassFeatureH\x00\x12\x34\n\tnamespace\x18\x0f \x01(\x0b\x32\x1f.mandiant.capa.NamespaceFeatureH\x00\x12(\n\x03\x61pi\x18\x10 \x01(\x0b\x32\x19.mandiant.capa.APIFeatureH\x00\x12\x33\n\tproperty_\x18\x11 \x01(\x0b\x32\x1e.mandiant.capa.PropertyFeatureH\x00\x12.\n\x06number\x18\x12 \x01(\x0b\x32\x1c.mandiant.capa.NumberFeatureH\x00\x12,\n\x05\x62ytes\x18\x13 \x01(\x0b\x32\x1b.mandiant.capa.BytesFeatureH\x00\x12.\n\x06offset\x18\x14 \x01(\x0b\x32\x1c.mandiant.capa.OffsetFeatureH\x00\x12\x32\n\x08mnemonic\x18\x15 \x01(\x0b\x32\x1e.mandiant.capa.MnemonicFeatureH\x00\x12=\n\x0eoperand_number\x18\x16 \x01(\x0b\x32#.mandiant.capa.OperandNumberFeatureH\x00\x12=\n\x0eoperand_offset\x18\x17 \x01(\x0b\x32#.mandiant.capa.OperandOffsetFeatureH\x00\x12\x37\n\x0b\x62\x61sic_block\x18\x18 \x01(\x0b\x32 .mandiant.capa.BasicBlockFeatureH\x00\x42\t\n\x07\x66\x65\x61ture\"W\n\rFormatFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06\x66ormat\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"N\n\x14\x46unctionFeatureCount\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\r\n\x05\x63ount\x18\x02 \x01(\x04\"x\n\x0e\x46unctionLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12=\n\x14matched_basic_blocks\x18\x02 \x03(\x0b\x32\x1f.mandiant.capa.BasicBlockLayout\"d\n\x13\x46unctionNameFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x15\n\rfunction_name\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"X\n\rImportFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07import_\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\":\n\x06Layout\x12\x30\n\tfunctions\x18\x01 \x03(\x0b\x32\x1d.mandiant.capa.FunctionLayout\"H\n\x0fLibraryFunction\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x0c\n\x04name\x18\x02 \x01(\t\"Y\n\x07MBCSpec\x12\r\n\x05parts\x18\x01 \x03(\t\x12\x11\n\tobjective\x18\x02 \x01(\t\x12\x10\n\x08\x62\x65havior\x18\x03 \x01(\t\x12\x0e\n\x06method\x18\x04 \x01(\t\x12\n\n\x02id\x18\x05 \x01(\t\"\x9a\x01\n\x0cMaecMetadata\x12\x1b\n\x13\x61nalysis_conclusion\x18\x01 \x01(\t\x12\x1e\n\x16\x61nalysis_conclusion_ov\x18\x02 \x01(\t\x12\x16\n\x0emalware_family\x18\x03 \x01(\t\x12\x18\n\x10malware_category\x18\x04 \x01(\t\x12\x1b\n\x13malware_category_ov\x18\x05 \x01(\t\"\xd6\x02\n\x05Match\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x31\n\tstatement\x18\x02 \x01(\x0b\x32\x1c.mandiant.capa.StatementNodeH\x00\x12-\n\x07\x66\x65\x61ture\x18\x03 \x01(\x0b\x32\x1a.mandiant.capa.FeatureNodeH\x00\x12&\n\x08\x63hildren\x18\x05 \x03(\x0b\x32\x14.mandiant.capa.Match\x12)\n\tlocations\x18\x06 \x03(\x0b\x32\x16.mandiant.capa.Address\x12\x34\n\x08\x63\x61ptures\x18\x07 \x03(\x0b\x32\".mandiant.capa.Match.CapturesEntry\x1aI\n\rCapturesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.mandiant.capa.Addresses:\x02\x38\x01\x42\x06\n\x04node\"U\n\x0cMatchFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05match\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xbc\x02\n\x08Metadata\x12\x11\n\ttimestamp\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0c\n\x04\x61rgv\x18\x03 \x03(\t\x12%\n\x06sample\x18\x04 \x01(\x0b\x32\x15.mandiant.capa.Sample\x12-\n\x08\x61nalysis\x18\x05 \x01(\x0b\x32\x17.mandiant.capa.AnalysisB\x02\x18\x01\x12%\n\x06\x66lavor\x18\x06 \x01(\x0e\x32\x15.mandiant.capa.Flavor\x12\x38\n\x0fstatic_analysis\x18\x07 \x01(\x0b\x32\x1d.mandiant.capa.StaticAnalysisH\x00\x12:\n\x10\x64ynamic_analysis\x18\x08 \x01(\x0b\x32\x1e.mandiant.capa.DynamicAnalysisH\x00\x42\x0b\n\tanalysis2\"[\n\x0fMnemonicFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x10\n\x08mnemonic\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"]\n\x10NamespaceFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"n\n\rNumberFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12%\n\x06number\x18\x02 \x01(\x0b\x32\x15.mandiant.capa.Number\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"O\n\tOSFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\n\n\x02os\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"o\n\rOffsetFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12&\n\x06offset\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x01\n\x14OperandNumberFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05index\x18\x02 \x01(\r\x12.\n\x0eoperand_number\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x01\n\x14OperandOffsetFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05index\x18\x02 \x01(\r\x12.\n\x0eoperand_offset\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"M\n\x13ProcessFeatureCount\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\r\n\x05\x63ount\x18\x02 \x01(\x04\"|\n\rProcessLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x34\n\x0fmatched_threads\x18\x02 \x03(\x0b\x32\x1b.mandiant.capa.ThreadLayout\x12\x0c\n\x04name\x18\x03 \x01(\t\"|\n\x0fPropertyFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x11\n\tproperty_\x18\x02 \x01(\t\x12\x13\n\x06\x61\x63\x63\x65ss\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_accessB\x0e\n\x0c_description\"\x8d\x01\n\x0eRangeStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03min\x18\x02 \x01(\x04\x12\x0b\n\x03max\x18\x03 \x01(\x04\x12)\n\x05\x63hild\x18\x04 \x01(\x0b\x32\x1a.mandiant.capa.FeatureNode\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"U\n\x0cRegexFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05regex\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xba\x01\n\x0eResultDocument\x12%\n\x04meta\x18\x01 \x01(\x0b\x32\x17.mandiant.capa.Metadata\x12\x37\n\x05rules\x18\x02 \x03(\x0b\x32(.mandiant.capa.ResultDocument.RulesEntry\x1aH\n\nRulesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12)\n\x05value\x18\x02 \x01(\x0b\x32\x1a.mandiant.capa.RuleMatches:\x02\x38\x01\"|\n\x0bRuleMatches\x12)\n\x04meta\x18\x01 \x01(\x0b\x32\x1b.mandiant.capa.RuleMetadata\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x32\n\x07matches\x18\x03 \x03(\x0b\x32!.mandiant.capa.Pair_Address_Match\"\xed\x02\n\x0cRuleMetadata\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x0f\n\x07\x61uthors\x18\x03 \x03(\t\x12\'\n\x05scope\x18\x04 \x01(\x0e\x32\x14.mandiant.capa.ScopeB\x02\x18\x01\x12)\n\x06\x61ttack\x18\x05 \x03(\x0b\x32\x19.mandiant.capa.AttackSpec\x12#\n\x03mbc\x18\x06 \x03(\x0b\x32\x16.mandiant.capa.MBCSpec\x12\x12\n\nreferences\x18\x07 \x03(\t\x12\x10\n\x08\x65xamples\x18\x08 \x03(\t\x12\x13\n\x0b\x64\x65scription\x18\t \x01(\t\x12\x0b\n\x03lib\x18\n \x01(\x08\x12)\n\x04maec\x18\x0b \x01(\x0b\x32\x1b.mandiant.capa.MaecMetadata\x12\x18\n\x10is_subscope_rule\x18\x0c \x01(\x08\x12%\n\x06scopes\x18\r \x01(\x0b\x32\x15.mandiant.capa.Scopes\"A\n\x06Sample\x12\x0b\n\x03md5\x18\x01 \x01(\t\x12\x0c\n\x04sha1\x18\x02 \x01(\t\x12\x0e\n\x06sha256\x18\x03 \x01(\t\x12\x0c\n\x04path\x18\x04 \x01(\t\"v\n\x06Scopes\x12)\n\x06static\x18\x01 \x01(\x0e\x32\x14.mandiant.capa.ScopeH\x00\x88\x01\x01\x12*\n\x07\x64ynamic\x18\x02 \x01(\x0e\x32\x14.mandiant.capa.ScopeH\x01\x88\x01\x01\x42\t\n\x07_staticB\n\n\x08_dynamic\"Y\n\x0eSectionFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07section\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"V\n\rSomeStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xf4\x01\n\rStatementNode\x12\x0c\n\x04type\x18\x01 \x01(\t\x12.\n\x05range\x18\x02 \x01(\x0b\x32\x1d.mandiant.capa.RangeStatementH\x00\x12,\n\x04some\x18\x03 \x01(\x0b\x32\x1c.mandiant.capa.SomeStatementH\x00\x12\x34\n\x08subscope\x18\x04 \x01(\x0b\x32 .mandiant.capa.SubscopeStatementH\x00\x12\x34\n\x08\x63ompound\x18\x05 \x01(\x0b\x32 .mandiant.capa.CompoundStatementH\x00\x42\x0b\n\tstatement\"\xae\x02\n\x0eStaticAnalysis\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\n\n\x02os\x18\x03 \x01(\t\x12\x11\n\textractor\x18\x04 \x01(\t\x12\r\n\x05rules\x18\x05 \x03(\t\x12,\n\x0c\x62\x61se_address\x18\x06 \x01(\x0b\x32\x16.mandiant.capa.Address\x12+\n\x06layout\x18\x07 \x01(\x0b\x32\x1b.mandiant.capa.StaticLayout\x12:\n\x0e\x66\x65\x61ture_counts\x18\x08 \x01(\x0b\x32\".mandiant.capa.StaticFeatureCounts\x12\x39\n\x11library_functions\x18\t \x03(\x0b\x32\x1e.mandiant.capa.LibraryFunction\"[\n\x13StaticFeatureCounts\x12\x0c\n\x04\x66ile\x18\x01 \x01(\x04\x12\x36\n\tfunctions\x18\x02 \x03(\x0b\x32#.mandiant.capa.FunctionFeatureCount\"@\n\x0cStaticLayout\x12\x30\n\tfunctions\x18\x01 \x03(\x0b\x32\x1d.mandiant.capa.FunctionLayout\"W\n\rStringFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06string\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"p\n\x11SubscopeStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12#\n\x05scope\x18\x02 \x01(\x0e\x32\x14.mandiant.capa.Scope\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"]\n\x10SubstringFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x11\n\tsubstring\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"C\n\nCallLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x0c\n\x04name\x18\x02 \x01(\t\"i\n\x0cThreadLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x30\n\rmatched_calls\x18\x02 \x03(\x0b\x32\x19.mandiant.capa.CallLayout\"4\n\tAddresses\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x03(\x0b\x32\x16.mandiant.capa.Address\"b\n\x12Pair_Address_Match\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12#\n\x05match\x18\x02 \x01(\x0b\x32\x14.mandiant.capa.Match\"E\n\x0cToken_Offset\x12%\n\x05token\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x0e\n\x06offset\x18\x02 \x01(\x04\"U\n\x08Ppid_Pid\x12$\n\x04ppid\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03pid\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\"~\n\x0cPpid_Pid_Tid\x12$\n\x04ppid\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03pid\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03tid\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\"\xa5\x01\n\x0fPpid_Pid_Tid_Id\x12$\n\x04ppid\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03pid\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03tid\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\"\n\x02id\x18\x04 \x01(\x0b\x32\x16.mandiant.capa.Integer\",\n\x07Integer\x12\x0b\n\x01u\x18\x01 \x01(\x04H\x00\x12\x0b\n\x01i\x18\x02 \x01(\x12H\x00\x42\x07\n\x05value\"8\n\x06Number\x12\x0b\n\x01u\x18\x01 \x01(\x04H\x00\x12\x0b\n\x01i\x18\x02 \x01(\x12H\x00\x12\x0b\n\x01\x66\x18\x03 \x01(\x01H\x00\x42\x07\n\x05value*\x92\x02\n\x0b\x41\x64\x64ressType\x12\x1b\n\x17\x41\x44\x44RESSTYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x41\x44\x44RESSTYPE_ABSOLUTE\x10\x01\x12\x18\n\x14\x41\x44\x44RESSTYPE_RELATIVE\x10\x02\x12\x14\n\x10\x41\x44\x44RESSTYPE_FILE\x10\x03\x12\x18\n\x14\x41\x44\x44RESSTYPE_DN_TOKEN\x10\x04\x12\x1f\n\x1b\x41\x44\x44RESSTYPE_DN_TOKEN_OFFSET\x10\x05\x12\x1a\n\x16\x41\x44\x44RESSTYPE_NO_ADDRESS\x10\x06\x12\x17\n\x13\x41\x44\x44RESSTYPE_PROCESS\x10\x07\x12\x16\n\x12\x41\x44\x44RESSTYPE_THREAD\x10\x08\x12\x14\n\x10\x41\x44\x44RESSTYPE_CALL\x10\t*G\n\x06\x46lavor\x12\x16\n\x12\x46LAVOR_UNSPECIFIED\x10\x00\x12\x11\n\rFLAVOR_STATIC\x10\x01\x12\x12\n\x0e\x46LAVOR_DYNAMIC\x10\x02*\xa5\x01\n\x05Scope\x12\x15\n\x11SCOPE_UNSPECIFIED\x10\x00\x12\x0e\n\nSCOPE_FILE\x10\x01\x12\x12\n\x0eSCOPE_FUNCTION\x10\x02\x12\x15\n\x11SCOPE_BASIC_BLOCK\x10\x03\x12\x15\n\x11SCOPE_INSTRUCTION\x10\x04\x12\x11\n\rSCOPE_PROCESS\x10\x05\x12\x10\n\x0cSCOPE_THREAD\x10\x06\x12\x0e\n\nSCOPE_CALL\x10\x07\x62\x06proto3') - -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'capa.render.proto.capa_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x63\x61pa/render/proto/capa.proto\x12\rmandiant.capa\"Q\n\nAPIFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03\x61pi\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xb3\x02\n\x07\x41\x64\x64ress\x12(\n\x04type\x18\x01 \x01(\x0e\x32\x1a.mandiant.capa.AddressType\x12#\n\x01v\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.IntegerH\x00\x12\x33\n\x0ctoken_offset\x18\x03 \x01(\x0b\x32\x1b.mandiant.capa.Token_OffsetH\x00\x12+\n\x08ppid_pid\x18\x04 \x01(\x0b\x32\x17.mandiant.capa.Ppid_PidH\x00\x12\x33\n\x0cppid_pid_tid\x18\x05 \x01(\x0b\x32\x1b.mandiant.capa.Ppid_Pid_TidH\x00\x12\x39\n\x0fppid_pid_tid_id\x18\x06 \x01(\x0b\x32\x1e.mandiant.capa.Ppid_Pid_Tid_IdH\x00\x42\x07\n\x05value\"\x9c\x02\n\x08\x41nalysis\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\n\n\x02os\x18\x03 \x01(\t\x12\x11\n\textractor\x18\x04 \x01(\t\x12\r\n\x05rules\x18\x05 \x03(\t\x12,\n\x0c\x62\x61se_address\x18\x06 \x01(\x0b\x32\x16.mandiant.capa.Address\x12%\n\x06layout\x18\x07 \x01(\x0b\x32\x15.mandiant.capa.Layout\x12\x34\n\x0e\x66\x65\x61ture_counts\x18\x08 \x01(\x0b\x32\x1c.mandiant.capa.FeatureCounts\x12\x39\n\x11library_functions\x18\t \x03(\x0b\x32\x1e.mandiant.capa.LibraryFunction\"S\n\x0b\x41rchFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"`\n\nAttackSpec\x12\r\n\x05parts\x18\x01 \x03(\t\x12\x0e\n\x06tactic\x18\x02 \x01(\t\x12\x11\n\ttechnique\x18\x03 \x01(\t\x12\x14\n\x0csubtechnique\x18\x04 \x01(\t\x12\n\n\x02id\x18\x05 \x01(\t\"K\n\x11\x42\x61sicBlockFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\";\n\x10\x42\x61sicBlockLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\"U\n\x0c\x42ytesFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05\x62ytes\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"g\n\x15\x43haracteristicFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x16\n\x0e\x63haracteristic\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"V\n\x0c\x43lassFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06\x63lass_\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"K\n\x11\x43ompoundStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xc8\x01\n\x0f\x44ynamicAnalysis\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\n\n\x02os\x18\x03 \x01(\t\x12\x11\n\textractor\x18\x04 \x01(\t\x12\r\n\x05rules\x18\x05 \x03(\t\x12,\n\x06layout\x18\x06 \x01(\x0b\x32\x1c.mandiant.capa.DynamicLayout\x12;\n\x0e\x66\x65\x61ture_counts\x18\x07 \x01(\x0b\x32#.mandiant.capa.DynamicFeatureCounts\"[\n\x14\x44ynamicFeatureCounts\x12\x0c\n\x04\x66ile\x18\x01 \x01(\x04\x12\x35\n\tprocesses\x18\x02 \x03(\x0b\x32\".mandiant.capa.ProcessFeatureCount\"@\n\rDynamicLayout\x12/\n\tprocesses\x18\x01 \x03(\x0b\x32\x1c.mandiant.capa.ProcessLayout\"W\n\rExportFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06\x65xport\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"U\n\rFeatureCounts\x12\x0c\n\x04\x66ile\x18\x01 \x01(\x04\x12\x36\n\tfunctions\x18\x02 \x03(\x0b\x32#.mandiant.capa.FunctionFeatureCount\"\xb9\t\n\x0b\x46\x65\x61tureNode\x12\x0c\n\x04type\x18\x01 \x01(\t\x12&\n\x02os\x18\x02 \x01(\x0b\x32\x18.mandiant.capa.OSFeatureH\x00\x12*\n\x04\x61rch\x18\x03 \x01(\x0b\x32\x1a.mandiant.capa.ArchFeatureH\x00\x12.\n\x06\x66ormat\x18\x04 \x01(\x0b\x32\x1c.mandiant.capa.FormatFeatureH\x00\x12,\n\x05match\x18\x05 \x01(\x0b\x32\x1b.mandiant.capa.MatchFeatureH\x00\x12>\n\x0e\x63haracteristic\x18\x06 \x01(\x0b\x32$.mandiant.capa.CharacteristicFeatureH\x00\x12.\n\x06\x65xport\x18\x07 \x01(\x0b\x32\x1c.mandiant.capa.ExportFeatureH\x00\x12/\n\x07import_\x18\x08 \x01(\x0b\x32\x1c.mandiant.capa.ImportFeatureH\x00\x12\x30\n\x07section\x18\t \x01(\x0b\x32\x1d.mandiant.capa.SectionFeatureH\x00\x12;\n\rfunction_name\x18\n \x01(\x0b\x32\".mandiant.capa.FunctionNameFeatureH\x00\x12\x34\n\tsubstring\x18\x0b \x01(\x0b\x32\x1f.mandiant.capa.SubstringFeatureH\x00\x12,\n\x05regex\x18\x0c \x01(\x0b\x32\x1b.mandiant.capa.RegexFeatureH\x00\x12.\n\x06string\x18\r \x01(\x0b\x32\x1c.mandiant.capa.StringFeatureH\x00\x12-\n\x06\x63lass_\x18\x0e \x01(\x0b\x32\x1b.mandiant.capa.ClassFeatureH\x00\x12\x34\n\tnamespace\x18\x0f \x01(\x0b\x32\x1f.mandiant.capa.NamespaceFeatureH\x00\x12(\n\x03\x61pi\x18\x10 \x01(\x0b\x32\x19.mandiant.capa.APIFeatureH\x00\x12\x33\n\tproperty_\x18\x11 \x01(\x0b\x32\x1e.mandiant.capa.PropertyFeatureH\x00\x12.\n\x06number\x18\x12 \x01(\x0b\x32\x1c.mandiant.capa.NumberFeatureH\x00\x12,\n\x05\x62ytes\x18\x13 \x01(\x0b\x32\x1b.mandiant.capa.BytesFeatureH\x00\x12.\n\x06offset\x18\x14 \x01(\x0b\x32\x1c.mandiant.capa.OffsetFeatureH\x00\x12\x32\n\x08mnemonic\x18\x15 \x01(\x0b\x32\x1e.mandiant.capa.MnemonicFeatureH\x00\x12=\n\x0eoperand_number\x18\x16 \x01(\x0b\x32#.mandiant.capa.OperandNumberFeatureH\x00\x12=\n\x0eoperand_offset\x18\x17 \x01(\x0b\x32#.mandiant.capa.OperandOffsetFeatureH\x00\x12\x37\n\x0b\x62\x61sic_block\x18\x18 \x01(\x0b\x32 .mandiant.capa.BasicBlockFeatureH\x00\x42\t\n\x07\x66\x65\x61ture\"W\n\rFormatFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06\x66ormat\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"N\n\x14\x46unctionFeatureCount\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\r\n\x05\x63ount\x18\x02 \x01(\x04\"x\n\x0e\x46unctionLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12=\n\x14matched_basic_blocks\x18\x02 \x03(\x0b\x32\x1f.mandiant.capa.BasicBlockLayout\"d\n\x13\x46unctionNameFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x15\n\rfunction_name\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"X\n\rImportFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07import_\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\":\n\x06Layout\x12\x30\n\tfunctions\x18\x01 \x03(\x0b\x32\x1d.mandiant.capa.FunctionLayout\"H\n\x0fLibraryFunction\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x0c\n\x04name\x18\x02 \x01(\t\"Y\n\x07MBCSpec\x12\r\n\x05parts\x18\x01 \x03(\t\x12\x11\n\tobjective\x18\x02 \x01(\t\x12\x10\n\x08\x62\x65havior\x18\x03 \x01(\t\x12\x0e\n\x06method\x18\x04 \x01(\t\x12\n\n\x02id\x18\x05 \x01(\t\"\x9a\x01\n\x0cMaecMetadata\x12\x1b\n\x13\x61nalysis_conclusion\x18\x01 \x01(\t\x12\x1e\n\x16\x61nalysis_conclusion_ov\x18\x02 \x01(\t\x12\x16\n\x0emalware_family\x18\x03 \x01(\t\x12\x18\n\x10malware_category\x18\x04 \x01(\t\x12\x1b\n\x13malware_category_ov\x18\x05 \x01(\t\"\xd6\x02\n\x05Match\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x31\n\tstatement\x18\x02 \x01(\x0b\x32\x1c.mandiant.capa.StatementNodeH\x00\x12-\n\x07\x66\x65\x61ture\x18\x03 \x01(\x0b\x32\x1a.mandiant.capa.FeatureNodeH\x00\x12&\n\x08\x63hildren\x18\x05 \x03(\x0b\x32\x14.mandiant.capa.Match\x12)\n\tlocations\x18\x06 \x03(\x0b\x32\x16.mandiant.capa.Address\x12\x34\n\x08\x63\x61ptures\x18\x07 \x03(\x0b\x32\".mandiant.capa.Match.CapturesEntry\x1aI\n\rCapturesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.mandiant.capa.Addresses:\x02\x38\x01\x42\x06\n\x04node\"U\n\x0cMatchFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05match\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xbc\x02\n\x08Metadata\x12\x11\n\ttimestamp\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0c\n\x04\x61rgv\x18\x03 \x03(\t\x12%\n\x06sample\x18\x04 \x01(\x0b\x32\x15.mandiant.capa.Sample\x12-\n\x08\x61nalysis\x18\x05 \x01(\x0b\x32\x17.mandiant.capa.AnalysisB\x02\x18\x01\x12%\n\x06\x66lavor\x18\x06 \x01(\x0e\x32\x15.mandiant.capa.Flavor\x12\x38\n\x0fstatic_analysis\x18\x07 \x01(\x0b\x32\x1d.mandiant.capa.StaticAnalysisH\x00\x12:\n\x10\x64ynamic_analysis\x18\x08 \x01(\x0b\x32\x1e.mandiant.capa.DynamicAnalysisH\x00\x42\x0b\n\tanalysis2\"[\n\x0fMnemonicFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x10\n\x08mnemonic\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"]\n\x10NamespaceFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"n\n\rNumberFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12%\n\x06number\x18\x02 \x01(\x0b\x32\x15.mandiant.capa.Number\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"O\n\tOSFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\n\n\x02os\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"o\n\rOffsetFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12&\n\x06offset\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x01\n\x14OperandNumberFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05index\x18\x02 \x01(\r\x12.\n\x0eoperand_number\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x01\n\x14OperandOffsetFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05index\x18\x02 \x01(\r\x12.\n\x0eoperand_offset\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"M\n\x13ProcessFeatureCount\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\r\n\x05\x63ount\x18\x02 \x01(\x04\"|\n\rProcessLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x34\n\x0fmatched_threads\x18\x02 \x03(\x0b\x32\x1b.mandiant.capa.ThreadLayout\x12\x0c\n\x04name\x18\x03 \x01(\t\"|\n\x0fPropertyFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x11\n\tproperty_\x18\x02 \x01(\t\x12\x13\n\x06\x61\x63\x63\x65ss\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_accessB\x0e\n\x0c_description\"\x8d\x01\n\x0eRangeStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03min\x18\x02 \x01(\x04\x12\x0b\n\x03max\x18\x03 \x01(\x04\x12)\n\x05\x63hild\x18\x04 \x01(\x0b\x32\x1a.mandiant.capa.FeatureNode\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"U\n\x0cRegexFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05regex\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xba\x01\n\x0eResultDocument\x12%\n\x04meta\x18\x01 \x01(\x0b\x32\x17.mandiant.capa.Metadata\x12\x37\n\x05rules\x18\x02 \x03(\x0b\x32(.mandiant.capa.ResultDocument.RulesEntry\x1aH\n\nRulesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12)\n\x05value\x18\x02 \x01(\x0b\x32\x1a.mandiant.capa.RuleMatches:\x02\x38\x01\"|\n\x0bRuleMatches\x12)\n\x04meta\x18\x01 \x01(\x0b\x32\x1b.mandiant.capa.RuleMetadata\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x32\n\x07matches\x18\x03 \x03(\x0b\x32!.mandiant.capa.Pair_Address_Match\"\xed\x02\n\x0cRuleMetadata\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x0f\n\x07\x61uthors\x18\x03 \x03(\t\x12\'\n\x05scope\x18\x04 \x01(\x0e\x32\x14.mandiant.capa.ScopeB\x02\x18\x01\x12)\n\x06\x61ttack\x18\x05 \x03(\x0b\x32\x19.mandiant.capa.AttackSpec\x12#\n\x03mbc\x18\x06 \x03(\x0b\x32\x16.mandiant.capa.MBCSpec\x12\x12\n\nreferences\x18\x07 \x03(\t\x12\x10\n\x08\x65xamples\x18\x08 \x03(\t\x12\x13\n\x0b\x64\x65scription\x18\t \x01(\t\x12\x0b\n\x03lib\x18\n \x01(\x08\x12)\n\x04maec\x18\x0b \x01(\x0b\x32\x1b.mandiant.capa.MaecMetadata\x12\x18\n\x10is_subscope_rule\x18\x0c \x01(\x08\x12%\n\x06scopes\x18\r \x01(\x0b\x32\x15.mandiant.capa.Scopes\"A\n\x06Sample\x12\x0b\n\x03md5\x18\x01 \x01(\t\x12\x0c\n\x04sha1\x18\x02 \x01(\t\x12\x0e\n\x06sha256\x18\x03 \x01(\t\x12\x0c\n\x04path\x18\x04 \x01(\t\"v\n\x06Scopes\x12)\n\x06static\x18\x01 \x01(\x0e\x32\x14.mandiant.capa.ScopeH\x00\x88\x01\x01\x12*\n\x07\x64ynamic\x18\x02 \x01(\x0e\x32\x14.mandiant.capa.ScopeH\x01\x88\x01\x01\x42\t\n\x07_staticB\n\n\x08_dynamic\"Y\n\x0eSectionFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07section\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"V\n\rSomeStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\xf4\x01\n\rStatementNode\x12\x0c\n\x04type\x18\x01 \x01(\t\x12.\n\x05range\x18\x02 \x01(\x0b\x32\x1d.mandiant.capa.RangeStatementH\x00\x12,\n\x04some\x18\x03 \x01(\x0b\x32\x1c.mandiant.capa.SomeStatementH\x00\x12\x34\n\x08subscope\x18\x04 \x01(\x0b\x32 .mandiant.capa.SubscopeStatementH\x00\x12\x34\n\x08\x63ompound\x18\x05 \x01(\x0b\x32 .mandiant.capa.CompoundStatementH\x00\x42\x0b\n\tstatement\"\xae\x02\n\x0eStaticAnalysis\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12\x0c\n\x04\x61rch\x18\x02 \x01(\t\x12\n\n\x02os\x18\x03 \x01(\t\x12\x11\n\textractor\x18\x04 \x01(\t\x12\r\n\x05rules\x18\x05 \x03(\t\x12,\n\x0c\x62\x61se_address\x18\x06 \x01(\x0b\x32\x16.mandiant.capa.Address\x12+\n\x06layout\x18\x07 \x01(\x0b\x32\x1b.mandiant.capa.StaticLayout\x12:\n\x0e\x66\x65\x61ture_counts\x18\x08 \x01(\x0b\x32\".mandiant.capa.StaticFeatureCounts\x12\x39\n\x11library_functions\x18\t \x03(\x0b\x32\x1e.mandiant.capa.LibraryFunction\"[\n\x13StaticFeatureCounts\x12\x0c\n\x04\x66ile\x18\x01 \x01(\x04\x12\x36\n\tfunctions\x18\x02 \x03(\x0b\x32#.mandiant.capa.FunctionFeatureCount\"@\n\x0cStaticLayout\x12\x30\n\tfunctions\x18\x01 \x03(\x0b\x32\x1d.mandiant.capa.FunctionLayout\"W\n\rStringFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06string\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"p\n\x11SubscopeStatement\x12\x0c\n\x04type\x18\x01 \x01(\t\x12#\n\x05scope\x18\x02 \x01(\x0e\x32\x14.mandiant.capa.Scope\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"]\n\x10SubstringFeature\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x11\n\tsubstring\x18\x02 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"C\n\nCallLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x0c\n\x04name\x18\x02 \x01(\t\"i\n\x0cThreadLayout\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12\x30\n\rmatched_calls\x18\x02 \x03(\x0b\x32\x19.mandiant.capa.CallLayout\"4\n\tAddresses\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x03(\x0b\x32\x16.mandiant.capa.Address\"b\n\x12Pair_Address_Match\x12\'\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Address\x12#\n\x05match\x18\x02 \x01(\x0b\x32\x14.mandiant.capa.Match\"E\n\x0cToken_Offset\x12%\n\x05token\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\x0e\n\x06offset\x18\x02 \x01(\x04\"U\n\x08Ppid_Pid\x12$\n\x04ppid\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03pid\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\"~\n\x0cPpid_Pid_Tid\x12$\n\x04ppid\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03pid\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03tid\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\"\xa5\x01\n\x0fPpid_Pid_Tid_Id\x12$\n\x04ppid\x18\x01 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03pid\x18\x02 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12#\n\x03tid\x18\x03 \x01(\x0b\x32\x16.mandiant.capa.Integer\x12\"\n\x02id\x18\x04 \x01(\x0b\x32\x16.mandiant.capa.Integer\",\n\x07Integer\x12\x0b\n\x01u\x18\x01 \x01(\x04H\x00\x12\x0b\n\x01i\x18\x02 \x01(\x12H\x00\x42\x07\n\x05value\"8\n\x06Number\x12\x0b\n\x01u\x18\x01 \x01(\x04H\x00\x12\x0b\n\x01i\x18\x02 \x01(\x12H\x00\x12\x0b\n\x01\x66\x18\x03 \x01(\x01H\x00\x42\x07\n\x05value*\x92\x02\n\x0b\x41\x64\x64ressType\x12\x1b\n\x17\x41\x44\x44RESSTYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x41\x44\x44RESSTYPE_ABSOLUTE\x10\x01\x12\x18\n\x14\x41\x44\x44RESSTYPE_RELATIVE\x10\x02\x12\x14\n\x10\x41\x44\x44RESSTYPE_FILE\x10\x03\x12\x18\n\x14\x41\x44\x44RESSTYPE_DN_TOKEN\x10\x04\x12\x1f\n\x1b\x41\x44\x44RESSTYPE_DN_TOKEN_OFFSET\x10\x05\x12\x1a\n\x16\x41\x44\x44RESSTYPE_NO_ADDRESS\x10\x06\x12\x17\n\x13\x41\x44\x44RESSTYPE_PROCESS\x10\x07\x12\x16\n\x12\x41\x44\x44RESSTYPE_THREAD\x10\x08\x12\x14\n\x10\x41\x44\x44RESSTYPE_CALL\x10\t*G\n\x06\x46lavor\x12\x16\n\x12\x46LAVOR_UNSPECIFIED\x10\x00\x12\x11\n\rFLAVOR_STATIC\x10\x01\x12\x12\n\x0e\x46LAVOR_DYNAMIC\x10\x02*\xbe\x01\n\x05Scope\x12\x15\n\x11SCOPE_UNSPECIFIED\x10\x00\x12\x0e\n\nSCOPE_FILE\x10\x01\x12\x12\n\x0eSCOPE_FUNCTION\x10\x02\x12\x15\n\x11SCOPE_BASIC_BLOCK\x10\x03\x12\x15\n\x11SCOPE_INSTRUCTION\x10\x04\x12\x11\n\rSCOPE_PROCESS\x10\x05\x12\x10\n\x0cSCOPE_THREAD\x10\x06\x12\x0e\n\nSCOPE_CALL\x10\x07\x12\x17\n\x13SCOPE_SPAN_OF_CALLS\x10\x08\x62\x06proto3') - DESCRIPTOR._options = None - _MATCH_CAPTURESENTRY._options = None - _MATCH_CAPTURESENTRY._serialized_options = b'8\001' - _METADATA.fields_by_name['analysis']._options = None - _METADATA.fields_by_name['analysis']._serialized_options = b'\030\001' - _RESULTDOCUMENT_RULESENTRY._options = None - _RESULTDOCUMENT_RULESENTRY._serialized_options = b'8\001' - _RULEMETADATA.fields_by_name['scope']._options = None - _RULEMETADATA.fields_by_name['scope']._serialized_options = b'\030\001' - _ADDRESSTYPE._serialized_start=9062 - _ADDRESSTYPE._serialized_end=9336 - _FLAVOR._serialized_start=9338 - _FLAVOR._serialized_end=9409 - _SCOPE._serialized_start=9412 - _SCOPE._serialized_end=9577 - _APIFEATURE._serialized_start=47 - _APIFEATURE._serialized_end=128 - _ADDRESS._serialized_start=131 - _ADDRESS._serialized_end=438 - _ANALYSIS._serialized_start=441 - _ANALYSIS._serialized_end=725 - _ARCHFEATURE._serialized_start=727 - _ARCHFEATURE._serialized_end=810 - _ATTACKSPEC._serialized_start=812 - _ATTACKSPEC._serialized_end=908 - _BASICBLOCKFEATURE._serialized_start=910 - _BASICBLOCKFEATURE._serialized_end=985 - _BASICBLOCKLAYOUT._serialized_start=987 - _BASICBLOCKLAYOUT._serialized_end=1046 - _BYTESFEATURE._serialized_start=1048 - _BYTESFEATURE._serialized_end=1133 - _CHARACTERISTICFEATURE._serialized_start=1135 - _CHARACTERISTICFEATURE._serialized_end=1238 - _CLASSFEATURE._serialized_start=1240 - _CLASSFEATURE._serialized_end=1326 - _COMPOUNDSTATEMENT._serialized_start=1328 - _COMPOUNDSTATEMENT._serialized_end=1403 - _DYNAMICANALYSIS._serialized_start=1406 - _DYNAMICANALYSIS._serialized_end=1606 - _DYNAMICFEATURECOUNTS._serialized_start=1608 - _DYNAMICFEATURECOUNTS._serialized_end=1699 - _DYNAMICLAYOUT._serialized_start=1701 - _DYNAMICLAYOUT._serialized_end=1765 - _EXPORTFEATURE._serialized_start=1767 - _EXPORTFEATURE._serialized_end=1854 - _FEATURECOUNTS._serialized_start=1856 - _FEATURECOUNTS._serialized_end=1941 - _FEATURENODE._serialized_start=1944 - _FEATURENODE._serialized_end=3153 - _FORMATFEATURE._serialized_start=3155 - _FORMATFEATURE._serialized_end=3242 - _FUNCTIONFEATURECOUNT._serialized_start=3244 - _FUNCTIONFEATURECOUNT._serialized_end=3322 - _FUNCTIONLAYOUT._serialized_start=3324 - _FUNCTIONLAYOUT._serialized_end=3444 - _FUNCTIONNAMEFEATURE._serialized_start=3446 - _FUNCTIONNAMEFEATURE._serialized_end=3546 - _IMPORTFEATURE._serialized_start=3548 - _IMPORTFEATURE._serialized_end=3636 - _LAYOUT._serialized_start=3638 - _LAYOUT._serialized_end=3696 - _LIBRARYFUNCTION._serialized_start=3698 - _LIBRARYFUNCTION._serialized_end=3770 - _MBCSPEC._serialized_start=3772 - _MBCSPEC._serialized_end=3861 - _MAECMETADATA._serialized_start=3864 - _MAECMETADATA._serialized_end=4018 - _MATCH._serialized_start=4021 - _MATCH._serialized_end=4363 - _MATCH_CAPTURESENTRY._serialized_start=4282 - _MATCH_CAPTURESENTRY._serialized_end=4355 - _MATCHFEATURE._serialized_start=4365 - _MATCHFEATURE._serialized_end=4450 - _METADATA._serialized_start=4453 - _METADATA._serialized_end=4769 - _MNEMONICFEATURE._serialized_start=4771 - _MNEMONICFEATURE._serialized_end=4862 - _NAMESPACEFEATURE._serialized_start=4864 - _NAMESPACEFEATURE._serialized_end=4957 - _NUMBERFEATURE._serialized_start=4959 - _NUMBERFEATURE._serialized_end=5069 - _OSFEATURE._serialized_start=5071 - _OSFEATURE._serialized_end=5150 - _OFFSETFEATURE._serialized_start=5152 - _OFFSETFEATURE._serialized_end=5263 - _OPERANDNUMBERFEATURE._serialized_start=5266 - _OPERANDNUMBERFEATURE._serialized_end=5407 - _OPERANDOFFSETFEATURE._serialized_start=5410 - _OPERANDOFFSETFEATURE._serialized_end=5551 - _PROCESSFEATURECOUNT._serialized_start=5553 - _PROCESSFEATURECOUNT._serialized_end=5630 - _PROCESSLAYOUT._serialized_start=5632 - _PROCESSLAYOUT._serialized_end=5756 - _PROPERTYFEATURE._serialized_start=5758 - _PROPERTYFEATURE._serialized_end=5882 - _RANGESTATEMENT._serialized_start=5885 - _RANGESTATEMENT._serialized_end=6026 - _REGEXFEATURE._serialized_start=6028 - _REGEXFEATURE._serialized_end=6113 - _RESULTDOCUMENT._serialized_start=6116 - _RESULTDOCUMENT._serialized_end=6302 - _RESULTDOCUMENT_RULESENTRY._serialized_start=6230 - _RESULTDOCUMENT_RULESENTRY._serialized_end=6302 - _RULEMATCHES._serialized_start=6304 - _RULEMATCHES._serialized_end=6428 - _RULEMETADATA._serialized_start=6431 - _RULEMETADATA._serialized_end=6796 - _SAMPLE._serialized_start=6798 - _SAMPLE._serialized_end=6863 - _SCOPES._serialized_start=6865 - _SCOPES._serialized_end=6983 - _SECTIONFEATURE._serialized_start=6985 - _SECTIONFEATURE._serialized_end=7074 - _SOMESTATEMENT._serialized_start=7076 - _SOMESTATEMENT._serialized_end=7162 - _STATEMENTNODE._serialized_start=7165 - _STATEMENTNODE._serialized_end=7409 - _STATICANALYSIS._serialized_start=7412 - _STATICANALYSIS._serialized_end=7714 - _STATICFEATURECOUNTS._serialized_start=7716 - _STATICFEATURECOUNTS._serialized_end=7807 - _STATICLAYOUT._serialized_start=7809 - _STATICLAYOUT._serialized_end=7873 - _STRINGFEATURE._serialized_start=7875 - _STRINGFEATURE._serialized_end=7962 - _SUBSCOPESTATEMENT._serialized_start=7964 - _SUBSCOPESTATEMENT._serialized_end=8076 - _SUBSTRINGFEATURE._serialized_start=8078 - _SUBSTRINGFEATURE._serialized_end=8171 - _CALLLAYOUT._serialized_start=8173 - _CALLLAYOUT._serialized_end=8240 - _THREADLAYOUT._serialized_start=8242 - _THREADLAYOUT._serialized_end=8347 - _ADDRESSES._serialized_start=8349 - _ADDRESSES._serialized_end=8401 - _PAIR_ADDRESS_MATCH._serialized_start=8403 - _PAIR_ADDRESS_MATCH._serialized_end=8501 - _TOKEN_OFFSET._serialized_start=8503 - _TOKEN_OFFSET._serialized_end=8572 - _PPID_PID._serialized_start=8574 - _PPID_PID._serialized_end=8659 - _PPID_PID_TID._serialized_start=8661 - _PPID_PID_TID._serialized_end=8787 - _PPID_PID_TID_ID._serialized_start=8790 - _PPID_PID_TID_ID._serialized_end=8955 - _INTEGER._serialized_start=8957 - _INTEGER._serialized_end=9001 - _NUMBER._serialized_start=9003 - _NUMBER._serialized_end=9059 +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'capa.render.proto.capa_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_MATCH_CAPTURESENTRY']._loaded_options = None + _globals['_MATCH_CAPTURESENTRY']._serialized_options = b'8\001' + _globals['_METADATA'].fields_by_name['analysis']._loaded_options = None + _globals['_METADATA'].fields_by_name['analysis']._serialized_options = b'\030\001' + _globals['_RESULTDOCUMENT_RULESENTRY']._loaded_options = None + _globals['_RESULTDOCUMENT_RULESENTRY']._serialized_options = b'8\001' + _globals['_RULEMETADATA'].fields_by_name['scope']._loaded_options = None + _globals['_RULEMETADATA'].fields_by_name['scope']._serialized_options = b'\030\001' + _globals['_ADDRESSTYPE']._serialized_start=9062 + _globals['_ADDRESSTYPE']._serialized_end=9336 + _globals['_FLAVOR']._serialized_start=9338 + _globals['_FLAVOR']._serialized_end=9409 + _globals['_SCOPE']._serialized_start=9412 + _globals['_SCOPE']._serialized_end=9602 + _globals['_APIFEATURE']._serialized_start=47 + _globals['_APIFEATURE']._serialized_end=128 + _globals['_ADDRESS']._serialized_start=131 + _globals['_ADDRESS']._serialized_end=438 + _globals['_ANALYSIS']._serialized_start=441 + _globals['_ANALYSIS']._serialized_end=725 + _globals['_ARCHFEATURE']._serialized_start=727 + _globals['_ARCHFEATURE']._serialized_end=810 + _globals['_ATTACKSPEC']._serialized_start=812 + _globals['_ATTACKSPEC']._serialized_end=908 + _globals['_BASICBLOCKFEATURE']._serialized_start=910 + _globals['_BASICBLOCKFEATURE']._serialized_end=985 + _globals['_BASICBLOCKLAYOUT']._serialized_start=987 + _globals['_BASICBLOCKLAYOUT']._serialized_end=1046 + _globals['_BYTESFEATURE']._serialized_start=1048 + _globals['_BYTESFEATURE']._serialized_end=1133 + _globals['_CHARACTERISTICFEATURE']._serialized_start=1135 + _globals['_CHARACTERISTICFEATURE']._serialized_end=1238 + _globals['_CLASSFEATURE']._serialized_start=1240 + _globals['_CLASSFEATURE']._serialized_end=1326 + _globals['_COMPOUNDSTATEMENT']._serialized_start=1328 + _globals['_COMPOUNDSTATEMENT']._serialized_end=1403 + _globals['_DYNAMICANALYSIS']._serialized_start=1406 + _globals['_DYNAMICANALYSIS']._serialized_end=1606 + _globals['_DYNAMICFEATURECOUNTS']._serialized_start=1608 + _globals['_DYNAMICFEATURECOUNTS']._serialized_end=1699 + _globals['_DYNAMICLAYOUT']._serialized_start=1701 + _globals['_DYNAMICLAYOUT']._serialized_end=1765 + _globals['_EXPORTFEATURE']._serialized_start=1767 + _globals['_EXPORTFEATURE']._serialized_end=1854 + _globals['_FEATURECOUNTS']._serialized_start=1856 + _globals['_FEATURECOUNTS']._serialized_end=1941 + _globals['_FEATURENODE']._serialized_start=1944 + _globals['_FEATURENODE']._serialized_end=3153 + _globals['_FORMATFEATURE']._serialized_start=3155 + _globals['_FORMATFEATURE']._serialized_end=3242 + _globals['_FUNCTIONFEATURECOUNT']._serialized_start=3244 + _globals['_FUNCTIONFEATURECOUNT']._serialized_end=3322 + _globals['_FUNCTIONLAYOUT']._serialized_start=3324 + _globals['_FUNCTIONLAYOUT']._serialized_end=3444 + _globals['_FUNCTIONNAMEFEATURE']._serialized_start=3446 + _globals['_FUNCTIONNAMEFEATURE']._serialized_end=3546 + _globals['_IMPORTFEATURE']._serialized_start=3548 + _globals['_IMPORTFEATURE']._serialized_end=3636 + _globals['_LAYOUT']._serialized_start=3638 + _globals['_LAYOUT']._serialized_end=3696 + _globals['_LIBRARYFUNCTION']._serialized_start=3698 + _globals['_LIBRARYFUNCTION']._serialized_end=3770 + _globals['_MBCSPEC']._serialized_start=3772 + _globals['_MBCSPEC']._serialized_end=3861 + _globals['_MAECMETADATA']._serialized_start=3864 + _globals['_MAECMETADATA']._serialized_end=4018 + _globals['_MATCH']._serialized_start=4021 + _globals['_MATCH']._serialized_end=4363 + _globals['_MATCH_CAPTURESENTRY']._serialized_start=4282 + _globals['_MATCH_CAPTURESENTRY']._serialized_end=4355 + _globals['_MATCHFEATURE']._serialized_start=4365 + _globals['_MATCHFEATURE']._serialized_end=4450 + _globals['_METADATA']._serialized_start=4453 + _globals['_METADATA']._serialized_end=4769 + _globals['_MNEMONICFEATURE']._serialized_start=4771 + _globals['_MNEMONICFEATURE']._serialized_end=4862 + _globals['_NAMESPACEFEATURE']._serialized_start=4864 + _globals['_NAMESPACEFEATURE']._serialized_end=4957 + _globals['_NUMBERFEATURE']._serialized_start=4959 + _globals['_NUMBERFEATURE']._serialized_end=5069 + _globals['_OSFEATURE']._serialized_start=5071 + _globals['_OSFEATURE']._serialized_end=5150 + _globals['_OFFSETFEATURE']._serialized_start=5152 + _globals['_OFFSETFEATURE']._serialized_end=5263 + _globals['_OPERANDNUMBERFEATURE']._serialized_start=5266 + _globals['_OPERANDNUMBERFEATURE']._serialized_end=5407 + _globals['_OPERANDOFFSETFEATURE']._serialized_start=5410 + _globals['_OPERANDOFFSETFEATURE']._serialized_end=5551 + _globals['_PROCESSFEATURECOUNT']._serialized_start=5553 + _globals['_PROCESSFEATURECOUNT']._serialized_end=5630 + _globals['_PROCESSLAYOUT']._serialized_start=5632 + _globals['_PROCESSLAYOUT']._serialized_end=5756 + _globals['_PROPERTYFEATURE']._serialized_start=5758 + _globals['_PROPERTYFEATURE']._serialized_end=5882 + _globals['_RANGESTATEMENT']._serialized_start=5885 + _globals['_RANGESTATEMENT']._serialized_end=6026 + _globals['_REGEXFEATURE']._serialized_start=6028 + _globals['_REGEXFEATURE']._serialized_end=6113 + _globals['_RESULTDOCUMENT']._serialized_start=6116 + _globals['_RESULTDOCUMENT']._serialized_end=6302 + _globals['_RESULTDOCUMENT_RULESENTRY']._serialized_start=6230 + _globals['_RESULTDOCUMENT_RULESENTRY']._serialized_end=6302 + _globals['_RULEMATCHES']._serialized_start=6304 + _globals['_RULEMATCHES']._serialized_end=6428 + _globals['_RULEMETADATA']._serialized_start=6431 + _globals['_RULEMETADATA']._serialized_end=6796 + _globals['_SAMPLE']._serialized_start=6798 + _globals['_SAMPLE']._serialized_end=6863 + _globals['_SCOPES']._serialized_start=6865 + _globals['_SCOPES']._serialized_end=6983 + _globals['_SECTIONFEATURE']._serialized_start=6985 + _globals['_SECTIONFEATURE']._serialized_end=7074 + _globals['_SOMESTATEMENT']._serialized_start=7076 + _globals['_SOMESTATEMENT']._serialized_end=7162 + _globals['_STATEMENTNODE']._serialized_start=7165 + _globals['_STATEMENTNODE']._serialized_end=7409 + _globals['_STATICANALYSIS']._serialized_start=7412 + _globals['_STATICANALYSIS']._serialized_end=7714 + _globals['_STATICFEATURECOUNTS']._serialized_start=7716 + _globals['_STATICFEATURECOUNTS']._serialized_end=7807 + _globals['_STATICLAYOUT']._serialized_start=7809 + _globals['_STATICLAYOUT']._serialized_end=7873 + _globals['_STRINGFEATURE']._serialized_start=7875 + _globals['_STRINGFEATURE']._serialized_end=7962 + _globals['_SUBSCOPESTATEMENT']._serialized_start=7964 + _globals['_SUBSCOPESTATEMENT']._serialized_end=8076 + _globals['_SUBSTRINGFEATURE']._serialized_start=8078 + _globals['_SUBSTRINGFEATURE']._serialized_end=8171 + _globals['_CALLLAYOUT']._serialized_start=8173 + _globals['_CALLLAYOUT']._serialized_end=8240 + _globals['_THREADLAYOUT']._serialized_start=8242 + _globals['_THREADLAYOUT']._serialized_end=8347 + _globals['_ADDRESSES']._serialized_start=8349 + _globals['_ADDRESSES']._serialized_end=8401 + _globals['_PAIR_ADDRESS_MATCH']._serialized_start=8403 + _globals['_PAIR_ADDRESS_MATCH']._serialized_end=8501 + _globals['_TOKEN_OFFSET']._serialized_start=8503 + _globals['_TOKEN_OFFSET']._serialized_end=8572 + _globals['_PPID_PID']._serialized_start=8574 + _globals['_PPID_PID']._serialized_end=8659 + _globals['_PPID_PID_TID']._serialized_start=8661 + _globals['_PPID_PID_TID']._serialized_end=8787 + _globals['_PPID_PID_TID_ID']._serialized_start=8790 + _globals['_PPID_PID_TID_ID']._serialized_end=8955 + _globals['_INTEGER']._serialized_start=8957 + _globals['_INTEGER']._serialized_end=9001 + _globals['_NUMBER']._serialized_start=9003 + _globals['_NUMBER']._serialized_end=9059 # @@protoc_insertion_point(module_scope) diff --git a/capa/render/proto/capa_pb2.pyi b/capa/render/proto/capa_pb2.pyi index ecb330bc6..98420a292 100644 --- a/capa/render/proto/capa_pb2.pyi +++ b/capa/render/proto/capa_pb2.pyi @@ -1,7 +1,21 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file +Copyright 2023 Google LLC + +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 + + http://www.apache.org/licenses/LICENSE-2.0 + +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 builtins import collections.abc import google.protobuf.descriptor @@ -80,6 +94,7 @@ class _ScopeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumType SCOPE_PROCESS: _Scope.ValueType # 5 SCOPE_THREAD: _Scope.ValueType # 6 SCOPE_CALL: _Scope.ValueType # 7 + SCOPE_SPAN_OF_CALLS: _Scope.ValueType # 8 class Scope(_Scope, metaclass=_ScopeEnumTypeWrapper): ... @@ -91,9 +106,10 @@ SCOPE_INSTRUCTION: Scope.ValueType # 4 SCOPE_PROCESS: Scope.ValueType # 5 SCOPE_THREAD: Scope.ValueType # 6 SCOPE_CALL: Scope.ValueType # 7 +SCOPE_SPAN_OF_CALLS: Scope.ValueType # 8 global___Scope = Scope -@typing_extensions.final +@typing.final class APIFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -110,13 +126,13 @@ class APIFeature(google.protobuf.message.Message): api: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "api", b"api", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "api", b"api", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___APIFeature = APIFeature -@typing_extensions.final +@typing.final class Address(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -147,13 +163,13 @@ class Address(google.protobuf.message.Message): ppid_pid_tid: global___Ppid_Pid_Tid | None = ..., ppid_pid_tid_id: global___Ppid_Pid_Tid_Id | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["ppid_pid", b"ppid_pid", "ppid_pid_tid", b"ppid_pid_tid", "ppid_pid_tid_id", b"ppid_pid_tid_id", "token_offset", b"token_offset", "v", b"v", "value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["ppid_pid", b"ppid_pid", "ppid_pid_tid", b"ppid_pid_tid", "ppid_pid_tid_id", b"ppid_pid_tid_id", "token_offset", b"token_offset", "type", b"type", "v", b"v", "value", b"value"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["v", "token_offset", "ppid_pid", "ppid_pid_tid", "ppid_pid_tid_id"] | None: ... + def HasField(self, field_name: typing.Literal["ppid_pid", b"ppid_pid", "ppid_pid_tid", b"ppid_pid_tid", "ppid_pid_tid_id", b"ppid_pid_tid_id", "token_offset", b"token_offset", "v", b"v", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["ppid_pid", b"ppid_pid", "ppid_pid_tid", b"ppid_pid_tid", "ppid_pid_tid_id", b"ppid_pid_tid_id", "token_offset", b"token_offset", "type", b"type", "v", b"v", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["value", b"value"]) -> typing.Literal["v", "token_offset", "ppid_pid", "ppid_pid_tid", "ppid_pid_tid_id"] | None: ... global___Address = Address -@typing_extensions.final +@typing.final class Analysis(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -193,12 +209,12 @@ class Analysis(google.protobuf.message.Message): feature_counts: global___FeatureCounts | None = ..., library_functions: collections.abc.Iterable[global___LibraryFunction] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["base_address", b"base_address", "feature_counts", b"feature_counts", "layout", b"layout"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["arch", b"arch", "base_address", b"base_address", "extractor", b"extractor", "feature_counts", b"feature_counts", "format", b"format", "layout", b"layout", "library_functions", b"library_functions", "os", b"os", "rules", b"rules"]) -> None: ... + def HasField(self, field_name: typing.Literal["base_address", b"base_address", "feature_counts", b"feature_counts", "layout", b"layout"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["arch", b"arch", "base_address", b"base_address", "extractor", b"extractor", "feature_counts", b"feature_counts", "format", b"format", "layout", b"layout", "library_functions", b"library_functions", "os", b"os", "rules", b"rules"]) -> None: ... global___Analysis = Analysis -@typing_extensions.final +@typing.final class ArchFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -215,13 +231,13 @@ class ArchFeature(google.protobuf.message.Message): arch: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "arch", b"arch", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "arch", b"arch", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___ArchFeature = ArchFeature -@typing_extensions.final +@typing.final class AttackSpec(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -230,12 +246,12 @@ class AttackSpec(google.protobuf.message.Message): TECHNIQUE_FIELD_NUMBER: builtins.int SUBTECHNIQUE_FIELD_NUMBER: builtins.int ID_FIELD_NUMBER: builtins.int - @property - def parts(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... tactic: builtins.str technique: builtins.str subtechnique: builtins.str id: builtins.str + @property + def parts(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... def __init__( self, *, @@ -245,11 +261,11 @@ class AttackSpec(google.protobuf.message.Message): subtechnique: builtins.str = ..., id: builtins.str = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "parts", b"parts", "subtechnique", b"subtechnique", "tactic", b"tactic", "technique", b"technique"]) -> None: ... + def ClearField(self, field_name: typing.Literal["id", b"id", "parts", b"parts", "subtechnique", b"subtechnique", "tactic", b"tactic", "technique", b"technique"]) -> None: ... global___AttackSpec = AttackSpec -@typing_extensions.final +@typing.final class BasicBlockFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -263,13 +279,13 @@ class BasicBlockFeature(google.protobuf.message.Message): type: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___BasicBlockFeature = BasicBlockFeature -@typing_extensions.final +@typing.final class BasicBlockLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -281,12 +297,12 @@ class BasicBlockLayout(google.protobuf.message.Message): *, address: global___Address | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address"]) -> None: ... global___BasicBlockLayout = BasicBlockLayout -@typing_extensions.final +@typing.final class BytesFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -303,13 +319,13 @@ class BytesFeature(google.protobuf.message.Message): bytes: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "bytes", b"bytes", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "bytes", b"bytes", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___BytesFeature = BytesFeature -@typing_extensions.final +@typing.final class CharacteristicFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -326,13 +342,13 @@ class CharacteristicFeature(google.protobuf.message.Message): characteristic: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "characteristic", b"characteristic", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "characteristic", b"characteristic", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___CharacteristicFeature = CharacteristicFeature -@typing_extensions.final +@typing.final class ClassFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -350,13 +366,13 @@ class ClassFeature(google.protobuf.message.Message): class_: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "class_", b"class_", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "class_", b"class_", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___ClassFeature = ClassFeature -@typing_extensions.final +@typing.final class CompoundStatement(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -370,13 +386,13 @@ class CompoundStatement(google.protobuf.message.Message): type: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___CompoundStatement = CompoundStatement -@typing_extensions.final +@typing.final class DynamicAnalysis(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -408,12 +424,12 @@ class DynamicAnalysis(google.protobuf.message.Message): layout: global___DynamicLayout | None = ..., feature_counts: global___DynamicFeatureCounts | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["feature_counts", b"feature_counts", "layout", b"layout"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["arch", b"arch", "extractor", b"extractor", "feature_counts", b"feature_counts", "format", b"format", "layout", b"layout", "os", b"os", "rules", b"rules"]) -> None: ... + def HasField(self, field_name: typing.Literal["feature_counts", b"feature_counts", "layout", b"layout"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["arch", b"arch", "extractor", b"extractor", "feature_counts", b"feature_counts", "format", b"format", "layout", b"layout", "os", b"os", "rules", b"rules"]) -> None: ... global___DynamicAnalysis = DynamicAnalysis -@typing_extensions.final +@typing.final class DynamicFeatureCounts(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -428,11 +444,11 @@ class DynamicFeatureCounts(google.protobuf.message.Message): file: builtins.int = ..., processes: collections.abc.Iterable[global___ProcessFeatureCount] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["file", b"file", "processes", b"processes"]) -> None: ... + def ClearField(self, field_name: typing.Literal["file", b"file", "processes", b"processes"]) -> None: ... global___DynamicFeatureCounts = DynamicFeatureCounts -@typing_extensions.final +@typing.final class DynamicLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -444,11 +460,11 @@ class DynamicLayout(google.protobuf.message.Message): *, processes: collections.abc.Iterable[global___ProcessLayout] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["processes", b"processes"]) -> None: ... + def ClearField(self, field_name: typing.Literal["processes", b"processes"]) -> None: ... global___DynamicLayout = DynamicLayout -@typing_extensions.final +@typing.final class ExportFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -465,13 +481,13 @@ class ExportFeature(google.protobuf.message.Message): export: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "export", b"export", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "export", b"export", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___ExportFeature = ExportFeature -@typing_extensions.final +@typing.final class FeatureCounts(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -486,11 +502,11 @@ class FeatureCounts(google.protobuf.message.Message): file: builtins.int = ..., functions: collections.abc.Iterable[global___FunctionFeatureCount] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["file", b"file", "functions", b"functions"]) -> None: ... + def ClearField(self, field_name: typing.Literal["file", b"file", "functions", b"functions"]) -> None: ... global___FeatureCounts = FeatureCounts -@typing_extensions.final +@typing.final class FeatureNode(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -534,6 +550,7 @@ class FeatureNode(google.protobuf.message.Message): @property def import_(self) -> global___ImportFeature: """import is Python keyword""" + @property def section(self) -> global___SectionFeature: ... @property @@ -553,6 +570,7 @@ class FeatureNode(google.protobuf.message.Message): @property def property_(self) -> global___PropertyFeature: """property is a Python top-level decorator name""" + @property def number(self) -> global___NumberFeature: ... @property @@ -595,13 +613,13 @@ class FeatureNode(google.protobuf.message.Message): operand_offset: global___OperandOffsetFeature | None = ..., basic_block: global___BasicBlockFeature | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["api", b"api", "arch", b"arch", "basic_block", b"basic_block", "bytes", b"bytes", "characteristic", b"characteristic", "class_", b"class_", "export", b"export", "feature", b"feature", "format", b"format", "function_name", b"function_name", "import_", b"import_", "match", b"match", "mnemonic", b"mnemonic", "namespace", b"namespace", "number", b"number", "offset", b"offset", "operand_number", b"operand_number", "operand_offset", b"operand_offset", "os", b"os", "property_", b"property_", "regex", b"regex", "section", b"section", "string", b"string", "substring", b"substring"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["api", b"api", "arch", b"arch", "basic_block", b"basic_block", "bytes", b"bytes", "characteristic", b"characteristic", "class_", b"class_", "export", b"export", "feature", b"feature", "format", b"format", "function_name", b"function_name", "import_", b"import_", "match", b"match", "mnemonic", b"mnemonic", "namespace", b"namespace", "number", b"number", "offset", b"offset", "operand_number", b"operand_number", "operand_offset", b"operand_offset", "os", b"os", "property_", b"property_", "regex", b"regex", "section", b"section", "string", b"string", "substring", b"substring", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["feature", b"feature"]) -> typing_extensions.Literal["os", "arch", "format", "match", "characteristic", "export", "import_", "section", "function_name", "substring", "regex", "string", "class_", "namespace", "api", "property_", "number", "bytes", "offset", "mnemonic", "operand_number", "operand_offset", "basic_block"] | None: ... + def HasField(self, field_name: typing.Literal["api", b"api", "arch", b"arch", "basic_block", b"basic_block", "bytes", b"bytes", "characteristic", b"characteristic", "class_", b"class_", "export", b"export", "feature", b"feature", "format", b"format", "function_name", b"function_name", "import_", b"import_", "match", b"match", "mnemonic", b"mnemonic", "namespace", b"namespace", "number", b"number", "offset", b"offset", "operand_number", b"operand_number", "operand_offset", b"operand_offset", "os", b"os", "property_", b"property_", "regex", b"regex", "section", b"section", "string", b"string", "substring", b"substring"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["api", b"api", "arch", b"arch", "basic_block", b"basic_block", "bytes", b"bytes", "characteristic", b"characteristic", "class_", b"class_", "export", b"export", "feature", b"feature", "format", b"format", "function_name", b"function_name", "import_", b"import_", "match", b"match", "mnemonic", b"mnemonic", "namespace", b"namespace", "number", b"number", "offset", b"offset", "operand_number", b"operand_number", "operand_offset", b"operand_offset", "os", b"os", "property_", b"property_", "regex", b"regex", "section", b"section", "string", b"string", "substring", b"substring", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["feature", b"feature"]) -> typing.Literal["os", "arch", "format", "match", "characteristic", "export", "import_", "section", "function_name", "substring", "regex", "string", "class_", "namespace", "api", "property_", "number", "bytes", "offset", "mnemonic", "operand_number", "operand_offset", "basic_block"] | None: ... global___FeatureNode = FeatureNode -@typing_extensions.final +@typing.final class FormatFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -618,33 +636,33 @@ class FormatFeature(google.protobuf.message.Message): format: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "format", b"format", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "format", b"format", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___FormatFeature = FormatFeature -@typing_extensions.final +@typing.final class FunctionFeatureCount(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ADDRESS_FIELD_NUMBER: builtins.int COUNT_FIELD_NUMBER: builtins.int + count: builtins.int @property def address(self) -> global___Address: ... - count: builtins.int def __init__( self, *, address: global___Address | None = ..., count: builtins.int = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "count", b"count"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "count", b"count"]) -> None: ... global___FunctionFeatureCount = FunctionFeatureCount -@typing_extensions.final +@typing.final class FunctionLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -660,12 +678,12 @@ class FunctionLayout(google.protobuf.message.Message): address: global___Address | None = ..., matched_basic_blocks: collections.abc.Iterable[global___BasicBlockLayout] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "matched_basic_blocks", b"matched_basic_blocks"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "matched_basic_blocks", b"matched_basic_blocks"]) -> None: ... global___FunctionLayout = FunctionLayout -@typing_extensions.final +@typing.final class FunctionNameFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -682,13 +700,13 @@ class FunctionNameFeature(google.protobuf.message.Message): function_name: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "function_name", b"function_name", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "function_name", b"function_name", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___FunctionNameFeature = FunctionNameFeature -@typing_extensions.final +@typing.final class ImportFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -705,13 +723,13 @@ class ImportFeature(google.protobuf.message.Message): import_: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "import_", b"import_", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "import_", b"import_", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___ImportFeature = ImportFeature -@typing_extensions.final +@typing.final class Layout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -723,31 +741,31 @@ class Layout(google.protobuf.message.Message): *, functions: collections.abc.Iterable[global___FunctionLayout] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["functions", b"functions"]) -> None: ... + def ClearField(self, field_name: typing.Literal["functions", b"functions"]) -> None: ... global___Layout = Layout -@typing_extensions.final +@typing.final class LibraryFunction(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ADDRESS_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int + name: builtins.str @property def address(self) -> global___Address: ... - name: builtins.str def __init__( self, *, address: global___Address | None = ..., name: builtins.str = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "name", b"name"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "name", b"name"]) -> None: ... global___LibraryFunction = LibraryFunction -@typing_extensions.final +@typing.final class MBCSpec(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -756,12 +774,12 @@ class MBCSpec(google.protobuf.message.Message): BEHAVIOR_FIELD_NUMBER: builtins.int METHOD_FIELD_NUMBER: builtins.int ID_FIELD_NUMBER: builtins.int - @property - def parts(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... objective: builtins.str behavior: builtins.str method: builtins.str id: builtins.str + @property + def parts(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... def __init__( self, *, @@ -771,11 +789,11 @@ class MBCSpec(google.protobuf.message.Message): method: builtins.str = ..., id: builtins.str = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["behavior", b"behavior", "id", b"id", "method", b"method", "objective", b"objective", "parts", b"parts"]) -> None: ... + def ClearField(self, field_name: typing.Literal["behavior", b"behavior", "id", b"id", "method", b"method", "objective", b"objective", "parts", b"parts"]) -> None: ... global___MBCSpec = MBCSpec -@typing_extensions.final +@typing.final class MaecMetadata(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -798,15 +816,15 @@ class MaecMetadata(google.protobuf.message.Message): malware_category: builtins.str = ..., malware_category_ov: builtins.str = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["analysis_conclusion", b"analysis_conclusion", "analysis_conclusion_ov", b"analysis_conclusion_ov", "malware_category", b"malware_category", "malware_category_ov", b"malware_category_ov", "malware_family", b"malware_family"]) -> None: ... + def ClearField(self, field_name: typing.Literal["analysis_conclusion", b"analysis_conclusion", "analysis_conclusion_ov", b"analysis_conclusion_ov", "malware_category", b"malware_category", "malware_category_ov", b"malware_category_ov", "malware_family", b"malware_family"]) -> None: ... global___MaecMetadata = MaecMetadata -@typing_extensions.final +@typing.final class Match(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + @typing.final class CapturesEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -821,8 +839,8 @@ class Match(google.protobuf.message.Message): key: builtins.str = ..., value: global___Addresses | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... SUCCESS_FIELD_NUMBER: builtins.int STATEMENT_FIELD_NUMBER: builtins.int @@ -851,13 +869,13 @@ class Match(google.protobuf.message.Message): locations: collections.abc.Iterable[global___Address] | None = ..., captures: collections.abc.Mapping[builtins.str, global___Addresses] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["feature", b"feature", "node", b"node", "statement", b"statement"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["captures", b"captures", "children", b"children", "feature", b"feature", "locations", b"locations", "node", b"node", "statement", b"statement", "success", b"success"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["node", b"node"]) -> typing_extensions.Literal["statement", "feature"] | None: ... + def HasField(self, field_name: typing.Literal["feature", b"feature", "node", b"node", "statement", b"statement"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["captures", b"captures", "children", b"children", "feature", b"feature", "locations", b"locations", "node", b"node", "statement", b"statement", "success", b"success"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["node", b"node"]) -> typing.Literal["statement", "feature"] | None: ... global___Match = Match -@typing_extensions.final +@typing.final class MatchFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -874,13 +892,13 @@ class MatchFeature(google.protobuf.message.Message): match: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "match", b"match", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "match", b"match", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___MatchFeature = MatchFeature -@typing_extensions.final +@typing.final class Metadata(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -895,6 +913,7 @@ class Metadata(google.protobuf.message.Message): timestamp: builtins.str """iso8601 format, like: 2019-01-01T00:00:00Z""" version: builtins.str + flavor: global___Flavor.ValueType @property def argv(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... @property @@ -904,10 +923,11 @@ class Metadata(google.protobuf.message.Message): """deprecated in v7.0. use analysis2 instead. """ - flavor: global___Flavor.ValueType + @property def static_analysis(self) -> global___StaticAnalysis: """use analysis2 instead of analysis (deprecated in v7.0).""" + @property def dynamic_analysis(self) -> global___DynamicAnalysis: ... def __init__( @@ -922,13 +942,13 @@ class Metadata(google.protobuf.message.Message): static_analysis: global___StaticAnalysis | None = ..., dynamic_analysis: global___DynamicAnalysis | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["analysis", b"analysis", "analysis2", b"analysis2", "dynamic_analysis", b"dynamic_analysis", "sample", b"sample", "static_analysis", b"static_analysis"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["analysis", b"analysis", "analysis2", b"analysis2", "argv", b"argv", "dynamic_analysis", b"dynamic_analysis", "flavor", b"flavor", "sample", b"sample", "static_analysis", b"static_analysis", "timestamp", b"timestamp", "version", b"version"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["analysis2", b"analysis2"]) -> typing_extensions.Literal["static_analysis", "dynamic_analysis"] | None: ... + def HasField(self, field_name: typing.Literal["analysis", b"analysis", "analysis2", b"analysis2", "dynamic_analysis", b"dynamic_analysis", "sample", b"sample", "static_analysis", b"static_analysis"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["analysis", b"analysis", "analysis2", b"analysis2", "argv", b"argv", "dynamic_analysis", b"dynamic_analysis", "flavor", b"flavor", "sample", b"sample", "static_analysis", b"static_analysis", "timestamp", b"timestamp", "version", b"version"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["analysis2", b"analysis2"]) -> typing.Literal["static_analysis", "dynamic_analysis"] | None: ... global___Metadata = Metadata -@typing_extensions.final +@typing.final class MnemonicFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -945,13 +965,13 @@ class MnemonicFeature(google.protobuf.message.Message): mnemonic: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "mnemonic", b"mnemonic", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "mnemonic", b"mnemonic", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___MnemonicFeature = MnemonicFeature -@typing_extensions.final +@typing.final class NamespaceFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -968,13 +988,13 @@ class NamespaceFeature(google.protobuf.message.Message): namespace: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "namespace", b"namespace", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "namespace", b"namespace", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___NamespaceFeature = NamespaceFeature -@typing_extensions.final +@typing.final class NumberFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -982,10 +1002,11 @@ class NumberFeature(google.protobuf.message.Message): NUMBER_FIELD_NUMBER: builtins.int DESCRIPTION_FIELD_NUMBER: builtins.int type: builtins.str + description: builtins.str @property def number(self) -> global___Number: """this can be positive (range: u64), negative (range: i64), or a double.""" - description: builtins.str + def __init__( self, *, @@ -993,13 +1014,13 @@ class NumberFeature(google.protobuf.message.Message): number: global___Number | None = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "number", b"number"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "number", b"number", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "number", b"number"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "number", b"number", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___NumberFeature = NumberFeature -@typing_extensions.final +@typing.final class OSFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1016,13 +1037,13 @@ class OSFeature(google.protobuf.message.Message): os: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "os", b"os", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "os", b"os", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___OSFeature = OSFeature -@typing_extensions.final +@typing.final class OffsetFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1030,10 +1051,11 @@ class OffsetFeature(google.protobuf.message.Message): OFFSET_FIELD_NUMBER: builtins.int DESCRIPTION_FIELD_NUMBER: builtins.int type: builtins.str + description: builtins.str @property def offset(self) -> global___Integer: """offset can be negative""" - description: builtins.str + def __init__( self, *, @@ -1041,13 +1063,13 @@ class OffsetFeature(google.protobuf.message.Message): offset: global___Integer | None = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "offset", b"offset"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "offset", b"offset", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "offset", b"offset"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "offset", b"offset", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___OffsetFeature = OffsetFeature -@typing_extensions.final +@typing.final class OperandNumberFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1057,10 +1079,11 @@ class OperandNumberFeature(google.protobuf.message.Message): DESCRIPTION_FIELD_NUMBER: builtins.int type: builtins.str index: builtins.int + description: builtins.str @property def operand_number(self) -> global___Integer: """this can be positive (range: u64), negative (range: i64), or a double.""" - description: builtins.str + def __init__( self, *, @@ -1069,13 +1092,13 @@ class OperandNumberFeature(google.protobuf.message.Message): operand_number: global___Integer | None = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "operand_number", b"operand_number"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "index", b"index", "operand_number", b"operand_number", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "operand_number", b"operand_number"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "index", b"index", "operand_number", b"operand_number", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___OperandNumberFeature = OperandNumberFeature -@typing_extensions.final +@typing.final class OperandOffsetFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1085,9 +1108,9 @@ class OperandOffsetFeature(google.protobuf.message.Message): DESCRIPTION_FIELD_NUMBER: builtins.int type: builtins.str index: builtins.int + description: builtins.str @property def operand_offset(self) -> global___Integer: ... - description: builtins.str def __init__( self, *, @@ -1096,44 +1119,44 @@ class OperandOffsetFeature(google.protobuf.message.Message): operand_offset: global___Integer | None = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "operand_offset", b"operand_offset"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "index", b"index", "operand_offset", b"operand_offset", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "operand_offset", b"operand_offset"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "index", b"index", "operand_offset", b"operand_offset", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___OperandOffsetFeature = OperandOffsetFeature -@typing_extensions.final +@typing.final class ProcessFeatureCount(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ADDRESS_FIELD_NUMBER: builtins.int COUNT_FIELD_NUMBER: builtins.int + count: builtins.int @property def address(self) -> global___Address: ... - count: builtins.int def __init__( self, *, address: global___Address | None = ..., count: builtins.int = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "count", b"count"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "count", b"count"]) -> None: ... global___ProcessFeatureCount = ProcessFeatureCount -@typing_extensions.final +@typing.final class ProcessLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ADDRESS_FIELD_NUMBER: builtins.int MATCHED_THREADS_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int + name: builtins.str @property def address(self) -> global___Address: ... @property def matched_threads(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ThreadLayout]: ... - name: builtins.str def __init__( self, *, @@ -1141,12 +1164,12 @@ class ProcessLayout(google.protobuf.message.Message): matched_threads: collections.abc.Iterable[global___ThreadLayout] | None = ..., name: builtins.str = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "matched_threads", b"matched_threads", "name", b"name"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "matched_threads", b"matched_threads", "name", b"name"]) -> None: ... global___ProcessLayout = ProcessLayout -@typing_extensions.final +@typing.final class PropertyFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1167,16 +1190,16 @@ class PropertyFeature(google.protobuf.message.Message): access: builtins.str | None = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_access", b"_access", "_description", b"_description", "access", b"access", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_access", b"_access", "_description", b"_description", "access", b"access", "description", b"description", "property_", b"property_", "type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["_access", b"_access", "_description", b"_description", "access", b"access", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_access", b"_access", "_description", b"_description", "access", b"access", "description", b"description", "property_", b"property_", "type", b"type"]) -> None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_access", b"_access"]) -> typing_extensions.Literal["access"] | None: ... + def WhichOneof(self, oneof_group: typing.Literal["_access", b"_access"]) -> typing.Literal["access"] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___PropertyFeature = PropertyFeature -@typing_extensions.final +@typing.final class RangeStatement(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1188,10 +1211,11 @@ class RangeStatement(google.protobuf.message.Message): type: builtins.str min: builtins.int max: builtins.int + description: builtins.str @property def child(self) -> global___FeatureNode: """reusing FeatureNode here to avoid duplication and list all features OSFeature, ArchFeature, ... again.""" - description: builtins.str + def __init__( self, *, @@ -1201,13 +1225,13 @@ class RangeStatement(google.protobuf.message.Message): child: global___FeatureNode | None = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "child", b"child", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "child", b"child", "description", b"description", "max", b"max", "min", b"min", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "child", b"child", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "child", b"child", "description", b"description", "max", b"max", "min", b"min", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___RangeStatement = RangeStatement -@typing_extensions.final +@typing.final class RegexFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1224,17 +1248,17 @@ class RegexFeature(google.protobuf.message.Message): regex: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "regex", b"regex", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "regex", b"regex", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___RegexFeature = RegexFeature -@typing_extensions.final +@typing.final class ResultDocument(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + @typing.final class RulesEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1249,8 +1273,8 @@ class ResultDocument(google.protobuf.message.Message): key: builtins.str = ..., value: global___RuleMatches | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... META_FIELD_NUMBER: builtins.int RULES_FIELD_NUMBER: builtins.int @@ -1264,21 +1288,21 @@ class ResultDocument(google.protobuf.message.Message): meta: global___Metadata | None = ..., rules: collections.abc.Mapping[builtins.str, global___RuleMatches] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["meta", b"meta"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["meta", b"meta", "rules", b"rules"]) -> None: ... + def HasField(self, field_name: typing.Literal["meta", b"meta"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["meta", b"meta", "rules", b"rules"]) -> None: ... global___ResultDocument = ResultDocument -@typing_extensions.final +@typing.final class RuleMatches(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor META_FIELD_NUMBER: builtins.int SOURCE_FIELD_NUMBER: builtins.int MATCHES_FIELD_NUMBER: builtins.int + source: builtins.str @property def meta(self) -> global___RuleMetadata: ... - source: builtins.str @property def matches(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Pair_Address_Match]: ... def __init__( @@ -1288,12 +1312,12 @@ class RuleMatches(google.protobuf.message.Message): source: builtins.str = ..., matches: collections.abc.Iterable[global___Pair_Address_Match] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["meta", b"meta"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["matches", b"matches", "meta", b"meta", "source", b"source"]) -> None: ... + def HasField(self, field_name: typing.Literal["meta", b"meta"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["matches", b"matches", "meta", b"meta", "source", b"source"]) -> None: ... global___RuleMatches = RuleMatches -@typing_extensions.final +@typing.final class RuleMetadata(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1312,12 +1336,15 @@ class RuleMetadata(google.protobuf.message.Message): SCOPES_FIELD_NUMBER: builtins.int name: builtins.str namespace: builtins.str - @property - def authors(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... scope: global___Scope.ValueType """deprecated in v7.0. use scopes instead. """ + description: builtins.str + lib: builtins.bool + is_subscope_rule: builtins.bool + @property + def authors(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... @property def attack(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AttackSpec]: ... @property @@ -1326,14 +1353,12 @@ class RuleMetadata(google.protobuf.message.Message): def references(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... @property def examples(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - description: builtins.str - lib: builtins.bool @property def maec(self) -> global___MaecMetadata: ... - is_subscope_rule: builtins.bool @property def scopes(self) -> global___Scopes: """use scopes over scope (deprecated in v7.0).""" + def __init__( self, *, @@ -1351,12 +1376,12 @@ class RuleMetadata(google.protobuf.message.Message): is_subscope_rule: builtins.bool = ..., scopes: global___Scopes | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["maec", b"maec", "scopes", b"scopes"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["attack", b"attack", "authors", b"authors", "description", b"description", "examples", b"examples", "is_subscope_rule", b"is_subscope_rule", "lib", b"lib", "maec", b"maec", "mbc", b"mbc", "name", b"name", "namespace", b"namespace", "references", b"references", "scope", b"scope", "scopes", b"scopes"]) -> None: ... + def HasField(self, field_name: typing.Literal["maec", b"maec", "scopes", b"scopes"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attack", b"attack", "authors", b"authors", "description", b"description", "examples", b"examples", "is_subscope_rule", b"is_subscope_rule", "lib", b"lib", "maec", b"maec", "mbc", b"mbc", "name", b"name", "namespace", b"namespace", "references", b"references", "scope", b"scope", "scopes", b"scopes"]) -> None: ... global___RuleMetadata = RuleMetadata -@typing_extensions.final +@typing.final class Sample(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1376,11 +1401,11 @@ class Sample(google.protobuf.message.Message): sha256: builtins.str = ..., path: builtins.str = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["md5", b"md5", "path", b"path", "sha1", b"sha1", "sha256", b"sha256"]) -> None: ... + def ClearField(self, field_name: typing.Literal["md5", b"md5", "path", b"path", "sha1", b"sha1", "sha256", b"sha256"]) -> None: ... global___Sample = Sample -@typing_extensions.final +@typing.final class Scopes(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1394,16 +1419,16 @@ class Scopes(google.protobuf.message.Message): static: global___Scope.ValueType | None = ..., dynamic: global___Scope.ValueType | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_dynamic", b"_dynamic", "_static", b"_static", "dynamic", b"dynamic", "static", b"static"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_dynamic", b"_dynamic", "_static", b"_static", "dynamic", b"dynamic", "static", b"static"]) -> None: ... + def HasField(self, field_name: typing.Literal["_dynamic", b"_dynamic", "_static", b"_static", "dynamic", b"dynamic", "static", b"static"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_dynamic", b"_dynamic", "_static", b"_static", "dynamic", b"dynamic", "static", b"static"]) -> None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_dynamic", b"_dynamic"]) -> typing_extensions.Literal["dynamic"] | None: ... + def WhichOneof(self, oneof_group: typing.Literal["_dynamic", b"_dynamic"]) -> typing.Literal["dynamic"] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_static", b"_static"]) -> typing_extensions.Literal["static"] | None: ... + def WhichOneof(self, oneof_group: typing.Literal["_static", b"_static"]) -> typing.Literal["static"] | None: ... global___Scopes = Scopes -@typing_extensions.final +@typing.final class SectionFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1420,13 +1445,13 @@ class SectionFeature(google.protobuf.message.Message): section: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "section", b"section", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "section", b"section", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___SectionFeature = SectionFeature -@typing_extensions.final +@typing.final class SomeStatement(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1443,13 +1468,13 @@ class SomeStatement(google.protobuf.message.Message): count: builtins.int = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "count", b"count", "description", b"description", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "count", b"count", "description", b"description", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___SomeStatement = SomeStatement -@typing_extensions.final +@typing.final class StatementNode(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1476,13 +1501,13 @@ class StatementNode(google.protobuf.message.Message): subscope: global___SubscopeStatement | None = ..., compound: global___CompoundStatement | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["compound", b"compound", "range", b"range", "some", b"some", "statement", b"statement", "subscope", b"subscope"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["compound", b"compound", "range", b"range", "some", b"some", "statement", b"statement", "subscope", b"subscope", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["statement", b"statement"]) -> typing_extensions.Literal["range", "some", "subscope", "compound"] | None: ... + def HasField(self, field_name: typing.Literal["compound", b"compound", "range", b"range", "some", b"some", "statement", b"statement", "subscope", b"subscope"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["compound", b"compound", "range", b"range", "some", b"some", "statement", b"statement", "subscope", b"subscope", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["statement", b"statement"]) -> typing.Literal["range", "some", "subscope", "compound"] | None: ... global___StatementNode = StatementNode -@typing_extensions.final +@typing.final class StaticAnalysis(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1522,12 +1547,12 @@ class StaticAnalysis(google.protobuf.message.Message): feature_counts: global___StaticFeatureCounts | None = ..., library_functions: collections.abc.Iterable[global___LibraryFunction] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["base_address", b"base_address", "feature_counts", b"feature_counts", "layout", b"layout"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["arch", b"arch", "base_address", b"base_address", "extractor", b"extractor", "feature_counts", b"feature_counts", "format", b"format", "layout", b"layout", "library_functions", b"library_functions", "os", b"os", "rules", b"rules"]) -> None: ... + def HasField(self, field_name: typing.Literal["base_address", b"base_address", "feature_counts", b"feature_counts", "layout", b"layout"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["arch", b"arch", "base_address", b"base_address", "extractor", b"extractor", "feature_counts", b"feature_counts", "format", b"format", "layout", b"layout", "library_functions", b"library_functions", "os", b"os", "rules", b"rules"]) -> None: ... global___StaticAnalysis = StaticAnalysis -@typing_extensions.final +@typing.final class StaticFeatureCounts(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1542,11 +1567,11 @@ class StaticFeatureCounts(google.protobuf.message.Message): file: builtins.int = ..., functions: collections.abc.Iterable[global___FunctionFeatureCount] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["file", b"file", "functions", b"functions"]) -> None: ... + def ClearField(self, field_name: typing.Literal["file", b"file", "functions", b"functions"]) -> None: ... global___StaticFeatureCounts = StaticFeatureCounts -@typing_extensions.final +@typing.final class StaticLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1558,11 +1583,11 @@ class StaticLayout(google.protobuf.message.Message): *, functions: collections.abc.Iterable[global___FunctionLayout] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["functions", b"functions"]) -> None: ... + def ClearField(self, field_name: typing.Literal["functions", b"functions"]) -> None: ... global___StaticLayout = StaticLayout -@typing_extensions.final +@typing.final class StringFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1579,13 +1604,13 @@ class StringFeature(google.protobuf.message.Message): string: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "string", b"string", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "string", b"string", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___StringFeature = StringFeature -@typing_extensions.final +@typing.final class SubscopeStatement(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1602,13 +1627,13 @@ class SubscopeStatement(google.protobuf.message.Message): scope: global___Scope.ValueType = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "scope", b"scope", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "scope", b"scope", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___SubscopeStatement = SubscopeStatement -@typing_extensions.final +@typing.final class SubstringFeature(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1625,33 +1650,33 @@ class SubstringFeature(google.protobuf.message.Message): substring: builtins.str = ..., description: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_description", b"_description", "description", b"description", "substring", b"substring", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_description", b"_description"]) -> typing_extensions.Literal["description"] | None: ... + def HasField(self, field_name: typing.Literal["_description", b"_description", "description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_description", b"_description", "description", b"description", "substring", b"substring", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_description", b"_description"]) -> typing.Literal["description"] | None: ... global___SubstringFeature = SubstringFeature -@typing_extensions.final +@typing.final class CallLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ADDRESS_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int + name: builtins.str @property def address(self) -> global___Address: ... - name: builtins.str def __init__( self, *, address: global___Address | None = ..., name: builtins.str = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "name", b"name"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "name", b"name"]) -> None: ... global___CallLayout = CallLayout -@typing_extensions.final +@typing.final class ThreadLayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1667,12 +1692,12 @@ class ThreadLayout(google.protobuf.message.Message): address: global___Address | None = ..., matched_calls: collections.abc.Iterable[global___CallLayout] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "matched_calls", b"matched_calls"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "matched_calls", b"matched_calls"]) -> None: ... global___ThreadLayout = ThreadLayout -@typing_extensions.final +@typing.final class Addresses(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1684,11 +1709,11 @@ class Addresses(google.protobuf.message.Message): *, address: collections.abc.Iterable[global___Address] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address"]) -> None: ... + def ClearField(self, field_name: typing.Literal["address", b"address"]) -> None: ... global___Addresses = Addresses -@typing_extensions.final +@typing.final class Pair_Address_Match(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1704,33 +1729,33 @@ class Pair_Address_Match(google.protobuf.message.Message): address: global___Address | None = ..., match: global___Match | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["address", b"address", "match", b"match"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["address", b"address", "match", b"match"]) -> None: ... + def HasField(self, field_name: typing.Literal["address", b"address", "match", b"match"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "match", b"match"]) -> None: ... global___Pair_Address_Match = Pair_Address_Match -@typing_extensions.final +@typing.final class Token_Offset(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor TOKEN_FIELD_NUMBER: builtins.int OFFSET_FIELD_NUMBER: builtins.int - @property - def token(self) -> global___Integer: ... offset: builtins.int """offset is always >= 0""" + @property + def token(self) -> global___Integer: ... def __init__( self, *, token: global___Integer | None = ..., offset: builtins.int = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["token", b"token"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["offset", b"offset", "token", b"token"]) -> None: ... + def HasField(self, field_name: typing.Literal["token", b"token"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["offset", b"offset", "token", b"token"]) -> None: ... global___Token_Offset = Token_Offset -@typing_extensions.final +@typing.final class Ppid_Pid(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1746,12 +1771,12 @@ class Ppid_Pid(google.protobuf.message.Message): ppid: global___Integer | None = ..., pid: global___Integer | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["pid", b"pid", "ppid", b"ppid"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["pid", b"pid", "ppid", b"ppid"]) -> None: ... + def HasField(self, field_name: typing.Literal["pid", b"pid", "ppid", b"ppid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["pid", b"pid", "ppid", b"ppid"]) -> None: ... global___Ppid_Pid = Ppid_Pid -@typing_extensions.final +@typing.final class Ppid_Pid_Tid(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1771,12 +1796,12 @@ class Ppid_Pid_Tid(google.protobuf.message.Message): pid: global___Integer | None = ..., tid: global___Integer | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> None: ... + def HasField(self, field_name: typing.Literal["pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> None: ... global___Ppid_Pid_Tid = Ppid_Pid_Tid -@typing_extensions.final +@typing.final class Ppid_Pid_Tid_Id(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1800,12 +1825,12 @@ class Ppid_Pid_Tid_Id(google.protobuf.message.Message): tid: global___Integer | None = ..., id: global___Integer | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["id", b"id", "pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> None: ... + def HasField(self, field_name: typing.Literal["id", b"id", "pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["id", b"id", "pid", b"pid", "ppid", b"ppid", "tid", b"tid"]) -> None: ... global___Ppid_Pid_Tid_Id = Ppid_Pid_Tid_Id -@typing_extensions.final +@typing.final class Integer(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1819,13 +1844,13 @@ class Integer(google.protobuf.message.Message): u: builtins.int = ..., i: builtins.int = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["i", b"i", "u", b"u", "value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["i", b"i", "u", b"u", "value", b"value"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["u", "i"] | None: ... + def HasField(self, field_name: typing.Literal["i", b"i", "u", b"u", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["i", b"i", "u", b"u", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["value", b"value"]) -> typing.Literal["u", "i"] | None: ... global___Integer = Integer -@typing_extensions.final +@typing.final class Number(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1842,8 +1867,8 @@ class Number(google.protobuf.message.Message): i: builtins.int = ..., f: builtins.float = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["f", b"f", "i", b"i", "u", b"u", "value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["f", b"f", "i", b"i", "u", b"u", "value", b"value"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["u", "i", "f"] | None: ... + def HasField(self, field_name: typing.Literal["f", b"f", "i", b"i", "u", b"u", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["f", b"f", "i", b"i", "u", b"u", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["value", b"value"]) -> typing.Literal["u", "i", "f"] | None: ... global___Number = Number diff --git a/capa/render/result_document.py b/capa/render/result_document.py index 9ef529bef..2acfa8cf3 100644 --- a/capa/render/result_document.py +++ b/capa/render/result_document.py @@ -15,7 +15,7 @@ import datetime import collections from enum import Enum -from typing import Union, Literal, Optional, TypeAlias +from typing import TYPE_CHECKING, Union, Literal, Optional, TypeAlias from pathlib import Path from pydantic import Field, BaseModel, ConfigDict @@ -29,6 +29,10 @@ from capa.rules import RuleSet from capa.engine import MatchResults from capa.helpers import assert_never, load_json_from_path +from capa.features.address import DynamicCallAddress + +if TYPE_CHECKING: + from capa.capabilities.common import Capabilities class FrozenModel(BaseModel): @@ -389,7 +393,36 @@ def from_capa( ) for location in result.locations: - children.append(Match.from_capa(rules, capabilities, rule_matches[location])) + + # keep this in sync with the copy below + if isinstance(location, DynamicCallAddress): + if location in rule_matches: + # exact match, such as matching a call-scoped rule. + children.append(Match.from_capa(rules, capabilities, rule_matches[location])) + # we'd like to assert the scope of the current rule is span-of-calls + # but we don't have that data here. + else: + # Span-of-calls scopes can match each other, but they don't strictly contain each other, + # like the way a function contains a basic block. + # So when we have a match within a span for another span, we need to look + # for all the places it might be found. + # + # Despite the edge cases (like API hammering), this turns out to be pretty easy: + # collect the most recent match (with the given name) prior to the wanted location. + matches_in_thread = sorted( + [ + (a.id, m) + for a, m in rule_matches.items() + if isinstance(a, DynamicCallAddress) + and a.thread == location.thread + and a.id <= location.id + ] + ) + _, most_recent_match = matches_in_thread[-1] + children.append(Match.from_capa(rules, capabilities, most_recent_match)) + + else: + children.append(Match.from_capa(rules, capabilities, rule_matches[location])) else: # this is a namespace that we're matching # @@ -430,8 +463,26 @@ def from_capa( # this is a subset of doc[locations]. # # so, grab only the locations for current rule. - if location in rule_matches: - children.append(Match.from_capa(rules, capabilities, rule_matches[location])) + + # keep this in sync with the block above. + if isinstance(location, DynamicCallAddress): + if location in rule_matches: + children.append(Match.from_capa(rules, capabilities, rule_matches[location])) + else: + matches_in_thread = sorted( + [ + (a.id, m) + for a, m in rule_matches.items() + if isinstance(a, DynamicCallAddress) + and a.thread == location.thread + and a.id <= location.id + ] + ) + _, most_recent_match = matches_in_thread[-1] + children.append(Match.from_capa(rules, capabilities, most_recent_match)) + else: + if location in rule_matches: + children.append(Match.from_capa(rules, capabilities, rule_matches[location])) return cls( success=success, @@ -474,6 +525,27 @@ def to_capa(self, rules_by_name: dict[str, capa.rules.Rule]) -> capa.engine.Resu children=children, ) + def __str__(self): + # as this object isn't user facing, this formatting is just to help with debugging + + lines = [] + + def rec(m: "Match", indent: int): + if isinstance(m.node, StatementNode): + line = (" " * indent) + str(m.node.statement.type) + " " + str(m.success) + elif isinstance(m.node, FeatureNode): + line = (" " * indent) + str(m.node.feature) + " " + str(m.success) + " " + str(m.locations) + else: + raise ValueError("unexpected node type") + + lines.append(line) + + for child in m.children: + rec(child, indent + 1) + + rec(self, 0) + return "\n".join(lines) + def parse_parts_id(s: str): id_ = "" @@ -654,8 +726,10 @@ def from_capa(cls, meta: Metadata, rules: RuleSet, capabilities: MatchResults) - return ResultDocument(meta=meta, rules=rule_matches) - def to_capa(self) -> tuple[Metadata, dict]: - capabilities: dict[str, list[tuple[capa.features.address.Address, capa.features.common.Result]]] = ( + def to_capa(self) -> tuple[Metadata, "Capabilities"]: + from capa.capabilities.common import Capabilities + + matches: dict[str, list[tuple[capa.features.address.Address, capa.features.common.Result]]] = ( collections.defaultdict(list) ) @@ -668,7 +742,14 @@ def to_capa(self) -> tuple[Metadata, dict]: for addr, match in rule_match.matches: result: capa.engine.Result = match.to_capa(rules_by_name) - capabilities[rule_name].append((addr.to_capa(), result)) + matches[rule_name].append((addr.to_capa(), result)) + + if isinstance(self.meta.analysis, StaticAnalysis): + capabilities = Capabilities( + matches, self.meta.analysis.feature_counts, self.meta.analysis.library_functions + ) + elif isinstance(self.meta.analysis, DynamicAnalysis): + capabilities = Capabilities(matches, self.meta.analysis.feature_counts) return self.meta, capabilities diff --git a/capa/render/verbose.py b/capa/render/verbose.py index 08c0707cb..11f244237 100644 --- a/capa/render/verbose.py +++ b/capa/render/verbose.py @@ -126,6 +126,22 @@ def render_thread(layout: rd.DynamicLayout, addr: frz.Address) -> str: return f"{name}{{pid:{thread.process.pid},tid:{thread.tid}}}" +def render_span_of_calls(layout: rd.DynamicLayout, addrs: list[frz.Address]) -> str: + calls: list[capa.features.address.DynamicCallAddress] = [addr.to_capa() for addr in addrs] # type: ignore + assert len(calls) > 0 + for call in calls: + assert isinstance(call, capa.features.address.DynamicCallAddress) + call = calls[0] + + pname = _get_process_name(layout, frz.Address.from_capa(calls[0].thread.process)) + call_ids = [str(call.id) for call in calls] + if len(call_ids) == 1: + call_id = call_ids[0] + return f"{pname}{{pid:{call.thread.process.pid},tid:{call.thread.tid},call:{call_id}}}" + else: + return f"{pname}{{pid:{call.thread.process.pid},tid:{call.thread.tid},calls:{{{','.join(call_ids)}}}}}" + + def render_call(layout: rd.DynamicLayout, addr: frz.Address) -> str: call = addr.to_capa() assert isinstance(call, capa.features.address.DynamicCallAddress) @@ -148,6 +164,25 @@ def render_call(layout: rd.DynamicLayout, addr: frz.Address) -> str: ) +def render_short_call(layout: rd.DynamicLayout, addr: frz.Address) -> str: + call = addr.to_capa() + assert isinstance(call, capa.features.address.DynamicCallAddress) + + cname = _get_call_name(layout, addr) + + fname, _, rest = cname.partition("(") + args, _, rest = rest.rpartition(")") + + s = [] + s.append(f"{fname}(") + for arg in args.split(", "): + s.append(f" {arg},") + s.append(f"){rest}") + + newline = "\n" + return f"call:{call.id}\n{rutils.mute(newline.join(s))}" + + def render_static_meta(console: Console, meta: rd.StaticMetadata): """ like: @@ -318,7 +353,7 @@ def render_rules(console: Console, doc: rd.ResultDocument): lines = [render_process(doc.meta.analysis.layout, loc) for loc in locations] elif rule.meta.scopes.dynamic == capa.rules.Scope.THREAD: lines = [render_thread(doc.meta.analysis.layout, loc) for loc in locations] - elif rule.meta.scopes.dynamic == capa.rules.Scope.CALL: + elif rule.meta.scopes.dynamic in (capa.rules.Scope.CALL, capa.rules.Scope.SPAN_OF_CALLS): # because we're only in verbose mode, we won't show the full call details (name, args, retval) # we'll only show the details of the thread in which the calls are found. # so select the thread locations and render those. diff --git a/capa/render/vverbose.py b/capa/render/vverbose.py index 428dffcbc..e905f8c0f 100644 --- a/capa/render/vverbose.py +++ b/capa/render/vverbose.py @@ -54,7 +54,19 @@ def hanging_indent(s: str, indent: int) -> str: return textwrap.indent(s, prefix=prefix)[len(prefix) :] -def render_locations(console: Console, layout: rd.Layout, locations: Iterable[frz.Address], indent: int): +def render_locations( + console: Console, layout: rd.Layout, locations: Iterable[frz.Address], indent: int, use_short_format: bool = False +): + """ + Render the given locations, such as virtual address or pid/tid/callid with process name. + + If `use_short_format` is True: + render a small representation of the given locations, such as just the call id, + assuming another region of output specified the full location details (like pid/tid). + + In effect, rather than rendering ppid/pid/tid/callid everywhere, + describe ppid/pid/tid once, and then refer to just callid in the subsequent blocklin the subsequent block. + """ import capa.render.verbose as v # it's possible to have an empty locations array here, @@ -73,7 +85,11 @@ def render_locations(console: Console, layout: rd.Layout, locations: Iterable[fr if location.type == frz.AddressType.CALL: assert isinstance(layout, rd.DynamicLayout) - console.write(hanging_indent(v.render_call(layout, location), indent + 1)) + if use_short_format: + render_call = v.render_short_call + else: + render_call = v.render_call + console.write(hanging_indent(render_call(layout, location), indent + 1)) else: console.write(v.format_address(locations[0])) @@ -81,6 +97,10 @@ def render_locations(console: Console, layout: rd.Layout, locations: Iterable[fr location = locations[0] assert isinstance(layout, rd.DynamicLayout) + if use_short_format: + render_call = v.render_short_call + else: + render_call = v.render_call s = f"{v.render_call(layout, location)}\nand {(len(locations) - 1)} more..." console.write(hanging_indent(s, indent + 1)) @@ -230,10 +250,11 @@ def render_feature( # if we're in call scope, then the call will have been rendered at the top # of the output, so don't re-render it again for each feature. pass - elif isinstance(feature, (frzf.OSFeature, frzf.ArchFeature, frzf.FormatFeature)): + elif isinstance(layout, rd.DynamicLayout) and isinstance(feature, frzf.MatchFeature): + # don't render copies of the span of calls address for submatches pass else: - render_locations(console, layout, match.locations, indent) + render_locations(console, layout, match.locations, indent, use_short_format=True) console.writeln() else: # like: @@ -250,7 +271,7 @@ def render_feature( # like above, don't re-render calls when in call scope. pass else: - render_locations(console, layout, locations, indent=indent) + render_locations(console, layout, locations, indent=indent + 1, use_short_format=True) console.writeln() @@ -311,6 +332,36 @@ def render_match( render_match(console, layout, rule, child, indent=indent + 1, mode=child_mode) +def collect_span_of_calls_locations( + match: rd.Match, + mode=MODE_SUCCESS, +): + """ + Find all the call locations used in a given span-of-calls match, recursively. + Useful to collect the events used to match a span-of-calls scoped rule. + """ + if not match.success: + return + + for location in match.locations: + if location.type != frz.AddressType.CALL: + continue + + if mode == MODE_FAILURE: + # only collect positive evidence, + # not things that filter out branches. + continue + + yield location + + child_mode = mode + if isinstance(match.node, rd.StatementNode) and match.node.statement.type == rd.CompoundStatementType.NOT: + child_mode = MODE_FAILURE if mode == MODE_SUCCESS else MODE_SUCCESS + + for child in match.children: + yield from collect_span_of_calls_locations(child, child_mode) + + def render_rules(console: Console, doc: rd.ResultDocument): """ like: @@ -450,6 +501,9 @@ def render_rules(console: Console, doc: rd.ResultDocument): console.write(v.render_process(doc.meta.analysis.layout, location)) elif rule.meta.scopes.dynamic == capa.rules.Scope.THREAD: console.write(v.render_thread(doc.meta.analysis.layout, location)) + elif rule.meta.scopes.dynamic == capa.rules.Scope.SPAN_OF_CALLS: + calls = sorted(set(collect_span_of_calls_locations(match))) + console.write(hanging_indent(v.render_span_of_calls(doc.meta.analysis.layout, calls), indent=1)) elif rule.meta.scopes.dynamic == capa.rules.Scope.CALL: console.write(hanging_indent(v.render_call(doc.meta.analysis.layout, location), indent=1)) else: diff --git a/capa/rules/__init__.py b/capa/rules/__init__.py index 291927d22..7c1025256 100644 --- a/capa/rules/__init__.py +++ b/capa/rules/__init__.py @@ -86,6 +86,7 @@ class Scope(str, Enum): FILE = "file" PROCESS = "process" THREAD = "thread" + SPAN_OF_CALLS = "span of calls" CALL = "call" FUNCTION = "function" BASIC_BLOCK = "basic block" @@ -114,6 +115,7 @@ def to_yaml(cls, representer, node): Scope.GLOBAL, Scope.PROCESS, Scope.THREAD, + Scope.SPAN_OF_CALLS, Scope.CALL, } @@ -199,6 +201,7 @@ def from_dict(self, scopes: dict[str, str]) -> "Scopes": capa.features.common.MatchedRule, }, Scope.THREAD: set(), + Scope.SPAN_OF_CALLS: set(), Scope.CALL: { capa.features.common.MatchedRule, capa.features.common.Regex, @@ -253,11 +256,14 @@ def from_dict(self, scopes: dict[str, str]) -> "Scopes": SUPPORTED_FEATURES[Scope.FILE].update(SUPPORTED_FEATURES[Scope.GLOBAL]) SUPPORTED_FEATURES[Scope.PROCESS].update(SUPPORTED_FEATURES[Scope.GLOBAL]) SUPPORTED_FEATURES[Scope.THREAD].update(SUPPORTED_FEATURES[Scope.GLOBAL]) +SUPPORTED_FEATURES[Scope.SPAN_OF_CALLS].update(SUPPORTED_FEATURES[Scope.GLOBAL]) SUPPORTED_FEATURES[Scope.CALL].update(SUPPORTED_FEATURES[Scope.GLOBAL]) -# all call scope features are also thread features -SUPPORTED_FEATURES[Scope.THREAD].update(SUPPORTED_FEATURES[Scope.CALL]) +# all call scope features are also span-of-calls features +SUPPORTED_FEATURES[Scope.SPAN_OF_CALLS].update(SUPPORTED_FEATURES[Scope.CALL]) +# all span-of-calls scope features (and therefore, call features) are also thread features +SUPPORTED_FEATURES[Scope.THREAD].update(SUPPORTED_FEATURES[Scope.SPAN_OF_CALLS]) # all thread scope features are also process features SUPPORTED_FEATURES[Scope.PROCESS].update(SUPPORTED_FEATURES[Scope.THREAD]) @@ -616,7 +622,7 @@ def build_statements(d, scopes: Scopes): elif key == "process": if Scope.FILE not in scopes: - raise InvalidRule("process subscope supported only for file scope") + raise InvalidRule("`process` subscope supported only for `file` scope") if len(d[key]) != 1: raise InvalidRule("subscope must have exactly one child statement") @@ -627,7 +633,7 @@ def build_statements(d, scopes: Scopes): elif key == "thread": if all(s not in scopes for s in (Scope.FILE, Scope.PROCESS)): - raise InvalidRule("thread subscope supported only for the process scope") + raise InvalidRule("`thread` subscope supported only for the `process` scope") if len(d[key]) != 1: raise InvalidRule("subscope must have exactly one child statement") @@ -636,9 +642,22 @@ def build_statements(d, scopes: Scopes): Scope.THREAD, build_statements(d[key][0], Scopes(dynamic=Scope.THREAD)), description=description ) + elif key == "span of calls": + if all(s not in scopes for s in (Scope.FILE, Scope.PROCESS, Scope.THREAD, Scope.SPAN_OF_CALLS)): + raise InvalidRule("`span of calls` subscope supported only for the `process` and `thread` scopes") + + if len(d[key]) != 1: + raise InvalidRule("subscope must have exactly one child statement") + + return ceng.Subscope( + Scope.SPAN_OF_CALLS, + build_statements(d[key][0], Scopes(dynamic=Scope.SPAN_OF_CALLS)), + description=description, + ) + elif key == "call": - if all(s not in scopes for s in (Scope.FILE, Scope.PROCESS, Scope.THREAD, Scope.CALL)): - raise InvalidRule("call subscope supported only for the process, thread, and call scopes") + if all(s not in scopes for s in (Scope.FILE, Scope.PROCESS, Scope.THREAD, Scope.SPAN_OF_CALLS, Scope.CALL)): + raise InvalidRule("`call` subscope supported only for the `process`, `thread`, and `call` scopes") if len(d[key]) != 1: raise InvalidRule("subscope must have exactly one child statement") @@ -649,7 +668,7 @@ def build_statements(d, scopes: Scopes): elif key == "function": if Scope.FILE not in scopes: - raise InvalidRule("function subscope supported only for file scope") + raise InvalidRule("`function` subscope supported only for `file` scope") if len(d[key]) != 1: raise InvalidRule("subscope must have exactly one child statement") @@ -660,7 +679,7 @@ def build_statements(d, scopes: Scopes): elif key == "basic block": if Scope.FUNCTION not in scopes: - raise InvalidRule("basic block subscope supported only for function scope") + raise InvalidRule("`basic block` subscope supported only for `function` scope") if len(d[key]) != 1: raise InvalidRule("subscope must have exactly one child statement") @@ -671,7 +690,7 @@ def build_statements(d, scopes: Scopes): elif key == "instruction": if all(s not in scopes for s in (Scope.FUNCTION, Scope.BASIC_BLOCK)): - raise InvalidRule("instruction subscope supported only for function and basic block scope") + raise InvalidRule("`instruction` subscope supported only for `function` and `basic block` scope") if len(d[key]) == 1: statements = build_statements(d[key][0], Scopes(static=Scope.INSTRUCTION)) @@ -853,18 +872,18 @@ def __str__(self): def __repr__(self): return f"Rule(scope={self.scopes}, name={self.name})" - def get_dependencies(self, namespaces): + def get_dependencies(self, namespaces: dict[str, list["Rule"]]) -> set[str]: """ fetch the names of rules this rule relies upon. these are only the direct dependencies; a user must compute the transitive dependency graph themself, if they want it. Args: - namespaces(dict[str, list[Rule]]): mapping from namespace name to rules in it. + namespaces: mapping from namespace name to rules in it. see `index_rules_by_namespace`. Returns: - list[str]: names of rules upon which this rule depends. + set[str]: names of rules upon which this rule depends. """ deps: set[str] = set() @@ -880,6 +899,7 @@ def rec(statement): # but, namespaces tend to use `-` while rule names use ` `. so, unlikely, but possible. if statement.value in namespaces: # matches a namespace, so take precedence and don't even check rule names. + assert isinstance(statement.value, str) deps.update(r.name for r in namespaces[statement.value]) else: # not a namespace, assume it's a rule name. @@ -1383,6 +1403,7 @@ def __init__( scopes = ( Scope.CALL, + Scope.SPAN_OF_CALLS, Scope.THREAD, Scope.PROCESS, Scope.INSTRUCTION, @@ -1413,6 +1434,10 @@ def process_rules(self): def thread_rules(self): return self.rules_by_scope[Scope.THREAD] + @property + def span_of_calls_rules(self): + return self.rules_by_scope[Scope.SPAN_OF_CALLS] + @property def call_rules(self): return self.rules_by_scope[Scope.CALL] diff --git a/rules b/rules index b4e0c8cdf..ff76d01d6 160000 --- a/rules +++ b/rules @@ -1 +1 @@ -Subproject commit b4e0c8cdf89a9d1fc41c2fc151bbb9c985d59a9e +Subproject commit ff76d01d62095839d8f430cc5538bf5c678e138b diff --git a/scripts/bulk-process.py b/scripts/bulk-process.py index f7324d7ee..768a551b2 100644 --- a/scripts/bulk-process.py +++ b/scripts/bulk-process.py @@ -146,12 +146,12 @@ def get_capa_results(args): "error": f"unexpected error: {e}", } - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) - meta = capa.loader.collect_metadata(argv, args.input_file, format_, os_, [], extractor, counts) - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta = capa.loader.collect_metadata(argv, args.input_file, format_, os_, [], extractor, capabilities) + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) - doc = rd.ResultDocument.from_capa(meta, rules, capabilities) + doc = rd.ResultDocument.from_capa(meta, rules, capabilities.matches) return {"path": input_file, "status": "ok", "ok": doc.model_dump()} diff --git a/scripts/capa-as-library.py b/scripts/capa-as-library.py index 3d556e21a..1d935a8e8 100644 --- a/scripts/capa-as-library.py +++ b/scripts/capa-as-library.py @@ -184,25 +184,25 @@ def capa_details(rules_path: Path, input_file: Path, output_format="dictionary") extractor = capa.loader.get_extractor( input_file, FORMAT_AUTO, OS_AUTO, capa.main.BACKEND_VIV, [], should_save_workspace=False, disable_progress=True ) - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) # collect metadata (used only to make rendering more complete) - meta = capa.loader.collect_metadata([], input_file, FORMAT_AUTO, OS_AUTO, [rules_path], extractor, counts) - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta = capa.loader.collect_metadata([], input_file, FORMAT_AUTO, OS_AUTO, [rules_path], extractor, capabilities) + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) capa_output: Any = False if output_format == "dictionary": # ...as python dictionary, simplified as textable but in dictionary - doc = rd.ResultDocument.from_capa(meta, rules, capabilities) + doc = rd.ResultDocument.from_capa(meta, rules, capabilities.matches) capa_output = render_dictionary(doc) elif output_format == "json": # render results # ...as json - capa_output = json.loads(capa.render.json.render(meta, rules, capabilities)) + capa_output = json.loads(capa.render.json.render(meta, rules, capabilities.matches)) elif output_format == "texttable": # ...as human readable text table - capa_output = capa.render.default.render(meta, rules, capabilities) + capa_output = capa.render.default.render(meta, rules, capabilities.matches) return capa_output diff --git a/scripts/detect-binexport2-capabilities.py b/scripts/detect-binexport2-capabilities.py index 6cf47789f..0a170d299 100644 --- a/scripts/detect-binexport2-capabilities.py +++ b/scripts/detect-binexport2-capabilities.py @@ -100,12 +100,12 @@ def main(argv=None): except capa.main.ShouldExitError as e: return e.status_code - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor) - meta = capa.loader.collect_metadata(argv, args.input_file, input_format, os_, args.rules, extractor, counts) - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta = capa.loader.collect_metadata(argv, args.input_file, input_format, os_, args.rules, extractor, capabilities) + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) - doc = rd.ResultDocument.from_capa(meta, rules, capabilities) + doc = rd.ResultDocument.from_capa(meta, rules, capabilities.matches) pb = capa.render.proto.doc_to_pb2(doc) sys.stdout.buffer.write(pb.SerializeToString(deterministic=True)) diff --git a/scripts/import-to-ida.py b/scripts/import-to-ida.py index d3eb92c7c..89ba19454 100644 --- a/scripts/import-to-ida.py +++ b/scripts/import-to-ida.py @@ -91,7 +91,7 @@ def main(): return -2 rows = [] - for name in capabilities.keys(): + for name in capabilities.matches.keys(): rule = result_doc.rules[name] if rule.meta.lib: continue diff --git a/scripts/lint.py b/scripts/lint.py index a24dc8260..18fec17c4 100644 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -195,6 +195,7 @@ def check_rule(self, ctx: Context, rule: Rule): "file", "process", "thread", + "span of calls", "call", "unsupported", ) @@ -358,10 +359,10 @@ def get_sample_capabilities(ctx: Context, path: Path) -> set[str]: disable_progress=True, ) - capabilities, _ = capa.capabilities.common.find_capabilities(ctx.rules, extractor, disable_progress=True) + capabilities = capa.capabilities.common.find_capabilities(ctx.rules, extractor, disable_progress=True) # mypy doesn't seem to be happy with the MatchResults type alias & set(...keys())? # so we ignore a few types here. - capabilities = set(capabilities.keys()) # type: ignore + capabilities = set(capabilities.matches.keys()) # type: ignore assert isinstance(capabilities, set) logger.debug("computed results: %s: %d capabilities", nice_path, len(capabilities)) diff --git a/scripts/show-capabilities-by-function.py b/scripts/show-capabilities-by-function.py index bf6261257..5ae5c5d08 100644 --- a/scripts/show-capabilities-by-function.py +++ b/scripts/show-capabilities-by-function.py @@ -155,18 +155,18 @@ def main(argv=None): except capa.main.ShouldExitError as e: return e.status_code - capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor) - meta = capa.loader.collect_metadata(argv, args.input_file, input_format, os_, args.rules, extractor, counts) - meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities) + meta = capa.loader.collect_metadata(argv, args.input_file, input_format, os_, args.rules, extractor, capabilities) + meta.analysis.layout = capa.loader.compute_layout(rules, extractor, capabilities.matches) - if capa.capabilities.common.has_file_limitation(rules, capabilities): + if capa.capabilities.common.has_file_limitation(rules, capabilities.matches): # 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 capa.main.E_FILE_LIMITATION - doc = rd.ResultDocument.from_capa(meta, rules, capabilities) + doc = rd.ResultDocument.from_capa(meta, rules, capabilities.matches) print(render_matches_by_function(doc)) colorama.deinit() diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py index 41abd5606..809173da2 100644 --- a/tests/test_capabilities.py +++ b/tests/test_capabilities.py @@ -82,10 +82,10 @@ def test_match_across_scopes_file_function(z9324d_extractor): ), ] ) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "install service" in capabilities - assert ".text section" in capabilities - assert ".text section and install service" in capabilities + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "install service" in capabilities.matches + assert ".text section" in capabilities.matches + assert ".text section and install service" in capabilities.matches def test_match_across_scopes(z9324d_extractor): @@ -150,10 +150,10 @@ def test_match_across_scopes(z9324d_extractor): ), ] ) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "tight loop" in capabilities - assert "kill thread loop" in capabilities - assert "kill thread program" in capabilities + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "tight loop" in capabilities.matches + assert "kill thread loop" in capabilities.matches + assert "kill thread program" in capabilities.matches def test_subscope_bb_rules(z9324d_extractor): @@ -178,8 +178,8 @@ def test_subscope_bb_rules(z9324d_extractor): ] ) # tight loop at 0x403685 - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "test rule" in capabilities + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "test rule" in capabilities.matches def test_match_specific_functions(z9324d_extractor): @@ -205,8 +205,8 @@ def test_match_specific_functions(z9324d_extractor): ] ) extractor = FunctionFilter(z9324d_extractor, {0x4019C0}) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, extractor) - matches = capabilities["receive data"] + capabilities = capa.capabilities.common.find_capabilities(rules, extractor) + matches = capabilities.matches["receive data"] # test that we received only one match assert len(matches) == 1 # and that this match is from the specified function @@ -233,8 +233,8 @@ def test_byte_matching(z9324d_extractor): ) ] ) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "byte match test" in capabilities + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "byte match test" in capabilities.matches def test_com_feature_matching(z395eb_extractor): @@ -259,8 +259,8 @@ def test_com_feature_matching(z395eb_extractor): ) ] ) - capabilities, meta = capa.main.find_capabilities(rules, z395eb_extractor) - assert "initialize IWebBrowser2" in capabilities + capabilities = capa.main.find_capabilities(rules, z395eb_extractor) + assert "initialize IWebBrowser2" in capabilities.matches def test_count_bb(z9324d_extractor): @@ -284,8 +284,8 @@ def test_count_bb(z9324d_extractor): ) ] ) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "count bb" in capabilities + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "count bb" in capabilities.matches def test_instruction_scope(z9324d_extractor): @@ -311,9 +311,9 @@ def test_instruction_scope(z9324d_extractor): ) ] ) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "push 1000" in capabilities - assert 0x4071A4 in {result[0] for result in capabilities["push 1000"]} + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "push 1000" in capabilities.matches + assert 0x4071A4 in {result[0] for result in capabilities.matches["push 1000"]} def test_instruction_subscope(z9324d_extractor): @@ -343,6 +343,6 @@ def test_instruction_subscope(z9324d_extractor): ) ] ) - capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) - assert "push 1000 on i386" in capabilities - assert 0x406F60 in {result[0] for result in capabilities["push 1000 on i386"]} + capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) + assert "push 1000 on i386" in capabilities.matches + assert 0x406F60 in {result[0] for result in capabilities.matches["push 1000 on i386"]} diff --git a/tests/test_dynamic_span_of_calls_scope.py b/tests/test_dynamic_span_of_calls_scope.py new file mode 100644 index 000000000..5f1a19a70 --- /dev/null +++ b/tests/test_dynamic_span_of_calls_scope.py @@ -0,0 +1,469 @@ +# Copyright 2022 Google LLC +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +# tests/data/dynamic/cape/v2.2/0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json.gz +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# ... +# thread: 3064 +# call 8: GetSystemTimeAsFileTime() +# call 9: GetSystemInfo() +# call 10: LdrGetDllHandle(1974337536, kernel32.dll) +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: LdrGetDllHandle(1974337536, kernel32.dll) +# call 13: LdrGetProcedureAddress(2010595072, 0, RemoveVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 14: RtlAddVectoredExceptionHandler(1921490089, 0) +# call 15: GetSystemTime() +# call 16: NtAllocateVirtualMemory(no, 4, 786432, 4784128, 4294967295) +# call 17: NtAllocateVirtualMemory(no, 4, 12288, 4784128, 4294967295) +# call 18: GetSystemInfo() +# ... +# ... + +import textwrap +from typing import Iterator +from functools import lru_cache + +import pytest +import fixtures + +import capa.main +import capa.rules +import capa.capabilities.dynamic +from capa.features.extractors.base_extractor import ThreadFilter, DynamicFeatureExtractor + + +def filter_threads(extractor: DynamicFeatureExtractor, ppid: int, pid: int, tid: int) -> DynamicFeatureExtractor: + for ph in extractor.get_processes(): + if (ph.address.ppid, ph.address.pid) != (ppid, pid): + continue + + for th in extractor.get_threads(ph): + if th.address.tid != tid: + continue + + return ThreadFilter( + extractor, + { + th.address, + }, + ) + + raise ValueError("failed to find target thread") + + +@lru_cache(maxsize=1) +def get_0000a657_thread3064(): + extractor = fixtures.get_cape_extractor(fixtures.get_data_path_by_name("0000a657")) + extractor = filter_threads(extractor, 2456, 3052, 3064) + return extractor + + +def get_call_ids(matches) -> Iterator[int]: + for address, _ in matches: + yield address.id + + +# sanity check: match the first call +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# call 8: GetSystemTimeAsFileTime() +def test_dynamic_call_scope(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: call + features: + - api: GetSystemTimeAsFileTime + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + assert r.name in capabilities.matches + assert 8 in get_call_ids(capabilities.matches[r.name]) + + +# match the first span. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# call 8: GetSystemTimeAsFileTime() +# call 9: GetSystemInfo() +# call 10: LdrGetDllHandle(1974337536, kernel32.dll) +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: LdrGetDllHandle(1974337536, kernel32.dll) +def test_dynamic_span_scope(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - api: GetSystemTimeAsFileTime + - api: GetSystemInfo + - api: LdrGetDllHandle + - api: LdrGetProcedureAddress + - count(api(LdrGetDllHandle)): 2 + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + assert r.name in capabilities.matches + assert 12 in get_call_ids(capabilities.matches[r.name]) + + +# show that when the span is only 5 calls long (for example), it doesn't match beyond that 5-tuple. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# call 8: GetSystemTimeAsFileTime() +# call 9: GetSystemInfo() +# call 10: LdrGetDllHandle(1974337536, kernel32.dll) +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: LdrGetDllHandle(1974337536, kernel32.dll) +# call 13: LdrGetProcedureAddress(2010595072, 0, RemoveVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 14: RtlAddVectoredExceptionHandler(1921490089, 0) +# call 15: GetSystemTime() +# call 16: NtAllocateVirtualMemory(no, 4, 786432, 4784128, 4294967295) +def test_dynamic_span_scope_length(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - api: GetSystemTimeAsFileTime + - api: RtlAddVectoredExceptionHandler + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + # patch SPAN_SIZE since we may use a much larger value in the real world. + from pytest import MonkeyPatch + + with MonkeyPatch.context() as m: + m.setattr(capa.capabilities.dynamic, "SPAN_SIZE", 5) + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + + assert r.name not in capabilities.matches + + +# show that you can use a call subscope in span-of-calls rules. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# ... +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# ... +def test_dynamic_span_call_subscope(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - call: + - and: + - api: LdrGetProcedureAddress + - string: AddVectoredExceptionHandler + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + assert r.name in capabilities.matches + assert 11 in get_call_ids(capabilities.matches[r.name]) + + +# show that you can use a span subscope in span rules. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# ... +# call 10: LdrGetDllHandle(1974337536, kernel32.dll) +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: LdrGetDllHandle(1974337536, kernel32.dll) +# call 13: LdrGetProcedureAddress(2010595072, 0, RemoveVectoredExceptionHandler, 1974337536, kernel32.dll) +# ... +def test_dynamic_span_scope_span_subscope(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - span of calls: + - description: resolve add VEH # should match at 11 + - and: + - api: LdrGetDllHandle + - api: LdrGetProcedureAddress + - string: AddVectoredExceptionHandler + - span of calls: + - description: resolve remove VEH # should match at 13 + - and: + - api: LdrGetDllHandle + - api: LdrGetProcedureAddress + - string: RemoveVectoredExceptionHandler + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + assert r.name in capabilities.matches + assert 13 in get_call_ids(capabilities.matches[r.name]) + + +# show that you can't use thread subscope in span rules. +def test_dynamic_span_scope_thread_subscope(): + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - thread: + - string: "foo" + """ + ) + + with pytest.raises(capa.rules.InvalidRule): + capa.rules.Rule.from_yaml(rule) + + +# show how you might use a span-of-calls rule: to match a small window for a collection of features. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# call 10: LdrGetDllHandle(1974337536, kernel32.dll) +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: ... +# call 13: ... +# call 14: RtlAddVectoredExceptionHandler(1921490089, 0) +def test_dynamic_span_example(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - call: + - and: + - api: LdrGetDllHandle + - string: "kernel32.dll" + - call: + - and: + - api: LdrGetProcedureAddress + - string: "AddVectoredExceptionHandler" + - api: RtlAddVectoredExceptionHandler + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + assert r.name in capabilities.matches + assert 14 in get_call_ids(capabilities.matches[r.name]) + + +# show how spans that overlap a single event are handled. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# ... +# call 10: ... +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: ... +# call 13: ... +# call 14: ... +# call 15: ... +# ... +def test_dynamic_span_multiple_spans_overlapping_single_event(): + extractor = get_0000a657_thread3064() + + rule = textwrap.dedent( + """ + rule: + meta: + name: test rule + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - call: + - and: + - api: LdrGetProcedureAddress + - string: "AddVectoredExceptionHandler" + """ + ) + + r = capa.rules.Rule.from_yaml(rule) + ruleset = capa.rules.RuleSet([r]) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + assert r.name in capabilities.matches + # we only match the first overlapping span + assert [11] == list(get_call_ids(capabilities.matches[r.name])) + + +# show that you can use match statements in span-of-calls rules. +# +# proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) +# thread: 3064 +# ... +# call 10: LdrGetDllHandle(1974337536, kernel32.dll) +# call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) +# call 12: LdrGetDllHandle(1974337536, kernel32.dll) +# call 13: LdrGetProcedureAddress(2010595072, 0, RemoveVectoredExceptionHandler, 1974337536, kernel32.dll) +# ... +def test_dynamic_span_scope_match_statements(): + extractor = get_0000a657_thread3064() + + ruleset = capa.rules.RuleSet( + [ + capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: resolve add VEH + namespace: linking/runtime-linking/veh + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - api: LdrGetDllHandle + - api: LdrGetProcedureAddress + - string: AddVectoredExceptionHandler + """ + ) + ), + capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: resolve remove VEH + namespace: linking/runtime-linking/veh + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - api: LdrGetDllHandle + - api: LdrGetProcedureAddress + - string: RemoveVectoredExceptionHandler + """ + ) + ), + capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: resolve add and remove VEH + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - match: resolve add VEH + - match: resolve remove VEH + """ + ) + ), + capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: has VEH runtime linking + scopes: + static: unsupported + dynamic: span of calls + features: + - and: + - match: linking/runtime-linking/veh + """ + ) + ), + ] + ) + + capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) + + # basic functionality, already known to work + assert "resolve add VEH" in capabilities.matches + assert "resolve remove VEH" in capabilities.matches + + # requires `match: ` to be working + assert "resolve add and remove VEH" in capabilities.matches + + # requires `match: ` to be working + assert "has VEH runtime linking" in capabilities.matches diff --git a/tests/test_freeze_dynamic.py b/tests/test_freeze_dynamic.py index eca671574..9ed84fa4c 100644 --- a/tests/test_freeze_dynamic.py +++ b/tests/test_freeze_dynamic.py @@ -125,8 +125,8 @@ def test_null_feature_extractor(): ), ] ) - capabilities, _ = capa.main.find_capabilities(rules, EXTRACTOR) - assert "create file" in capabilities + capabilities = capa.main.find_capabilities(rules, EXTRACTOR) + assert "create file" in capabilities.matches def compare_extractors(a: DynamicFeatureExtractor, b: DynamicFeatureExtractor): diff --git a/tests/test_freeze_static.py b/tests/test_freeze_static.py index fe1aafa03..e9f58bc42 100644 --- a/tests/test_freeze_static.py +++ b/tests/test_freeze_static.py @@ -107,8 +107,8 @@ def test_null_feature_extractor(): ), ] ) - capabilities, meta = capa.main.find_capabilities(rules, EXTRACTOR) - assert "xor loop" in capabilities + capabilities = capa.main.find_capabilities(rules, EXTRACTOR) + assert "xor loop" in capabilities.matches def compare_extractors(a, b): diff --git a/tests/test_proto.py b/tests/test_proto.py index 8e6e12bd4..b0dc10604 100644 --- a/tests/test_proto.py +++ b/tests/test_proto.py @@ -129,6 +129,7 @@ def test_scope_to_pb2(): assert capa.render.proto.scope_to_pb2(capa.rules.Scope.INSTRUCTION) == capa_pb2.SCOPE_INSTRUCTION assert capa.render.proto.scope_to_pb2(capa.rules.Scope.PROCESS) == capa_pb2.SCOPE_PROCESS assert capa.render.proto.scope_to_pb2(capa.rules.Scope.THREAD) == capa_pb2.SCOPE_THREAD + assert capa.render.proto.scope_to_pb2(capa.rules.Scope.SPAN_OF_CALLS) == capa_pb2.SCOPE_SPAN_OF_CALLS assert capa.render.proto.scope_to_pb2(capa.rules.Scope.CALL) == capa_pb2.SCOPE_CALL diff --git a/tests/test_result_document.py b/tests/test_result_document.py index 0c1d842fd..7775c0330 100644 --- a/tests/test_result_document.py +++ b/tests/test_result_document.py @@ -21,6 +21,7 @@ import capa.engine as ceng import capa.render.result_document as rdoc import capa.features.freeze.features as frzf +from capa.capabilities.common import Capabilities def test_optional_node_from_capa(): @@ -289,4 +290,4 @@ def test_rdoc_to_capa(): meta, capabilites = rd.to_capa() assert isinstance(meta, rdoc.Metadata) - assert isinstance(capabilites, dict) + assert isinstance(capabilites, Capabilities)