From 06472c1e72f89616e687a14fd0745d19fd668b1b Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Fri, 17 Jan 2025 12:44:47 +0000 Subject: [PATCH] rename "sequence" scope to "span of calls" scope --- CHANGELOG.md | 4 +- capa/capabilities/dynamic.py | 82 +++++++++---------- capa/render/proto/__init__.py | 8 +- capa/render/proto/capa.proto | 2 +- capa/render/proto/capa_pb2.py | 4 +- capa/render/proto/capa_pb2.pyi | 4 +- capa/render/result_document.py | 6 +- capa/render/verbose.py | 4 +- capa/render/vverbose.py | 18 ++-- capa/rules/__init__.py | 44 +++++----- scripts/lint.py | 2 +- ...py => test_dynamic_span_of_calls_scope.py} | 82 +++++++++---------- tests/test_proto.py | 2 +- 13 files changed, 131 insertions(+), 131 deletions(-) rename tests/{test_dynamic_sequence_scope.py => test_dynamic_span_of_calls_scope.py} (86%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b727bba3be..1acbfeced7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,11 @@ ### New Features -- add sequence scope to match features against a across a sliding window of API calls within a thread @williballenthin #2532 +- add span-of-calls scope to match features against a across a sliding window of API calls within a thread @williballenthin #2532 ### Breaking Changes -- add sequence scope to rule format +- add span-of-calls scope to rule format - capabilities functions return dataclasses instead of tuples ### New Rules (0) diff --git a/capa/capabilities/dynamic.py b/capa/capabilities/dynamic.py index c261c1e5de..10628504c4 100644 --- a/capa/capabilities/dynamic.py +++ b/capa/capabilities/dynamic.py @@ -32,11 +32,11 @@ logger = logging.getLogger(__name__) -# The number of calls that make up a sequence. +# 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. -SEQUENCE_SIZE = 20 +SPAN_SIZE = 20 @dataclass @@ -45,8 +45,8 @@ class CallCapabilities: matches: MatchResults -# The number of calls that make up a sequence. -SEQUENCE_SIZE = 5 +# The number of calls that make up a span. +SPAN_SIZE = 5 def find_call_capabilities( @@ -78,44 +78,44 @@ def find_call_capabilities( class ThreadCapabilities: features: FeatureSet thread_matches: MatchResults - sequence_matches: MatchResults + span_matches: MatchResults call_matches: MatchResults -class SequenceMatcher: +class SpanOfCallsMatcher: def __init__(self, ruleset: RuleSet): super().__init__() self.ruleset = ruleset - # matches found at the sequence scope. + # matches found at the span scope. self.matches: MatchResults = collections.defaultdict(list) - # We matches sequences as the sliding window of calls with size SEQUENCE_SIZE. + # We matches spans as the sliding window of calls with size SPAN_SIZE. # - # For each call, we consider the window of SEQUENCE_SIZE calls leading up to it, + # 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 SEQUENCE_SIZE, the oldest items are removed. - # 2. a live set of features seen in the sequence. + # 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 SEQUENCE_SIZE. - # The naive algorithm, of merging all the trailing feature sets at each call, is dependent upon SEQUENCE_SIZE - # (that is, runtime gets slower the larger SEQUENCE_SIZE is). - self.current_feature_sets: collections.deque[FeatureSet] = collections.deque(maxlen=SEQUENCE_SIZE) + # 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 sequence, + # the names of rules matched at the last span, # so that we can deduplicate long strings of the same matches. - self.last_sequence_matches: set[str] = set() + 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) == SEQUENCE_SIZE: + if len(self.current_feature_sets) == SPAN_SIZE: overflowing_feature_set = self.current_feature_sets.popleft() for feature, vas in overflowing_feature_set.items(): @@ -135,20 +135,20 @@ def next(self, ch: CallHandle, call_features: FeatureSet): for feature, vas in call_features.items(): self.current_features[feature] |= vas - _, matches = self.ruleset.match(Scope.SEQUENCE, self.current_features, ch.address) + _, matches = self.ruleset.match(Scope.SPAN_OF_CALLS, self.current_features, ch.address) - newly_encountered_rules = set(matches.keys()) - self.last_sequence_matches + newly_encountered_rules = set(matches.keys()) - self.last_span_matches - # don't emit match results for rules seen during the immediately preceeding sequence. + # 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 sequences + # 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_sequence_matches) + 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. @@ -161,7 +161,7 @@ def next(self, ch: CallHandle, call_features: FeatureSet): continue self.matches[rule_name].extend(res) - self.last_sequence_matches = set(matches.keys()) + self.last_span_matches = set(matches.keys()) def find_thread_capabilities( @@ -169,7 +169,7 @@ def find_thread_capabilities( ) -> ThreadCapabilities: """ find matches for the given rules within the given thread, - which includes matches for all the sequences and calls within it. + which includes matches for all the spans and calls within it. """ # all features found within this thread, # includes features found within calls. @@ -179,7 +179,7 @@ def find_thread_capabilities( # might be found at different calls, that's ok. call_matches: MatchResults = collections.defaultdict(list) - sequence_matcher = SequenceMatcher(ruleset) + span_matcher = SpanOfCallsMatcher(ruleset) call_count = 0 for call_count, ch in enumerate(extractor.get_calls(ph, th)): # noqa: B007 @@ -190,7 +190,7 @@ def find_thread_capabilities( for rule_name, res in call_capabilities.matches.items(): call_matches[rule_name].extend(res) - sequence_matcher.next(ch, call_capabilities.features) + 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) @@ -209,16 +209,16 @@ def find_thread_capabilities( th.address.tid, call_count, len(features), - len(matches) + len(sequence_matcher.matches) + len(call_matches), + len(matches) + len(span_matcher.matches) + len(call_matches), ) - return ThreadCapabilities(features, matches, sequence_matcher.matches, call_matches) + return ThreadCapabilities(features, matches, span_matcher.matches, call_matches) @dataclass class ProcessCapabilities: process_matches: MatchResults thread_matches: MatchResults - sequence_matches: MatchResults + span_matches: MatchResults call_matches: MatchResults feature_count: int @@ -237,9 +237,9 @@ def find_process_capabilities( # might be found at different threads, that's ok. thread_matches: MatchResults = collections.defaultdict(list) - # matches found at the sequence scope. - # might be found at different sequences, that's ok. - sequence_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. @@ -253,8 +253,8 @@ def find_process_capabilities( for rule_name, res in thread_capabilities.thread_matches.items(): thread_matches[rule_name].extend(res) - for rule_name, res in thread_capabilities.sequence_matches.items(): - sequence_matches[rule_name].extend(res) + 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) @@ -270,7 +270,7 @@ def find_process_capabilities( len(process_features), len(process_matches), ) - return ProcessCapabilities(process_matches, thread_matches, sequence_matches, call_matches, len(process_features)) + return ProcessCapabilities(process_matches, thread_matches, span_matches, call_matches, len(process_features)) def find_dynamic_capabilities( @@ -278,7 +278,7 @@ def find_dynamic_capabilities( ) -> Capabilities: all_process_matches: MatchResults = collections.defaultdict(list) all_thread_matches: MatchResults = collections.defaultdict(list) - all_sequence_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=()) @@ -303,8 +303,8 @@ def find_dynamic_capabilities( all_process_matches[rule_name].extend(res) for rule_name, res in process_capabilities.thread_matches.items(): all_thread_matches[rule_name].extend(res) - for rule_name, res in process_capabilities.sequence_matches.items(): - all_sequence_matches[rule_name].extend(res) + 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) @@ -314,7 +314,7 @@ 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_sequence_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] @@ -329,7 +329,7 @@ def find_dynamic_capabilities( # so there won't be any overlap among these following MatchResults, # and we can merge the dictionaries naively. all_call_matches.items(), - all_sequence_matches.items(), + all_span_matches.items(), all_thread_matches.items(), all_process_matches.items(), all_file_capabilities.matches.items(), diff --git a/capa/render/proto/__init__.py b/capa/render/proto/__init__.py index 2541c944cb..31b272e525 100644 --- a/capa/render/proto/__init__.py +++ b/capa/render/proto/__init__.py @@ -163,8 +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.SEQUENCE: - return capa_pb2.Scope.SCOPE_SEQUENCE + 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: @@ -657,8 +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_SEQUENCE: - return capa.rules.Scope.SEQUENCE + 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 e824c42e6e..b072347482 100644 --- a/capa/render/proto/capa.proto +++ b/capa/render/proto/capa.proto @@ -378,7 +378,7 @@ enum Scope { SCOPE_PROCESS = 5; SCOPE_THREAD = 6; SCOPE_CALL = 7; - SCOPE_SEQUENCE = 8; + SCOPE_SPAN_OF_CALLS = 8; } message Scopes { diff --git a/capa/render/proto/capa_pb2.py b/capa/render/proto/capa_pb2.py index e695a27a70..b581c9ea79 100644 --- a/capa/render/proto/capa_pb2.py +++ b/capa/render/proto/capa_pb2.py @@ -24,7 +24,7 @@ -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*\xb9\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\x12\n\x0eSCOPE_SEQUENCE\x10\x08\x62\x06proto3') +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') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -44,7 +44,7 @@ _globals['_FLAVOR']._serialized_start=9338 _globals['_FLAVOR']._serialized_end=9409 _globals['_SCOPE']._serialized_start=9412 - _globals['_SCOPE']._serialized_end=9597 + _globals['_SCOPE']._serialized_end=9602 _globals['_APIFEATURE']._serialized_start=47 _globals['_APIFEATURE']._serialized_end=128 _globals['_ADDRESS']._serialized_start=131 diff --git a/capa/render/proto/capa_pb2.pyi b/capa/render/proto/capa_pb2.pyi index 06f846170d..98420a2929 100644 --- a/capa/render/proto/capa_pb2.pyi +++ b/capa/render/proto/capa_pb2.pyi @@ -94,7 +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_SEQUENCE: _Scope.ValueType # 8 + SCOPE_SPAN_OF_CALLS: _Scope.ValueType # 8 class Scope(_Scope, metaclass=_ScopeEnumTypeWrapper): ... @@ -106,7 +106,7 @@ SCOPE_INSTRUCTION: Scope.ValueType # 4 SCOPE_PROCESS: Scope.ValueType # 5 SCOPE_THREAD: Scope.ValueType # 6 SCOPE_CALL: Scope.ValueType # 7 -SCOPE_SEQUENCE: Scope.ValueType # 8 +SCOPE_SPAN_OF_CALLS: Scope.ValueType # 8 global___Scope = Scope @typing.final diff --git a/capa/render/result_document.py b/capa/render/result_document.py index d1bce9add7..2acfa8cf3f 100644 --- a/capa/render/result_document.py +++ b/capa/render/result_document.py @@ -399,12 +399,12 @@ def from_capa( 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 "sequence" + # we'd like to assert the scope of the current rule is span-of-calls # but we don't have that data here. else: - # Sequence scopes can match each other, but they don't strictly contain each other, + # 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 sequence for another sequence, we need to look + # 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: diff --git a/capa/render/verbose.py b/capa/render/verbose.py index a89db5039e..f33ea7304a 100644 --- a/capa/render/verbose.py +++ b/capa/render/verbose.py @@ -126,7 +126,7 @@ def render_thread(layout: rd.DynamicLayout, addr: frz.Address) -> str: return f"{name}{{pid:{thread.process.pid},tid:{thread.tid}}}" -def render_sequence(layout: rd.DynamicLayout, addrs: list[frz.Address]) -> str: +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 for call in calls: assert isinstance(call, capa.features.address.DynamicCallAddress) @@ -328,7 +328,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 in (capa.rules.Scope.CALL, capa.rules.Scope.SEQUENCE): + 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 ad8ff49646..6779200ffa 100644 --- a/capa/render/vverbose.py +++ b/capa/render/vverbose.py @@ -231,7 +231,7 @@ def render_feature( # of the output, so don't re-render it again for each feature. pass elif isinstance(layout, rd.DynamicLayout) and isinstance(feature, frzf.MatchFeature): - # don't render copies of the sequence address for submatches + # don't render copies of the span of calls address for submatches pass else: render_locations(console, layout, match.locations, indent) @@ -312,13 +312,13 @@ def render_match( render_match(console, layout, rule, child, indent=indent + 1, mode=child_mode) -def collect_sequence_locations( +def collect_span_of_calls_locations( match: rd.Match, mode=MODE_SUCCESS, ): """ - Find all the (call, sequence) locations used in a given sequence match, recursively. - Useful to collect the events used to match a sequence scoped rule. + 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 isinstance(match.node, rd.StatementNode): if ( @@ -327,7 +327,7 @@ def collect_sequence_locations( ): child_mode = MODE_FAILURE if mode == MODE_SUCCESS else MODE_SUCCESS for child in match.children: - yield from collect_sequence_locations(child, child_mode) + yield from collect_span_of_calls_locations(child, child_mode) elif isinstance(match.node.statement, rd.RangeStatement): for location in match.locations: if location.type not in (frz.AddressType.CALL,): @@ -337,7 +337,7 @@ def collect_sequence_locations( yield location else: for child in match.children: - yield from collect_sequence_locations(child, mode) + yield from collect_span_of_calls_locations(child, mode) elif isinstance(match.node, rd.FeatureNode): for location in match.locations: if location.type not in (frz.AddressType.CALL,): @@ -488,9 +488,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.SEQUENCE: - calls = sorted(set(collect_sequence_locations(match))) - console.write(hanging_indent(v.render_sequence(doc.meta.analysis.layout, calls), indent=1)) + 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 907ae9be4c..be3b6ce10d 100644 --- a/capa/rules/__init__.py +++ b/capa/rules/__init__.py @@ -86,7 +86,7 @@ class Scope(str, Enum): FILE = "file" PROCESS = "process" THREAD = "thread" - SEQUENCE = "sequence" + SPAN_OF_CALLS = "span of calls" CALL = "call" FUNCTION = "function" BASIC_BLOCK = "basic block" @@ -115,7 +115,7 @@ def to_yaml(cls, representer, node): Scope.GLOBAL, Scope.PROCESS, Scope.THREAD, - Scope.SEQUENCE, + Scope.SPAN_OF_CALLS, Scope.CALL, } @@ -201,7 +201,7 @@ def from_dict(self, scopes: dict[str, str]) -> "Scopes": capa.features.common.MatchedRule, }, Scope.THREAD: set(), - Scope.SEQUENCE: set(), + Scope.SPAN_OF_CALLS: set(), Scope.CALL: { capa.features.common.MatchedRule, capa.features.common.Regex, @@ -256,14 +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.SEQUENCE].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 sequence features -SUPPORTED_FEATURES[Scope.SEQUENCE].update(SUPPORTED_FEATURES[Scope.CALL]) -# all sequence scope features (and therefore, call features) are also thread features -SUPPORTED_FEATURES[Scope.THREAD].update(SUPPORTED_FEATURES[Scope.SEQUENCE]) +# 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]) @@ -622,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") @@ -633,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") @@ -642,20 +642,20 @@ def build_statements(d, scopes: Scopes): Scope.THREAD, build_statements(d[key][0], Scopes(dynamic=Scope.THREAD)), description=description ) - elif key == "sequence": - if all(s not in scopes for s in (Scope.FILE, Scope.PROCESS, Scope.THREAD, Scope.SEQUENCE)): - raise InvalidRule("sequence subscope supported only for the process and thread scopes") + 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.SEQUENCE, build_statements(d[key][0], Scopes(dynamic=Scope.SEQUENCE)), description=description + 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.SEQUENCE, 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") @@ -666,7 +666,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") @@ -677,7 +677,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") @@ -688,7 +688,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)) @@ -1401,7 +1401,7 @@ def __init__( scopes = ( Scope.CALL, - Scope.SEQUENCE, + Scope.SPAN_OF_CALLS, Scope.THREAD, Scope.PROCESS, Scope.INSTRUCTION, @@ -1433,8 +1433,8 @@ def thread_rules(self): return self.rules_by_scope[Scope.THREAD] @property - def sequence_rules(self): - return self.rules_by_scope[Scope.SEQUENCE] + def span_of_calls_rules(self): + return self.rules_by_scope[Scope.SPAN_OF_CALLS] @property def call_rules(self): diff --git a/scripts/lint.py b/scripts/lint.py index 7bf432e084..4c4e093223 100644 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -194,7 +194,7 @@ def check_rule(self, ctx: Context, rule: Rule): "file", "process", "thread", - "sequence", + "span of calls", "call", "unsupported", ) diff --git a/tests/test_dynamic_sequence_scope.py b/tests/test_dynamic_span_of_calls_scope.py similarity index 86% rename from tests/test_dynamic_sequence_scope.py rename to tests/test_dynamic_span_of_calls_scope.py index 98b3eaf4eb..5f1a19a706 100644 --- a/tests/test_dynamic_sequence_scope.py +++ b/tests/test_dynamic_span_of_calls_scope.py @@ -99,12 +99,12 @@ def test_dynamic_call_scope(): r = capa.rules.Rule.from_yaml(rule) ruleset = capa.rules.RuleSet([r]) - matches, features = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) - assert r.name in matches - assert 8 in get_call_ids(matches[r.name]) + 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 sequence. +# match the first span. # # proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) # thread: 3064 @@ -113,7 +113,7 @@ def test_dynamic_call_scope(): # call 10: LdrGetDllHandle(1974337536, kernel32.dll) # call 11: LdrGetProcedureAddress(2010595649, 0, AddVectoredExceptionHandler, 1974337536, kernel32.dll) # call 12: LdrGetDllHandle(1974337536, kernel32.dll) -def test_dynamic_sequence_scope(): +def test_dynamic_span_scope(): extractor = get_0000a657_thread3064() rule = textwrap.dedent( @@ -123,7 +123,7 @@ def test_dynamic_sequence_scope(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - api: GetSystemTimeAsFileTime @@ -137,12 +137,12 @@ def test_dynamic_sequence_scope(): r = capa.rules.Rule.from_yaml(rule) ruleset = capa.rules.RuleSet([r]) - matches, features = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) - assert r.name in matches - assert 12 in get_call_ids(matches[r.name]) + 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 sequence is only 5 calls long (for example), it doesn't match beyond that 5-tuple. +# 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 @@ -155,7 +155,7 @@ def test_dynamic_sequence_scope(): # call 14: RtlAddVectoredExceptionHandler(1921490089, 0) # call 15: GetSystemTime() # call 16: NtAllocateVirtualMemory(no, 4, 786432, 4784128, 4294967295) -def test_dynamic_sequence_scope_length(): +def test_dynamic_span_scope_length(): extractor = get_0000a657_thread3064() rule = textwrap.dedent( @@ -165,7 +165,7 @@ def test_dynamic_sequence_scope_length(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - api: GetSystemTimeAsFileTime @@ -176,24 +176,24 @@ def test_dynamic_sequence_scope_length(): r = capa.rules.Rule.from_yaml(rule) ruleset = capa.rules.RuleSet([r]) - # patch SEQUENCE_SIZE since we may use a much larger value in the real world. + # 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, "SEQUENCE_SIZE", 5) + 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 sequence rules. +# 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_sequence_call_subscope(): +def test_dynamic_span_call_subscope(): extractor = get_0000a657_thread3064() rule = textwrap.dedent( @@ -203,7 +203,7 @@ def test_dynamic_sequence_call_subscope(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - call: @@ -221,7 +221,7 @@ def test_dynamic_sequence_call_subscope(): assert 11 in get_call_ids(capabilities.matches[r.name]) -# show that you can use a sequence subscope in sequence rules. +# show that you can use a span subscope in span rules. # # proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) # thread: 3064 @@ -231,7 +231,7 @@ def test_dynamic_sequence_call_subscope(): # call 12: LdrGetDllHandle(1974337536, kernel32.dll) # call 13: LdrGetProcedureAddress(2010595072, 0, RemoveVectoredExceptionHandler, 1974337536, kernel32.dll) # ... -def test_dynamic_sequence_scope_sequence_subscope(): +def test_dynamic_span_scope_span_subscope(): extractor = get_0000a657_thread3064() rule = textwrap.dedent( @@ -241,16 +241,16 @@ def test_dynamic_sequence_scope_sequence_subscope(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - - sequence: + - span of calls: - description: resolve add VEH # should match at 11 - and: - api: LdrGetDllHandle - api: LdrGetProcedureAddress - string: AddVectoredExceptionHandler - - sequence: + - span of calls: - description: resolve remove VEH # should match at 13 - and: - api: LdrGetDllHandle @@ -267,8 +267,8 @@ def test_dynamic_sequence_scope_sequence_subscope(): assert 13 in get_call_ids(capabilities.matches[r.name]) -# show that you can't use thread subscope in sequence rules. -def test_dynamic_sequence_scope_thread_subscope(): +# show that you can't use thread subscope in span rules. +def test_dynamic_span_scope_thread_subscope(): rule = textwrap.dedent( """ rule: @@ -276,7 +276,7 @@ def test_dynamic_sequence_scope_thread_subscope(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - thread: @@ -288,7 +288,7 @@ def test_dynamic_sequence_scope_thread_subscope(): capa.rules.Rule.from_yaml(rule) -# show how you might use a sequence rule: to match a small window for a collection of features. +# 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 @@ -297,7 +297,7 @@ def test_dynamic_sequence_scope_thread_subscope(): # call 12: ... # call 13: ... # call 14: RtlAddVectoredExceptionHandler(1921490089, 0) -def test_dynamic_sequence_example(): +def test_dynamic_span_example(): extractor = get_0000a657_thread3064() rule = textwrap.dedent( @@ -307,7 +307,7 @@ def test_dynamic_sequence_example(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - call: @@ -325,12 +325,12 @@ def test_dynamic_sequence_example(): r = capa.rules.Rule.from_yaml(rule) ruleset = capa.rules.RuleSet([r]) - matches, features = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) - assert r.name in matches - assert 14 in get_call_ids(matches[r.name]) + 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 sequences that overlap a single event are handled. +# show how spans that overlap a single event are handled. # # proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) # thread: 3064 @@ -342,7 +342,7 @@ def test_dynamic_sequence_example(): # call 14: ... # call 15: ... # ... -def test_dynamic_sequence_multiple_sequences_overlapping_single_event(): +def test_dynamic_span_multiple_spans_overlapping_single_event(): extractor = get_0000a657_thread3064() rule = textwrap.dedent( @@ -352,7 +352,7 @@ def test_dynamic_sequence_multiple_sequences_overlapping_single_event(): name: test rule scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - call: @@ -367,11 +367,11 @@ def test_dynamic_sequence_multiple_sequences_overlapping_single_event(): capabilities = capa.capabilities.dynamic.find_dynamic_capabilities(ruleset, extractor, disable_progress=True) assert r.name in capabilities.matches - # we only match the first overlapping sequence + # 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 sequence rules. +# show that you can use match statements in span-of-calls rules. # # proc: 0000A65749F5902C4D82.exe (ppid=2456, pid=3052) # thread: 3064 @@ -381,7 +381,7 @@ def test_dynamic_sequence_multiple_sequences_overlapping_single_event(): # call 12: LdrGetDllHandle(1974337536, kernel32.dll) # call 13: LdrGetProcedureAddress(2010595072, 0, RemoveVectoredExceptionHandler, 1974337536, kernel32.dll) # ... -def test_dynamic_sequence_scope_match_statements(): +def test_dynamic_span_scope_match_statements(): extractor = get_0000a657_thread3064() ruleset = capa.rules.RuleSet( @@ -395,7 +395,7 @@ def test_dynamic_sequence_scope_match_statements(): namespace: linking/runtime-linking/veh scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - api: LdrGetDllHandle @@ -413,7 +413,7 @@ def test_dynamic_sequence_scope_match_statements(): namespace: linking/runtime-linking/veh scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - api: LdrGetDllHandle @@ -430,7 +430,7 @@ def test_dynamic_sequence_scope_match_statements(): name: resolve add and remove VEH scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - match: resolve add VEH @@ -446,7 +446,7 @@ def test_dynamic_sequence_scope_match_statements(): name: has VEH runtime linking scopes: static: unsupported - dynamic: sequence + dynamic: span of calls features: - and: - match: linking/runtime-linking/veh diff --git a/tests/test_proto.py b/tests/test_proto.py index 474efc0172..78f24abeea 100644 --- a/tests/test_proto.py +++ b/tests/test_proto.py @@ -129,7 +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.SEQUENCE) == capa_pb2.SCOPE_SEQUENCE + assert capa.render.proto.scope_to_pb2(capa.rules.Scope.SPAN_OF_CALLS) == capa_pb2.SCOPE_SEQUENCE assert capa.render.proto.scope_to_pb2(capa.rules.Scope.CALL) == capa_pb2.SCOPE_CALL