mirror of
https://github.com/mandiant/capa.git
synced 2025-12-23 07:28:34 -08:00
Remove True from Characteristic rules and output
Get rid of `True` in characteristic (rules, output and json) as it is implicit. This way, the same syntax is used for characteristic as for the rest of the features. Co-authored-by: William Ballenthin <william.ballenthin@fireeye.com>
This commit is contained in:
@@ -66,26 +66,22 @@ class MatchedRule(Feature):
|
|||||||
|
|
||||||
|
|
||||||
class Characteristic(Feature):
|
class Characteristic(Feature):
|
||||||
def __init__(self, attribute, value=None, description=None):
|
def __init__(self, value, description=None):
|
||||||
'''
|
super(Characteristic, self).__init__([value], description)
|
||||||
when `value` is not provided, this serves as descriptor for a class of characteristics.
|
|
||||||
this is only used internally, such as in `rules.py` when checking if a statement is
|
|
||||||
supported by a given scope.
|
|
||||||
'''
|
|
||||||
super(Characteristic, self).__init__([attribute, value], description)
|
|
||||||
self.attribute = attribute
|
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def evaluate(self, ctx):
|
def freeze_serialize(self):
|
||||||
if self.value is None:
|
# in an older version of capa, characteristics could theoretically match non-existence (value=False).
|
||||||
raise ValueError('cannot evaluate characteristc %s with empty value' % (str(self)))
|
# but we found this was never used (and better expressed with `not: characteristic: ...`).
|
||||||
return super(Characteristic, self).evaluate(ctx)
|
# this was represented using an additional parameter for Characteristic.
|
||||||
|
# its been removed, but we keep it around in the freeze format to maintain backwards compatibility.
|
||||||
|
# this value is ignored, however.
|
||||||
|
return (self.__class__.__name__, [self.value, True])
|
||||||
|
|
||||||
def get_args_str(self):
|
@classmethod
|
||||||
if self.value is None:
|
def freeze_deserialize(cls, args):
|
||||||
return self.attribute
|
# see above. we ignore the second element in the 2-tuple here.
|
||||||
else:
|
return cls(args[0])
|
||||||
return '%s(%s)' % (self.attribute, self.value)
|
|
||||||
|
|
||||||
|
|
||||||
class String(Feature):
|
class String(Feature):
|
||||||
|
|||||||
@@ -184,22 +184,22 @@ class NullFeatureExtractor(FeatureExtractor):
|
|||||||
|
|
||||||
extractor = NullFeatureExtractor({
|
extractor = NullFeatureExtractor({
|
||||||
'file features': [
|
'file features': [
|
||||||
(0x402345, capa.features.Characteristic('embedded pe', True)),
|
(0x402345, capa.features.Characteristic('embedded pe')),
|
||||||
],
|
],
|
||||||
'functions': {
|
'functions': {
|
||||||
0x401000: {
|
0x401000: {
|
||||||
'features': [
|
'features': [
|
||||||
(0x401000, capa.features.Characteristic('switch', True)),
|
(0x401000, capa.features.Characteristic('switch')),
|
||||||
],
|
],
|
||||||
'basic blocks': {
|
'basic blocks': {
|
||||||
0x401000: {
|
0x401000: {
|
||||||
'features': [
|
'features': [
|
||||||
(0x401000, capa.features.Characteristic('tight-loop', True)),
|
(0x401000, capa.features.Characteristic('tight-loop')),
|
||||||
],
|
],
|
||||||
'instructions': {
|
'instructions': {
|
||||||
0x401000: {
|
0x401000: {
|
||||||
'features': [
|
'features': [
|
||||||
(0x401000, capa.features.Characteristic('nzxor', True)),
|
(0x401000, capa.features.Characteristic('nzxor')),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
0x401002: ...
|
0x401002: ...
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ def extract_bb_stackstring(f, bb):
|
|||||||
bb (IDA BasicBlock)
|
bb (IDA BasicBlock)
|
||||||
'''
|
'''
|
||||||
if _ida_bb_contains_stackstring(f, bb):
|
if _ida_bb_contains_stackstring(f, bb):
|
||||||
yield Characteristic('stack string', True), bb.start_ea
|
yield Characteristic('stack string'), bb.start_ea
|
||||||
|
|
||||||
|
|
||||||
def _ida_bb_contains_tight_loop(f, bb):
|
def _ida_bb_contains_tight_loop(f, bb):
|
||||||
@@ -133,7 +133,7 @@ def extract_bb_tight_loop(f, bb):
|
|||||||
bb (IDA BasicBlock)
|
bb (IDA BasicBlock)
|
||||||
'''
|
'''
|
||||||
if _ida_bb_contains_tight_loop(f, bb):
|
if _ida_bb_contains_tight_loop(f, bb):
|
||||||
yield Characteristic('tight loop', True), bb.start_ea
|
yield Characteristic('tight loop'), bb.start_ea
|
||||||
|
|
||||||
|
|
||||||
def extract_features(f, bb):
|
def extract_features(f, bb):
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ def extract_file_embedded_pe():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for ea, _ in _ida_check_segment_for_pe(seg):
|
for ea, _ in _ida_check_segment_for_pe(seg):
|
||||||
yield Characteristic('embedded pe', True), ea
|
yield Characteristic('embedded pe'), ea
|
||||||
|
|
||||||
|
|
||||||
def extract_file_export_names():
|
def extract_file_export_names():
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ def extract_function_switch(f):
|
|||||||
f (IDA func_t)
|
f (IDA func_t)
|
||||||
'''
|
'''
|
||||||
if _ida_function_contains_switch(f):
|
if _ida_function_contains_switch(f):
|
||||||
yield Characteristic('switch', True), f.start_ea
|
yield Characteristic('switch'), f.start_ea
|
||||||
|
|
||||||
|
|
||||||
def extract_function_calls_to(f):
|
def extract_function_calls_to(f):
|
||||||
@@ -39,7 +39,7 @@ def extract_function_calls_to(f):
|
|||||||
f (IDA func_t)
|
f (IDA func_t)
|
||||||
'''
|
'''
|
||||||
for ea in idautils.CodeRefsTo(f.start_ea, True):
|
for ea in idautils.CodeRefsTo(f.start_ea, True):
|
||||||
yield Characteristic('calls to', True), ea
|
yield Characteristic('calls to'), ea
|
||||||
|
|
||||||
|
|
||||||
def extract_function_loop(f):
|
def extract_function_loop(f):
|
||||||
@@ -53,7 +53,7 @@ def extract_function_loop(f):
|
|||||||
map(lambda s: edges.append((bb.start_ea, s.start_ea)), bb.succs())
|
map(lambda s: edges.append((bb.start_ea, s.start_ea)), bb.succs())
|
||||||
|
|
||||||
if edges and loops.has_loop(edges):
|
if edges and loops.has_loop(edges):
|
||||||
yield Characteristic('loop', True), f.start_ea
|
yield Characteristic('loop'), f.start_ea
|
||||||
|
|
||||||
|
|
||||||
def extract_recursive_call(f):
|
def extract_recursive_call(f):
|
||||||
@@ -64,7 +64,7 @@ def extract_recursive_call(f):
|
|||||||
'''
|
'''
|
||||||
for ref in idautils.CodeRefsTo(f.start_ea, True):
|
for ref in idautils.CodeRefsTo(f.start_ea, True):
|
||||||
if f.contains(ref):
|
if f.contains(ref):
|
||||||
yield Characteristic('recursive call', True), f.start_ea
|
yield Characteristic('recursive call'), f.start_ea
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ def extract_insn_nzxor_characteristic_features(f, bb, insn):
|
|||||||
if _is_nzxor_stack_cookie(f, bb, insn):
|
if _is_nzxor_stack_cookie(f, bb, insn):
|
||||||
return
|
return
|
||||||
|
|
||||||
yield Characteristic('nzxor', True), insn.ea
|
yield Characteristic('nzxor'), insn.ea
|
||||||
|
|
||||||
|
|
||||||
def extract_insn_mnemonic_features(f, bb, insn):
|
def extract_insn_mnemonic_features(f, bb, insn):
|
||||||
@@ -292,7 +292,7 @@ def extract_insn_peb_access_characteristic_features(f, bb, insn):
|
|||||||
|
|
||||||
if ' fs:30h' in disasm or ' gs:60h' in disasm:
|
if ' fs:30h' in disasm or ' gs:60h' in disasm:
|
||||||
# TODO: replace above with proper IDA
|
# TODO: replace above with proper IDA
|
||||||
yield Characteristic('peb access', True), insn.ea
|
yield Characteristic('peb access'), insn.ea
|
||||||
|
|
||||||
|
|
||||||
def extract_insn_segment_access_features(f, bb, insn):
|
def extract_insn_segment_access_features(f, bb, insn):
|
||||||
@@ -309,11 +309,11 @@ def extract_insn_segment_access_features(f, bb, insn):
|
|||||||
|
|
||||||
if ' fs:' in disasm:
|
if ' fs:' in disasm:
|
||||||
# TODO: replace above with proper IDA
|
# TODO: replace above with proper IDA
|
||||||
yield Characteristic('fs access', True), insn.ea
|
yield Characteristic('fs access'), insn.ea
|
||||||
|
|
||||||
if ' gs:' in disasm:
|
if ' gs:' in disasm:
|
||||||
# TODO: replace above with proper IDA
|
# TODO: replace above with proper IDA
|
||||||
yield Characteristic('gs access', True), insn.ea
|
yield Characteristic('gs access'), insn.ea
|
||||||
|
|
||||||
|
|
||||||
def extract_insn_cross_section_cflow(f, bb, insn):
|
def extract_insn_cross_section_cflow(f, bb, insn):
|
||||||
@@ -336,7 +336,7 @@ def extract_insn_cross_section_cflow(f, bb, insn):
|
|||||||
if idaapi.getseg(ref) == idaapi.getseg(insn.ea):
|
if idaapi.getseg(ref) == idaapi.getseg(insn.ea):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
yield Characteristic('cross section flow', True), insn.ea
|
yield Characteristic('cross section flow'), insn.ea
|
||||||
|
|
||||||
|
|
||||||
def extract_function_calls_from(f, bb, insn):
|
def extract_function_calls_from(f, bb, insn):
|
||||||
@@ -354,7 +354,7 @@ def extract_function_calls_from(f, bb, insn):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for ref in idautils.CodeRefsFrom(insn.ea, False):
|
for ref in idautils.CodeRefsFrom(insn.ea, False):
|
||||||
yield Characteristic('calls from', True), ref
|
yield Characteristic('calls from'), ref
|
||||||
|
|
||||||
|
|
||||||
def extract_function_indirect_call_characteristic_features(f, bb, insn):
|
def extract_function_indirect_call_characteristic_features(f, bb, insn):
|
||||||
@@ -373,7 +373,7 @@ def extract_function_indirect_call_characteristic_features(f, bb, insn):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if idc.get_operand_type(insn.ea, 0) in (idc.o_reg, idc.o_phrase, idc.o_displ):
|
if idc.get_operand_type(insn.ea, 0) in (idc.o_reg, idc.o_phrase, idc.o_displ):
|
||||||
yield Characteristic('indirect call', True), insn.ea
|
yield Characteristic('indirect call'), insn.ea
|
||||||
|
|
||||||
|
|
||||||
def extract_features(f, bb, insn):
|
def extract_features(f, bb, insn):
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def _bb_has_tight_loop(f, bb):
|
|||||||
def extract_bb_tight_loop(f, bb):
|
def extract_bb_tight_loop(f, bb):
|
||||||
''' check basic block for tight loop indicators '''
|
''' check basic block for tight loop indicators '''
|
||||||
if _bb_has_tight_loop(f, bb):
|
if _bb_has_tight_loop(f, bb):
|
||||||
yield Characteristic('tight loop', True), bb.va
|
yield Characteristic('tight loop'), bb.va
|
||||||
|
|
||||||
|
|
||||||
def _bb_has_stackstring(f, bb):
|
def _bb_has_stackstring(f, bb):
|
||||||
@@ -62,7 +62,7 @@ def _bb_has_stackstring(f, bb):
|
|||||||
def extract_stackstring(f, bb):
|
def extract_stackstring(f, bb):
|
||||||
''' check basic block for stackstring indicators '''
|
''' check basic block for stackstring indicators '''
|
||||||
if _bb_has_stackstring(f, bb):
|
if _bb_has_stackstring(f, bb):
|
||||||
yield Characteristic('stack string', True), bb.va
|
yield Characteristic('stack string'), bb.va
|
||||||
|
|
||||||
|
|
||||||
def is_mov_imm_to_stack(instr):
|
def is_mov_imm_to_stack(instr):
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ def extract_file_embedded_pe(vw, file_path):
|
|||||||
fbytes = f.read()
|
fbytes = f.read()
|
||||||
|
|
||||||
for offset, i in pe_carve.carve(fbytes, 1):
|
for offset, i in pe_carve.carve(fbytes, 1):
|
||||||
yield Characteristic('embedded pe', True), offset
|
yield Characteristic('embedded pe'), offset
|
||||||
|
|
||||||
|
|
||||||
def extract_file_export_names(vw, file_path):
|
def extract_file_export_names(vw, file_path):
|
||||||
|
|||||||
@@ -53,12 +53,12 @@ def extract_function_switch(f):
|
|||||||
method can be optimized
|
method can be optimized
|
||||||
'''
|
'''
|
||||||
if f.va in get_functions_with_switch(f.vw):
|
if f.va in get_functions_with_switch(f.vw):
|
||||||
yield Characteristic('switch', True), f.va
|
yield Characteristic('switch'), f.va
|
||||||
|
|
||||||
|
|
||||||
def extract_function_calls_to(f):
|
def extract_function_calls_to(f):
|
||||||
for src, _, _, _ in f.vw.getXrefsTo(f.va, rtype=vivisect.const.REF_CODE):
|
for src, _, _, _ in f.vw.getXrefsTo(f.va, rtype=vivisect.const.REF_CODE):
|
||||||
yield Characteristic('calls to', True), src
|
yield Characteristic('calls to'), src
|
||||||
|
|
||||||
|
|
||||||
def extract_function_loop(f):
|
def extract_function_loop(f):
|
||||||
@@ -74,7 +74,7 @@ def extract_function_loop(f):
|
|||||||
edges.append((bb.va, bva))
|
edges.append((bb.va, bva))
|
||||||
|
|
||||||
if edges and loops.has_loop(edges):
|
if edges and loops.has_loop(edges):
|
||||||
yield Characteristic('loop', True), f.va
|
yield Characteristic('loop'), f.va
|
||||||
|
|
||||||
|
|
||||||
def extract_features(f):
|
def extract_features(f):
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ def extract_insn_nzxor_characteristic_features(f, bb, insn):
|
|||||||
if is_security_cookie(f, bb, insn):
|
if is_security_cookie(f, bb, insn):
|
||||||
return
|
return
|
||||||
|
|
||||||
yield Characteristic('nzxor', True), insn.va
|
yield Characteristic('nzxor'), insn.va
|
||||||
|
|
||||||
|
|
||||||
def extract_insn_mnemonic_features(f, bb, insn):
|
def extract_insn_mnemonic_features(f, bb, insn):
|
||||||
@@ -314,12 +314,12 @@ def extract_insn_peb_access_characteristic_features(f, bb, insn):
|
|||||||
# fs: push dword [eax + 0x30] ; i386RegMemOper, with eax = 0
|
# fs: push dword [eax + 0x30] ; i386RegMemOper, with eax = 0
|
||||||
if (isinstance(oper, envi.archs.i386.disasm.i386RegMemOper) and oper.disp == 0x30) or \
|
if (isinstance(oper, envi.archs.i386.disasm.i386RegMemOper) and oper.disp == 0x30) or \
|
||||||
(isinstance(oper, envi.archs.i386.disasm.i386ImmMemOper) and oper.imm == 0x30):
|
(isinstance(oper, envi.archs.i386.disasm.i386ImmMemOper) and oper.imm == 0x30):
|
||||||
yield Characteristic('peb access', True), insn.va
|
yield Characteristic('peb access'), insn.va
|
||||||
elif 'gs' in insn.getPrefixName():
|
elif 'gs' in insn.getPrefixName():
|
||||||
for oper in insn.opers:
|
for oper in insn.opers:
|
||||||
if (isinstance(oper, envi.archs.amd64.disasm.i386RegMemOper) and oper.disp == 0x60) or \
|
if (isinstance(oper, envi.archs.amd64.disasm.i386RegMemOper) and oper.disp == 0x60) or \
|
||||||
(isinstance(oper, envi.archs.amd64.disasm.i386ImmMemOper) and oper.imm == 0x60):
|
(isinstance(oper, envi.archs.amd64.disasm.i386ImmMemOper) and oper.imm == 0x60):
|
||||||
yield Characteristic('peb access', True), insn.va
|
yield Characteristic('peb access'), insn.va
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -329,10 +329,10 @@ def extract_insn_segment_access_features(f, bb, insn):
|
|||||||
prefix = insn.getPrefixName()
|
prefix = insn.getPrefixName()
|
||||||
|
|
||||||
if prefix == 'fs':
|
if prefix == 'fs':
|
||||||
yield Characteristic('fs access', True), insn.va
|
yield Characteristic('fs access'), insn.va
|
||||||
|
|
||||||
if prefix == 'gs':
|
if prefix == 'gs':
|
||||||
yield Characteristic('gs access', True), insn.va
|
yield Characteristic('gs access'), insn.va
|
||||||
|
|
||||||
|
|
||||||
def get_section(vw, va):
|
def get_section(vw, va):
|
||||||
@@ -369,7 +369,7 @@ def extract_insn_cross_section_cflow(f, bb, insn):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if get_section(f.vw, insn.va) != get_section(f.vw, va):
|
if get_section(f.vw, insn.va) != get_section(f.vw, va):
|
||||||
yield Characteristic('cross section flow', True), insn.va
|
yield Characteristic('cross section flow'), insn.va
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
@@ -387,7 +387,7 @@ def extract_function_calls_from(f, bb, insn):
|
|||||||
if isinstance(insn.opers[0], envi.archs.i386.disasm.i386ImmMemOper):
|
if isinstance(insn.opers[0], envi.archs.i386.disasm.i386ImmMemOper):
|
||||||
oper = insn.opers[0]
|
oper = insn.opers[0]
|
||||||
target = oper.getOperAddr(insn)
|
target = oper.getOperAddr(insn)
|
||||||
yield Characteristic('calls from', True), target
|
yield Characteristic('calls from'), target
|
||||||
|
|
||||||
# call via thunk on x86,
|
# call via thunk on x86,
|
||||||
# see 9324d1a8ae37a36ae560c37448c9705a at 0x407985
|
# see 9324d1a8ae37a36ae560c37448c9705a at 0x407985
|
||||||
@@ -396,18 +396,18 @@ def extract_function_calls_from(f, bb, insn):
|
|||||||
# see Lab21-01.exe_:0x140001178
|
# see Lab21-01.exe_:0x140001178
|
||||||
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386PcRelOper):
|
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386PcRelOper):
|
||||||
target = insn.opers[0].getOperValue(insn)
|
target = insn.opers[0].getOperValue(insn)
|
||||||
yield Characteristic('calls from', True), target
|
yield Characteristic('calls from'), target
|
||||||
|
|
||||||
# call via IAT, x64
|
# call via IAT, x64
|
||||||
elif isinstance(insn.opers[0], envi.archs.amd64.disasm.Amd64RipRelOper):
|
elif isinstance(insn.opers[0], envi.archs.amd64.disasm.Amd64RipRelOper):
|
||||||
op = insn.opers[0]
|
op = insn.opers[0]
|
||||||
target = op.getOperAddr(insn)
|
target = op.getOperAddr(insn)
|
||||||
yield Characteristic('calls from', True), target
|
yield Characteristic('calls from'), target
|
||||||
|
|
||||||
if target and target == f.va:
|
if target and target == f.va:
|
||||||
# if we found a jump target and it's the function address
|
# if we found a jump target and it's the function address
|
||||||
# mark as recursive
|
# mark as recursive
|
||||||
yield Characteristic('recursive call', True), target
|
yield Characteristic('recursive call'), target
|
||||||
|
|
||||||
|
|
||||||
# this is a feature that's most relevant at the function or basic block scope,
|
# this is a feature that's most relevant at the function or basic block scope,
|
||||||
@@ -423,13 +423,13 @@ def extract_function_indirect_call_characteristic_features(f, bb, insn):
|
|||||||
# Checks below work for x86 and x64
|
# Checks below work for x86 and x64
|
||||||
if isinstance(insn.opers[0], envi.archs.i386.disasm.i386RegOper):
|
if isinstance(insn.opers[0], envi.archs.i386.disasm.i386RegOper):
|
||||||
# call edx
|
# call edx
|
||||||
yield Characteristic('indirect call', True), insn.va
|
yield Characteristic('indirect call'), insn.va
|
||||||
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386RegMemOper):
|
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386RegMemOper):
|
||||||
# call dword ptr [eax+50h]
|
# call dword ptr [eax+50h]
|
||||||
yield Characteristic('indirect call', True), insn.va
|
yield Characteristic('indirect call'), insn.va
|
||||||
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386SibOper):
|
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386SibOper):
|
||||||
# call qword ptr [rsp+78h]
|
# call qword ptr [rsp+78h]
|
||||||
yield Characteristic('indirect call', True), insn.va
|
yield Characteristic('indirect call'), insn.va
|
||||||
|
|
||||||
|
|
||||||
def extract_features(f, bb, insn):
|
def extract_features(f, bb, insn):
|
||||||
|
|||||||
@@ -41,14 +41,12 @@ def render_statement(ostream, match, statement, indent=0):
|
|||||||
# so, we have to inline some of the feature rendering here.
|
# so, we have to inline some of the feature rendering here.
|
||||||
|
|
||||||
child = statement['child']
|
child = statement['child']
|
||||||
if child['type'] in ('string', 'api', 'mnemonic', 'basic block', 'export', 'import', 'section', 'match'):
|
if child['type'] in ('string', 'api', 'mnemonic', 'basic block', 'export', 'import', 'section', 'match', 'characteristic'):
|
||||||
feature = '%s(%s)' % (child['type'], rutils.bold2(child[child['type']]))
|
feature = '%s(%s)' % (child['type'], rutils.bold2(child[child['type']]))
|
||||||
elif child['type'] in ('number', 'offset'):
|
elif child['type'] in ('number', 'offset'):
|
||||||
feature = '%s(%s)' % (child['type'], rutils.bold2(rutils.hex(child[child['type']])))
|
feature = '%s(%s)' % (child['type'], rutils.bold2(rutils.hex(child[child['type']])))
|
||||||
elif child['type'] == 'bytes':
|
elif child['type'] == 'bytes':
|
||||||
feature = '%s(%s)' % (child['type'], rutils.bold2(rutils.hex_string(child[child['type']])))
|
feature = '%s(%s)' % (child['type'], rutils.bold2(rutils.hex_string(child[child['type']])))
|
||||||
elif child['type'] == 'characteristic':
|
|
||||||
feature = 'characteristic(%s)' % (rutils.bold2(child['characteristic']))
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('unexpected feature type: ' + str(child))
|
raise RuntimeError('unexpected feature type: ' + str(child))
|
||||||
|
|
||||||
@@ -80,7 +78,7 @@ def render_statement(ostream, match, statement, indent=0):
|
|||||||
def render_feature(ostream, match, feature, indent=0):
|
def render_feature(ostream, match, feature, indent=0):
|
||||||
ostream.write(' ' * indent)
|
ostream.write(' ' * indent)
|
||||||
|
|
||||||
if feature['type'] in ('string', 'api', 'mnemonic', 'basic block', 'export', 'import', 'section', 'match'):
|
if feature['type'] in ('string', 'api', 'mnemonic', 'basic block', 'export', 'import', 'section', 'match', 'characteristic'):
|
||||||
ostream.write(feature['type'])
|
ostream.write(feature['type'])
|
||||||
ostream.write(': ')
|
ostream.write(': ')
|
||||||
ostream.write(rutils.bold2(feature[feature['type']]))
|
ostream.write(rutils.bold2(feature[feature['type']]))
|
||||||
@@ -93,8 +91,6 @@ def render_feature(ostream, match, feature, indent=0):
|
|||||||
# bytes is the uppercase, hex-encoded string.
|
# bytes is the uppercase, hex-encoded string.
|
||||||
# it should always be an even number of characters (its hex).
|
# it should always be an even number of characters (its hex).
|
||||||
ostream.write(rutils.bold2(rutils.hex_string(feature[feature['type']])))
|
ostream.write(rutils.bold2(rutils.hex_string(feature[feature['type']])))
|
||||||
elif feature['type'] == 'characteristic':
|
|
||||||
ostream.write('characteristic(%s)' % (rutils.bold2(feature['characteristic'])))
|
|
||||||
# note that regex is found in `render_statement`
|
# note that regex is found in `render_statement`
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('unexpected feature type: ' + str(feature))
|
raise RuntimeError('unexpected feature type: ' + str(feature))
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class InvalidRuleSet(ValueError):
|
|||||||
|
|
||||||
def ensure_feature_valid_for_scope(scope, feature):
|
def ensure_feature_valid_for_scope(scope, feature):
|
||||||
if isinstance(feature, capa.features.Characteristic):
|
if isinstance(feature, capa.features.Characteristic):
|
||||||
if capa.features.Characteristic(feature.attribute) not in SUPPORTED_FEATURES[scope]:
|
if capa.features.Characteristic(feature.value) not in SUPPORTED_FEATURES[scope]:
|
||||||
raise InvalidRule('feature %s not support for scope %s' % (feature, scope))
|
raise InvalidRule('feature %s not support for scope %s' % (feature, scope))
|
||||||
elif not isinstance(feature, tuple(filter(lambda t: isinstance(t, type), SUPPORTED_FEATURES[scope]))):
|
elif not isinstance(feature, tuple(filter(lambda t: isinstance(t, type), SUPPORTED_FEATURES[scope]))):
|
||||||
raise InvalidRule('feature %s not support for scope %s' % (feature, scope))
|
raise InvalidRule('feature %s not support for scope %s' % (feature, scope))
|
||||||
@@ -205,9 +205,8 @@ def parse_feature(key):
|
|||||||
return capa.features.insn.Mnemonic
|
return capa.features.insn.Mnemonic
|
||||||
elif key == 'basic blocks':
|
elif key == 'basic blocks':
|
||||||
return capa.features.basicblock.BasicBlock
|
return capa.features.basicblock.BasicBlock
|
||||||
elif key.startswith('characteristic(') and key.endswith(')'):
|
elif key == 'characteristic':
|
||||||
characteristic = key[len('characteristic('):-len(')')]
|
return capa.features.Characteristic
|
||||||
return lambda v, description=None: capa.features.Characteristic(characteristic, v, description)
|
|
||||||
elif key == 'export':
|
elif key == 'export':
|
||||||
return capa.features.file.Export
|
return capa.features.file.Export
|
||||||
elif key == 'import':
|
elif key == 'import':
|
||||||
@@ -302,48 +301,34 @@ def build_statements(d, scope):
|
|||||||
|
|
||||||
term = key[len('count('):-len(')')]
|
term = key[len('count('):-len(')')]
|
||||||
|
|
||||||
if term.startswith('characteristic('):
|
# when looking for the existence of such a feature, our rule might look like:
|
||||||
# characteristic features are specified a bit specially:
|
# - mnemonic: mov
|
||||||
# they simply indicate the presence of something unusual/interesting,
|
#
|
||||||
# and we embed the name in the feature name, like `characteristic(nzxor)`.
|
# but here we deal with the form: `mnemonic(mov)`.
|
||||||
#
|
term, _, arg = term.partition('(')
|
||||||
# when we're dealing with counts, like `count(characteristic(nzxor))`,
|
Feature = parse_feature(term)
|
||||||
# we can simply extract the feature and assume we're looking for `True` values.
|
|
||||||
Feature = parse_feature(term)
|
|
||||||
feature = Feature(True)
|
|
||||||
ensure_feature_valid_for_scope(scope, feature)
|
|
||||||
else:
|
|
||||||
# however, for remaining counted features, like `count(mnemonic(mov))`,
|
|
||||||
# we have to jump through hoops.
|
|
||||||
#
|
|
||||||
# when looking for the existance of such a feature, our rule might look like:
|
|
||||||
# - mnemonic: mov
|
|
||||||
#
|
|
||||||
# but here we deal with the form: `mnemonic(mov)`.
|
|
||||||
term, _, arg = term.partition('(')
|
|
||||||
Feature = parse_feature(term)
|
|
||||||
|
|
||||||
if arg:
|
if arg:
|
||||||
arg = arg[:-len(')')]
|
arg = arg[:-len(')')]
|
||||||
# can't rely on yaml parsing ints embedded within strings
|
# can't rely on yaml parsing ints embedded within strings
|
||||||
# like:
|
# like:
|
||||||
#
|
#
|
||||||
# count(offset(0xC))
|
# count(offset(0xC))
|
||||||
# count(number(0x11223344))
|
# count(number(0x11223344))
|
||||||
# count(number(0x100 = description))
|
# count(number(0x100 = description))
|
||||||
if term in ('number', 'offset', 'bytes'):
|
if term in ('number', 'offset', 'bytes'):
|
||||||
value, description = parse_description(arg, term)
|
value, description = parse_description(arg, term)
|
||||||
feature = Feature(value, description)
|
feature = Feature(value, description)
|
||||||
else:
|
|
||||||
# arg is string, like:
|
|
||||||
#
|
|
||||||
# count(mnemonic(mov))
|
|
||||||
# count(string(error))
|
|
||||||
# TODO: what about embedded newlines?
|
|
||||||
feature = Feature(arg)
|
|
||||||
else:
|
else:
|
||||||
feature = Feature()
|
# arg is string, like:
|
||||||
ensure_feature_valid_for_scope(scope, feature)
|
#
|
||||||
|
# count(mnemonic(mov))
|
||||||
|
# count(string(error))
|
||||||
|
# TODO: what about embedded newlines?
|
||||||
|
feature = Feature(arg)
|
||||||
|
else:
|
||||||
|
feature = Feature()
|
||||||
|
ensure_feature_valid_for_scope(scope, feature)
|
||||||
|
|
||||||
count = d[key]
|
count = d[key]
|
||||||
if isinstance(count, int):
|
if isinstance(count, int):
|
||||||
|
|||||||
2
rules
2
rules
Submodule rules updated: e5db226844...da61c9138e
@@ -12,23 +12,23 @@ from fixtures import *
|
|||||||
|
|
||||||
EXTRACTOR = capa.features.extractors.NullFeatureExtractor({
|
EXTRACTOR = capa.features.extractors.NullFeatureExtractor({
|
||||||
'file features': [
|
'file features': [
|
||||||
(0x402345, capa.features.Characteristic('embedded pe', True)),
|
(0x402345, capa.features.Characteristic('embedded pe')),
|
||||||
],
|
],
|
||||||
'functions': {
|
'functions': {
|
||||||
0x401000: {
|
0x401000: {
|
||||||
'features': [
|
'features': [
|
||||||
(0x401000, capa.features.Characteristic('switch', True)),
|
(0x401000, capa.features.Characteristic('switch')),
|
||||||
],
|
],
|
||||||
'basic blocks': {
|
'basic blocks': {
|
||||||
0x401000: {
|
0x401000: {
|
||||||
'features': [
|
'features': [
|
||||||
(0x401000, capa.features.Characteristic('tight loop', True)),
|
(0x401000, capa.features.Characteristic('tight loop')),
|
||||||
],
|
],
|
||||||
'instructions': {
|
'instructions': {
|
||||||
0x401000: {
|
0x401000: {
|
||||||
'features': [
|
'features': [
|
||||||
(0x401000, capa.features.insn.Mnemonic('xor')),
|
(0x401000, capa.features.insn.Mnemonic('xor')),
|
||||||
(0x401000, capa.features.Characteristic('nzxor', True)),
|
(0x401000, capa.features.Characteristic('nzxor')),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
0x401002: {
|
0x401002: {
|
||||||
@@ -57,9 +57,9 @@ def test_null_feature_extractor():
|
|||||||
scope: basic block
|
scope: basic block
|
||||||
features:
|
features:
|
||||||
- and:
|
- and:
|
||||||
- characteristic(tight loop): true
|
- characteristic: tight loop
|
||||||
- mnemonic: xor
|
- mnemonic: xor
|
||||||
- characteristic(nzxor): true
|
- characteristic: nzxor
|
||||||
''')),
|
''')),
|
||||||
])
|
])
|
||||||
capabilities = capa.main.find_capabilities(rules, EXTRACTOR)
|
capabilities = capa.main.find_capabilities(rules, EXTRACTOR)
|
||||||
@@ -150,7 +150,7 @@ def test_serialize_features():
|
|||||||
roundtrip_feature(capa.features.insn.Offset(0x0))
|
roundtrip_feature(capa.features.insn.Offset(0x0))
|
||||||
roundtrip_feature(capa.features.insn.Mnemonic('push'))
|
roundtrip_feature(capa.features.insn.Mnemonic('push'))
|
||||||
roundtrip_feature(capa.features.file.Section('.rsrc'))
|
roundtrip_feature(capa.features.file.Section('.rsrc'))
|
||||||
roundtrip_feature(capa.features.Characteristic('tight loop', True))
|
roundtrip_feature(capa.features.Characteristic('tight loop'))
|
||||||
roundtrip_feature(capa.features.basicblock.BasicBlock())
|
roundtrip_feature(capa.features.basicblock.BasicBlock())
|
||||||
roundtrip_feature(capa.features.file.Export('BaseThreadInitThunk'))
|
roundtrip_feature(capa.features.file.Export('BaseThreadInitThunk'))
|
||||||
roundtrip_feature(capa.features.file.Import('kernel32.IsWow64Process'))
|
roundtrip_feature(capa.features.file.Import('kernel32.IsWow64Process'))
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def test_ruleset():
|
|||||||
name: file rule
|
name: file rule
|
||||||
scope: file
|
scope: file
|
||||||
features:
|
features:
|
||||||
- characteristic(embedded pe): y
|
- characteristic: embedded pe
|
||||||
''')),
|
''')),
|
||||||
capa.rules.Rule.from_yaml(textwrap.dedent('''
|
capa.rules.Rule.from_yaml(textwrap.dedent('''
|
||||||
rule:
|
rule:
|
||||||
@@ -50,7 +50,7 @@ def test_ruleset():
|
|||||||
name: function rule
|
name: function rule
|
||||||
scope: function
|
scope: function
|
||||||
features:
|
features:
|
||||||
- characteristic(switch): y
|
- characteristic: switch
|
||||||
''')),
|
''')),
|
||||||
capa.rules.Rule.from_yaml(textwrap.dedent('''
|
capa.rules.Rule.from_yaml(textwrap.dedent('''
|
||||||
rule:
|
rule:
|
||||||
@@ -58,7 +58,7 @@ def test_ruleset():
|
|||||||
name: basic block rule
|
name: basic block rule
|
||||||
scope: basic block
|
scope: basic block
|
||||||
features:
|
features:
|
||||||
- characteristic(nzxor): y
|
- characteristic: nzxor
|
||||||
''')),
|
''')),
|
||||||
|
|
||||||
])
|
])
|
||||||
@@ -128,7 +128,7 @@ def test_match_across_scopes(sample_9324d1a8ae37a36ae560c37448c9705a):
|
|||||||
examples:
|
examples:
|
||||||
- 9324d1a8ae37a36ae560c37448c9705a:0x403685
|
- 9324d1a8ae37a36ae560c37448c9705a:0x403685
|
||||||
features:
|
features:
|
||||||
- characteristic(tight loop): true
|
- characteristic: tight loop
|
||||||
''')),
|
''')),
|
||||||
# this rule should match on a function (0x403660)
|
# this rule should match on a function (0x403660)
|
||||||
# based on API, as well as prior basic block rule match
|
# based on API, as well as prior basic block rule match
|
||||||
@@ -176,7 +176,7 @@ def test_subscope_bb_rules(sample_9324d1a8ae37a36ae560c37448c9705a):
|
|||||||
features:
|
features:
|
||||||
- and:
|
- and:
|
||||||
- basic block:
|
- basic block:
|
||||||
- characteristic(tight loop): true
|
- characteristic: tight loop
|
||||||
'''))
|
'''))
|
||||||
])
|
])
|
||||||
# tight loop at 0x403685
|
# tight loop at 0x403685
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ def test_invalid_rule_feature():
|
|||||||
name: test rule
|
name: test rule
|
||||||
scope: file
|
scope: file
|
||||||
features:
|
features:
|
||||||
- characteristic(nzxor): true
|
- characteristic: nzxor
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
with pytest.raises(capa.rules.InvalidRule):
|
with pytest.raises(capa.rules.InvalidRule):
|
||||||
@@ -128,7 +128,7 @@ def test_invalid_rule_feature():
|
|||||||
name: test rule
|
name: test rule
|
||||||
scope: function
|
scope: function
|
||||||
features:
|
features:
|
||||||
- characteristic(embedded pe): true
|
- characteristic: embedded pe
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
with pytest.raises(capa.rules.InvalidRule):
|
with pytest.raises(capa.rules.InvalidRule):
|
||||||
@@ -138,7 +138,7 @@ def test_invalid_rule_feature():
|
|||||||
name: test rule
|
name: test rule
|
||||||
scope: basic block
|
scope: basic block
|
||||||
features:
|
features:
|
||||||
- characteristic(embedded pe): true
|
- characteristic: embedded pe
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
|
|
||||||
@@ -173,11 +173,11 @@ def test_subscope_rules():
|
|||||||
scope: file
|
scope: file
|
||||||
features:
|
features:
|
||||||
- and:
|
- and:
|
||||||
- characteristic(embedded pe): true
|
- characteristic: embedded pe
|
||||||
- function:
|
- function:
|
||||||
- and:
|
- and:
|
||||||
- characteristic(nzxor): true
|
- characteristic: nzxor
|
||||||
- characteristic(switch): true
|
- characteristic: switch
|
||||||
'''))
|
'''))
|
||||||
])
|
])
|
||||||
# the file rule scope will have one rules:
|
# the file rule scope will have one rules:
|
||||||
@@ -229,7 +229,7 @@ def test_invalid_rules():
|
|||||||
meta:
|
meta:
|
||||||
name: test rule
|
name: test rule
|
||||||
features:
|
features:
|
||||||
- characteristic(number(1)): True
|
- characteristic: number(1)
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
with pytest.raises(capa.rules.InvalidRule):
|
with pytest.raises(capa.rules.InvalidRule):
|
||||||
@@ -238,7 +238,7 @@ def test_invalid_rules():
|
|||||||
meta:
|
meta:
|
||||||
name: test rule
|
name: test rule
|
||||||
features:
|
features:
|
||||||
- characteristic(count(number(100))): True
|
- characteristic: count(number(100))
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ def test_offset_features(mimikatz):
|
|||||||
|
|
||||||
def test_nzxor_features(mimikatz):
|
def test_nzxor_features(mimikatz):
|
||||||
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x410DFC))
|
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x410DFC))
|
||||||
assert capa.features.Characteristic('nzxor', True) in features # 0x0410F0B
|
assert capa.features.Characteristic('nzxor') in features # 0x0410F0B
|
||||||
|
|
||||||
|
|
||||||
def get_bb_insn(f, va):
|
def get_bb_insn(f, va):
|
||||||
@@ -154,7 +154,7 @@ def test_mnemonic_features(mimikatz):
|
|||||||
|
|
||||||
def test_peb_access_features(sample_a933a1a402775cfa94b6bee0963f4b46):
|
def test_peb_access_features(sample_a933a1a402775cfa94b6bee0963f4b46):
|
||||||
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC))
|
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC))
|
||||||
assert capa.features.Characteristic('peb access', True) in features
|
assert capa.features.Characteristic('peb access') in features
|
||||||
|
|
||||||
|
|
||||||
def test_file_section_name_features(mimikatz):
|
def test_file_section_name_features(mimikatz):
|
||||||
@@ -170,7 +170,7 @@ def test_tight_loop_features(mimikatz):
|
|||||||
if bb.va != 0x402F8E:
|
if bb.va != 0x402F8E:
|
||||||
continue
|
continue
|
||||||
features = extract_basic_block_features(f, bb)
|
features = extract_basic_block_features(f, bb)
|
||||||
assert capa.features.Characteristic('tight loop', True) in features
|
assert capa.features.Characteristic('tight loop') in features
|
||||||
assert capa.features.basicblock.BasicBlock() in features
|
assert capa.features.basicblock.BasicBlock() in features
|
||||||
|
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ def test_tight_loop_bb_features(mimikatz):
|
|||||||
if bb.va != 0x402F8E:
|
if bb.va != 0x402F8E:
|
||||||
continue
|
continue
|
||||||
features = extract_basic_block_features(f, bb)
|
features = extract_basic_block_features(f, bb)
|
||||||
assert capa.features.Characteristic('tight loop', True) in features
|
assert capa.features.Characteristic('tight loop') in features
|
||||||
assert capa.features.basicblock.BasicBlock() in features
|
assert capa.features.basicblock.BasicBlock() in features
|
||||||
|
|
||||||
|
|
||||||
@@ -202,17 +202,17 @@ def test_file_import_name_features(mimikatz):
|
|||||||
|
|
||||||
def test_cross_section_flow_features(sample_a198216798ca38f280dc413f8c57f2c2):
|
def test_cross_section_flow_features(sample_a198216798ca38f280dc413f8c57f2c2):
|
||||||
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x4014D0))
|
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x4014D0))
|
||||||
assert capa.features.Characteristic('cross section flow', True) in features
|
assert capa.features.Characteristic('cross section flow') in features
|
||||||
|
|
||||||
# this function has calls to some imports,
|
# this function has calls to some imports,
|
||||||
# which should not trigger cross-section flow characteristic
|
# which should not trigger cross-section flow characteristic
|
||||||
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x401563))
|
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x401563))
|
||||||
assert capa.features.Characteristic('cross section flow', True) not in features
|
assert capa.features.Characteristic('cross section flow') not in features
|
||||||
|
|
||||||
|
|
||||||
def test_segment_access_features(sample_a933a1a402775cfa94b6bee0963f4b46):
|
def test_segment_access_features(sample_a933a1a402775cfa94b6bee0963f4b46):
|
||||||
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC))
|
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC))
|
||||||
assert capa.features.Characteristic('fs access', True) in features
|
assert capa.features.Characteristic('fs access') in features
|
||||||
|
|
||||||
|
|
||||||
def test_thunk_features(sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_thunk_features(sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
@@ -223,36 +223,36 @@ def test_thunk_features(sample_9324d1a8ae37a36ae560c37448c9705a):
|
|||||||
|
|
||||||
def test_file_embedded_pe(pma_lab_12_04):
|
def test_file_embedded_pe(pma_lab_12_04):
|
||||||
features = extract_file_features(pma_lab_12_04.vw, pma_lab_12_04.path)
|
features = extract_file_features(pma_lab_12_04.vw, pma_lab_12_04.path)
|
||||||
assert capa.features.Characteristic('embedded pe', True) in features
|
assert capa.features.Characteristic('embedded pe') in features
|
||||||
|
|
||||||
|
|
||||||
def test_stackstring_features(mimikatz):
|
def test_stackstring_features(mimikatz):
|
||||||
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x4556E5))
|
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x4556E5))
|
||||||
assert capa.features.Characteristic('stack string', True) in features
|
assert capa.features.Characteristic('stack string') in features
|
||||||
|
|
||||||
|
|
||||||
def test_switch_features(mimikatz):
|
def test_switch_features(mimikatz):
|
||||||
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409411))
|
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409411))
|
||||||
assert capa.features.Characteristic('switch', True) in features
|
assert capa.features.Characteristic('switch') in features
|
||||||
|
|
||||||
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409393))
|
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409393))
|
||||||
assert capa.features.Characteristic('switch', True) not in features
|
assert capa.features.Characteristic('switch') not in features
|
||||||
|
|
||||||
|
|
||||||
def test_recursive_call_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41):
|
def test_recursive_call_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41):
|
||||||
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003100))
|
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003100))
|
||||||
assert capa.features.Characteristic('recursive call', True) in features
|
assert capa.features.Characteristic('recursive call') in features
|
||||||
|
|
||||||
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10007B00))
|
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10007B00))
|
||||||
assert capa.features.Characteristic('recursive call', True) not in features
|
assert capa.features.Characteristic('recursive call') not in features
|
||||||
|
|
||||||
|
|
||||||
def test_loop_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41):
|
def test_loop_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41):
|
||||||
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003D30))
|
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003D30))
|
||||||
assert capa.features.Characteristic('loop', True) in features
|
assert capa.features.Characteristic('loop') in features
|
||||||
|
|
||||||
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10007250))
|
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10007250))
|
||||||
assert capa.features.Characteristic('loop', True) not in features
|
assert capa.features.Characteristic('loop') not in features
|
||||||
|
|
||||||
|
|
||||||
def test_file_string_features(sample_bfb9b5391a13d0afd787e87ab90f14f5):
|
def test_file_string_features(sample_bfb9b5391a13d0afd787e87ab90f14f5):
|
||||||
@@ -263,20 +263,20 @@ def test_file_string_features(sample_bfb9b5391a13d0afd787e87ab90f14f5):
|
|||||||
|
|
||||||
def test_function_calls_to(sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_function_calls_to(sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
|
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
|
||||||
assert capa.features.Characteristic('calls to', True) in features
|
assert capa.features.Characteristic('calls to') in features
|
||||||
assert len(features[capa.features.Characteristic('calls to', True)]) == 1
|
assert len(features[capa.features.Characteristic('calls to')]) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_function_calls_to64(sample_lab21_01):
|
def test_function_calls_to64(sample_lab21_01):
|
||||||
features = extract_function_features(viv_utils.Function(sample_lab21_01.vw, 0x1400052D0)) # memcpy
|
features = extract_function_features(viv_utils.Function(sample_lab21_01.vw, 0x1400052D0)) # memcpy
|
||||||
assert capa.features.Characteristic('calls to', True) in features
|
assert capa.features.Characteristic('calls to') in features
|
||||||
assert len(features[capa.features.Characteristic('calls to', True)]) == 8
|
assert len(features[capa.features.Characteristic('calls to')]) == 8
|
||||||
|
|
||||||
|
|
||||||
def test_function_calls_from(sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_function_calls_from(sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
|
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
|
||||||
assert capa.features.Characteristic('calls from', True) in features
|
assert capa.features.Characteristic('calls from') in features
|
||||||
assert len(features[capa.features.Characteristic('calls from', True)]) == 23
|
assert len(features[capa.features.Characteristic('calls from')]) == 23
|
||||||
|
|
||||||
|
|
||||||
def test_basic_block_count(sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_basic_block_count(sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
@@ -286,8 +286,8 @@ def test_basic_block_count(sample_9324d1a8ae37a36ae560c37448c9705a):
|
|||||||
|
|
||||||
def test_indirect_call_features(sample_a933a1a402775cfa94b6bee0963f4b46):
|
def test_indirect_call_features(sample_a933a1a402775cfa94b6bee0963f4b46):
|
||||||
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA68A0))
|
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA68A0))
|
||||||
assert capa.features.Characteristic('indirect call', True) in features
|
assert capa.features.Characteristic('indirect call') in features
|
||||||
assert len(features[capa.features.Characteristic('indirect call', True)]) == 3
|
assert len(features[capa.features.Characteristic('indirect call')]) == 3
|
||||||
|
|
||||||
|
|
||||||
def test_indirect_calls_resolved(sample_c91887d861d9bd4a5872249b641bc9f9):
|
def test_indirect_calls_resolved(sample_c91887d861d9bd4a5872249b641bc9f9):
|
||||||
|
|||||||
Reference in New Issue
Block a user