mirror of
https://github.com/mandiant/capa.git
synced 2025-12-22 07:10:29 -08:00
double to single quotes
This commit is contained in:
@@ -10,7 +10,7 @@ try:
|
|||||||
except (ImportError, SyntaxError):
|
except (ImportError, SyntaxError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__all__ = ["ida", "viv"]
|
__all__ = ['ida', 'viv']
|
||||||
|
|
||||||
|
|
||||||
class FeatureExtractor(object):
|
class FeatureExtractor(object):
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import re
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
ASCII_BYTE = r" !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t".encode('ascii')
|
ASCII_BYTE = r' !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t'.encode('ascii')
|
||||||
ASCII_RE_4 = re.compile(b"([%s]{%d,})" % (ASCII_BYTE, 4))
|
ASCII_RE_4 = re.compile(b'([%s]{%d,})' % (ASCII_BYTE, 4))
|
||||||
UNICODE_RE_4 = re.compile(b"((?:[%s]\x00){%d,})" % (ASCII_BYTE, 4))
|
UNICODE_RE_4 = re.compile(b'((?:[%s]\x00){%d,})' % (ASCII_BYTE, 4))
|
||||||
REPEATS = [b"A", b"\x00", b"\xfe", b"\xff"]
|
REPEATS = [b'A', b'\x00', b'\xfe', b'\xff']
|
||||||
SLICE_SIZE = 4096
|
SLICE_SIZE = 4096
|
||||||
|
|
||||||
String = namedtuple("String", ["s", "offset"])
|
String = namedtuple('String', ['s', 'offset'])
|
||||||
|
|
||||||
|
|
||||||
def buf_filled_with(buf, character):
|
def buf_filled_with(buf, character):
|
||||||
@@ -46,10 +46,10 @@ def extract_ascii_strings(buf, n=4):
|
|||||||
if n == 4:
|
if n == 4:
|
||||||
r = ASCII_RE_4
|
r = ASCII_RE_4
|
||||||
else:
|
else:
|
||||||
reg = b"([%s]{%d,})" % (ASCII_BYTE, n)
|
reg = b'([%s]{%d,})' % (ASCII_BYTE, n)
|
||||||
r = re.compile(reg)
|
r = re.compile(reg)
|
||||||
for match in r.finditer(buf):
|
for match in r.finditer(buf):
|
||||||
yield String(match.group().decode("ascii"), match.start())
|
yield String(match.group().decode('ascii'), match.start())
|
||||||
|
|
||||||
|
|
||||||
def extract_unicode_strings(buf, n=4):
|
def extract_unicode_strings(buf, n=4):
|
||||||
@@ -72,11 +72,11 @@ def extract_unicode_strings(buf, n=4):
|
|||||||
if n == 4:
|
if n == 4:
|
||||||
r = UNICODE_RE_4
|
r = UNICODE_RE_4
|
||||||
else:
|
else:
|
||||||
reg = b"((?:[%s]\x00){%d,})" % (ASCII_BYTE, n)
|
reg = b'((?:[%s]\x00){%d,})' % (ASCII_BYTE, n)
|
||||||
r = re.compile(reg)
|
r = re.compile(reg)
|
||||||
for match in r.finditer(buf):
|
for match in r.finditer(buf):
|
||||||
try:
|
try:
|
||||||
yield String(match.group().decode("utf-16"), match.start())
|
yield String(match.group().decode('utf-16'), match.start())
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import file
|
|||||||
import function
|
import function
|
||||||
import basicblock
|
import basicblock
|
||||||
import insn
|
import insn
|
||||||
__all__ = ["file", "function", "basicblock", "insn"]
|
__all__ = ['file', 'function', 'basicblock', 'insn']
|
||||||
|
|
||||||
|
|
||||||
def get_va(self):
|
def get_va(self):
|
||||||
|
|||||||
@@ -237,17 +237,17 @@ def main(argv=None):
|
|||||||
]
|
]
|
||||||
format_help = ', '.join(['%s: %s' % (f[0], f[1]) for f in formats])
|
format_help = ', '.join(['%s: %s' % (f[0], f[1]) for f in formats])
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="save capa features to a file")
|
parser = argparse.ArgumentParser(description='save capa features to a file')
|
||||||
parser.add_argument("sample", type=str,
|
parser.add_argument('sample', type=str,
|
||||||
help="Path to sample to analyze")
|
help='Path to sample to analyze')
|
||||||
parser.add_argument("output", type=str,
|
parser.add_argument('output', type=str,
|
||||||
help="Path to output file")
|
help='Path to output file')
|
||||||
parser.add_argument("-v", "--verbose", action="store_true",
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
help="Enable verbose output")
|
help='Enable verbose output')
|
||||||
parser.add_argument("-q", "--quiet", action="store_true",
|
parser.add_argument('-q', '--quiet', action='store_true',
|
||||||
help="Disable all output but errors")
|
help='Disable all output but errors')
|
||||||
parser.add_argument("-f", "--format", choices=[f[0] for f in formats], default="auto",
|
parser.add_argument('-f', '--format', choices=[f[0] for f in formats], default='auto',
|
||||||
help="Select sample format, %s" % format_help)
|
help='Select sample format, %s' % format_help)
|
||||||
args = parser.parse_args(args=argv)
|
args = parser.parse_args(args=argv)
|
||||||
|
|
||||||
if args.quiet:
|
if args.quiet:
|
||||||
@@ -271,6 +271,6 @@ def main(argv=None):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
technique = parts[2].replace('-', ' ')
|
technique = parts[2].replace('-', ' ')
|
||||||
techniques.add(technique)
|
techniques.add(technique)
|
||||||
if len(parts) > 3:
|
if len(parts) > 3:
|
||||||
raise capa.rules.InvalidRule(capa.main.RULE_CATEGORY + " tag must have at most three components")
|
raise capa.rules.InvalidRule(capa.main.RULE_CATEGORY + ' tag must have at most three components')
|
||||||
|
|
||||||
# set row count to max set size
|
# set row count to max set size
|
||||||
self._view_summary.setRowCount(max(map(len, (rules, objectives, behaviors, techniques))))
|
self._view_summary.setRowCount(max(map(len, (rules, objectives, behaviors, techniques))))
|
||||||
|
|||||||
102
capa/main.py
102
capa/main.py
@@ -221,7 +221,7 @@ def render_capabilities_default(ruleset, results):
|
|||||||
technique = parts[2].replace('-', ' ')
|
technique = parts[2].replace('-', ' ')
|
||||||
techniques.add(technique)
|
techniques.add(technique)
|
||||||
if len(parts) > 3:
|
if len(parts) > 3:
|
||||||
raise capa.rules.InvalidRule(RULE_CATEGORY + " tag must have at most three components")
|
raise capa.rules.InvalidRule(RULE_CATEGORY + ' tag must have at most three components')
|
||||||
|
|
||||||
if technique:
|
if technique:
|
||||||
o[objective][behavior][technique][rule.name] = rule
|
o[objective][behavior][technique][rule.name] = rule
|
||||||
@@ -349,7 +349,7 @@ def render_result(res, indent=''):
|
|||||||
if sum(map(lambda c: c.success, res.children)) > 0:
|
if sum(map(lambda c: c.success, res.children)) > 0:
|
||||||
print('%soptional:' % indent)
|
print('%soptional:' % indent)
|
||||||
else:
|
else:
|
||||||
print("%s%d or more" % (indent, res.statement.count))
|
print('%s%d or more' % (indent, res.statement.count))
|
||||||
elif not isinstance(res.statement, (capa.features.Feature, capa.engine.Element, capa.engine.Range, capa.engine.Regex)):
|
elif not isinstance(res.statement, (capa.features.Feature, capa.engine.Element, capa.engine.Range, capa.engine.Regex)):
|
||||||
# when rending a structural node (and/or/not),
|
# when rending a structural node (and/or/not),
|
||||||
# then we only care about the node name.
|
# then we only care about the node name.
|
||||||
@@ -517,7 +517,7 @@ def get_rules(rule_path):
|
|||||||
with open(rule_path, 'rb') as f:
|
with open(rule_path, 'rb') as f:
|
||||||
rule = capa.rules.Rule.from_yaml(f.read().decode('utf-8'))
|
rule = capa.rules.Rule.from_yaml(f.read().decode('utf-8'))
|
||||||
|
|
||||||
if is_nursery_rule_path(root):
|
if is_nursery_rule_path(rule_path):
|
||||||
rule.meta['nursery'] = True
|
rule.meta['nursery'] = True
|
||||||
|
|
||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
@@ -637,35 +637,35 @@ def main(argv=None):
|
|||||||
try:
|
try:
|
||||||
extractor = get_extractor(args.sample, args.format)
|
extractor = get_extractor(args.sample, args.format)
|
||||||
except UnsupportedFormatError:
|
except UnsupportedFormatError:
|
||||||
logger.error("-" * 80)
|
logger.error('-' * 80)
|
||||||
logger.error(" Input file does not appear to be a PE file.")
|
logger.error(' Input file does not appear to be a PE file.')
|
||||||
logger.error(" ")
|
logger.error(' ')
|
||||||
logger.error(" Today, capa currently only supports analyzing PE files (or shellcode, when using --format sc32|sc64).")
|
logger.error(' Today, capa currently only supports analyzing PE files (or shellcode, when using --format sc32|sc64).')
|
||||||
logger.error(" If you don't know the input file type, you can try using the `file` utility to guess it.")
|
logger.error(' If you don\'t know the input file type, you can try using the `file` utility to guess it.')
|
||||||
logger.error("-" * 80)
|
logger.error('-' * 80)
|
||||||
return -1
|
return -1
|
||||||
except UnsupportedRuntimeError:
|
except UnsupportedRuntimeError:
|
||||||
logger.error("-" * 80)
|
logger.error('-' * 80)
|
||||||
logger.error(" Unsupported runtime or Python interpreter.")
|
logger.error(' Unsupported runtime or Python interpreter.')
|
||||||
logger.error(" ")
|
logger.error(' ')
|
||||||
logger.error(" Today, capa supports running under Python 2.7 using Vivisect for binary analysis.")
|
logger.error(' Today, capa supports running under Python 2.7 using Vivisect for binary analysis.')
|
||||||
logger.error(" It can also run within IDA Pro, using either Python 2.7 or 3.5+.")
|
logger.error(' It can also run within IDA Pro, using either Python 2.7 or 3.5+.')
|
||||||
logger.error(" ")
|
logger.error(' ')
|
||||||
logger.error(" If you're seeing this message on the command line, please ensure you're running Python 2.7.")
|
logger.error(' If you\'re seeing this message on the command line, please ensure you\'re running Python 2.7.')
|
||||||
logger.error("-" * 80)
|
logger.error('-' * 80)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
capabilities = find_capabilities(rules, extractor)
|
capabilities = find_capabilities(rules, extractor)
|
||||||
|
|
||||||
if appears_rule_cat(rules, capabilities, 'other-features/installer/'):
|
if appears_rule_cat(rules, capabilities, 'other-features/installer/'):
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
logger.warning(" This sample appears to be an installer.")
|
logger.warning(' This sample appears to be an installer.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" capa cannot handle installers well. This means the results may be misleading or incomplete.")
|
logger.warning(' capa cannot handle installers well. This means the results may be misleading or incomplete.')
|
||||||
logger.warning(" You should try to understand the install mechanism and analyze created files with capa.")
|
logger.warning(' You should try to understand the install mechanism and analyze created files with capa.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" Use -v or -vv if you really want to see the capabilities identified by capa.")
|
logger.warning(' Use -v or -vv if you really want to see the capabilities identified by capa.')
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
# capa will likely detect installer specific functionality.
|
# capa will likely detect installer specific functionality.
|
||||||
# this is probably not what the user wants.
|
# this is probably not what the user wants.
|
||||||
#
|
#
|
||||||
@@ -674,16 +674,16 @@ def main(argv=None):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
if appears_rule_cat(rules, capabilities, 'other-features/compiled-to-dot-net'):
|
if appears_rule_cat(rules, capabilities, 'other-features/compiled-to-dot-net'):
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
logger.warning(" This sample appears to be a .NET module.")
|
logger.warning(' This sample appears to be a .NET module.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" .NET is a cross-platform framework for running managed applications.")
|
logger.warning(' .NET is a cross-platform framework for running managed applications.')
|
||||||
logger.warning(
|
logger.warning(
|
||||||
" Today, capa cannot handle non-native files. This means that the results may be misleading or incomplete.")
|
' Today, capa cannot handle non-native files. This means that the results may be misleading or incomplete.')
|
||||||
logger.warning(" You may have to analyze the file manually, using a tool like the .NET decompiler dnSpy.")
|
logger.warning(' You may have to analyze the file manually, using a tool like the .NET decompiler dnSpy.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" Use -v or -vv if you really want to see the capabilities identified by capa.")
|
logger.warning(' Use -v or -vv if you really want to see the capabilities identified by capa.')
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
# capa won't detect much in .NET samples.
|
# capa won't detect much in .NET samples.
|
||||||
# it might match some file-level things.
|
# it might match some file-level things.
|
||||||
# for consistency, bail on things that we don't support.
|
# for consistency, bail on things that we don't support.
|
||||||
@@ -693,16 +693,16 @@ def main(argv=None):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
if appears_rule_cat(rules, capabilities, 'other-features/compiled-with-autoit'):
|
if appears_rule_cat(rules, capabilities, 'other-features/compiled-with-autoit'):
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
logger.warning(" This sample appears to be compiled with AutoIt.")
|
logger.warning(' This sample appears to be compiled with AutoIt.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" AutoIt is a freeware BASIC-like scripting language designed for automating the Windows GUI.")
|
logger.warning(' AutoIt is a freeware BASIC-like scripting language designed for automating the Windows GUI.')
|
||||||
logger.warning(
|
logger.warning(
|
||||||
" Today, capa cannot handle AutoIt scripts. This means that the results will be misleading or incomplete.")
|
' Today, capa cannot handle AutoIt scripts. This means that the results will be misleading or incomplete.')
|
||||||
logger.warning(" You may have to analyze the file manually, using a tool like the AutoIt decompiler MyAut2Exe.")
|
logger.warning(' You may have to analyze the file manually, using a tool like the AutoIt decompiler MyAut2Exe.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" Use -v or -vv if you really want to see the capabilities identified by capa.")
|
logger.warning(' Use -v or -vv if you really want to see the capabilities identified by capa.')
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
# capa will detect dozens of capabilities for AutoIt samples,
|
# capa will detect dozens of capabilities for AutoIt samples,
|
||||||
# but these are due to the AutoIt runtime, not the payload script.
|
# but these are due to the AutoIt runtime, not the payload script.
|
||||||
# so, don't confuse the user with FP matches - bail instead
|
# so, don't confuse the user with FP matches - bail instead
|
||||||
@@ -712,13 +712,13 @@ def main(argv=None):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
if appears_rule_cat(rules, capabilities, 'anti-analysis/packing/'):
|
if appears_rule_cat(rules, capabilities, 'anti-analysis/packing/'):
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
logger.warning(" This sample appears packed.")
|
logger.warning(' This sample appears packed.')
|
||||||
logger.warning(" ")
|
logger.warning(' ')
|
||||||
logger.warning(" Packed samples have often been obfuscated to hide their logic.")
|
logger.warning(' Packed samples have often been obfuscated to hide their logic.')
|
||||||
logger.warning(" capa cannot handle obfuscation well. This means the results may be misleading or incomplete.")
|
logger.warning(' capa cannot handle obfuscation well. This means the results may be misleading or incomplete.')
|
||||||
logger.warning(" If possible, you should try to unpack this input file before analyzing it with capa.")
|
logger.warning(' If possible, you should try to unpack this input file before analyzing it with capa.')
|
||||||
logger.warning("-" * 80)
|
logger.warning('-' * 80)
|
||||||
|
|
||||||
if args.vverbose:
|
if args.vverbose:
|
||||||
render_capabilities_vverbose(capabilities)
|
render_capabilities_vverbose(capabilities)
|
||||||
@@ -770,7 +770,7 @@ def is_runtime_ida():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
if is_runtime_ida():
|
if is_runtime_ida():
|
||||||
ida_main()
|
ida_main()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ def main(argv=None):
|
|||||||
]
|
]
|
||||||
format_help = ', '.join(['%s: %s' % (f[0], f[1]) for f in formats])
|
format_help = ', '.join(['%s: %s' % (f[0], f[1]) for f in formats])
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="detect capabilities in programs.")
|
parser = argparse.ArgumentParser(description='detect capabilities in programs.')
|
||||||
parser.add_argument("sample", type=str,
|
parser.add_argument('sample', type=str,
|
||||||
help="Path to sample to analyze")
|
help='Path to sample to analyze')
|
||||||
parser.add_argument("-f", "--format", choices=[f[0] for f in formats], default="auto",
|
parser.add_argument('-f', '--format', choices=[f[0] for f in formats], default='auto',
|
||||||
help="Select sample format, %s" % format_help)
|
help='Select sample format, %s' % format_help)
|
||||||
parser.add_argument("-F", "--function", type=lambda x: int(x, 0),
|
parser.add_argument('-F', '--function', type=lambda x: int(x, 0),
|
||||||
help="Show features for specific function")
|
help='Show features for specific function')
|
||||||
args = parser.parse_args(args=argv)
|
args = parser.parse_args(args=argv)
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@@ -77,5 +77,5 @@ def main(argv=None):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def main():
|
|||||||
fnames = {}
|
fnames = {}
|
||||||
for f in idautils.Functions():
|
for f in idautils.Functions():
|
||||||
fname = idc.get_name(f)
|
fname = idc.get_name(f)
|
||||||
if fname.startswith("sub_"):
|
if fname.startswith('sub_'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name_demangled = idc.demangle_name(fname, INF_SHORT_DN_ATTR)
|
name_demangled = idc.demangle_name(fname, INF_SHORT_DN_ATTR)
|
||||||
@@ -35,12 +35,12 @@ def main():
|
|||||||
|
|
||||||
fnames[f] = fname
|
fnames[f] = fname
|
||||||
|
|
||||||
with open(idc.ARGV[1], "w") as f:
|
with open(idc.ARGV[1], 'w') as f:
|
||||||
json.dump(fnames, f)
|
json.dump(fnames, f)
|
||||||
|
|
||||||
# exit IDA
|
# exit IDA
|
||||||
idc.qexit(0)
|
idc.qexit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -40,5 +40,5 @@ def main():
|
|||||||
idc.qexit(0)
|
idc.qexit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -66,15 +66,15 @@ def main(argv=None):
|
|||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Freeze capa features of a file or of files in a directory")
|
parser = argparse.ArgumentParser(description='Freeze capa features of a file or of files in a directory')
|
||||||
parser.add_argument("file_path", type=str,
|
parser.add_argument('file_path', type=str,
|
||||||
help="Path to file or directory to analyze")
|
help='Path to file or directory to analyze')
|
||||||
parser.add_argument("-r", "--reprocess", action="store_true", default=False,
|
parser.add_argument('-r', '--reprocess', action='store_true', default=False,
|
||||||
help="Overwrite existing analysis")
|
help='Overwrite existing analysis')
|
||||||
parser.add_argument("-v", "--verbose", action="store_true",
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
help="Enable verbose output")
|
help='Enable verbose output')
|
||||||
parser.add_argument("-q", "--quiet", action="store_true",
|
parser.add_argument('-q', '--quiet', action='store_true',
|
||||||
help="Disable all output but errors")
|
help='Disable all output but errors')
|
||||||
args = parser.parse_args(args=argv)
|
args = parser.parse_args(args=argv)
|
||||||
|
|
||||||
if args.quiet:
|
if args.quiet:
|
||||||
@@ -98,5 +98,5 @@ def main(argv=None):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -217,23 +217,23 @@ def main(argv=None):
|
|||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Run capa rule file against frozen features in a directory")
|
parser = argparse.ArgumentParser(description='Run capa rule file against frozen features in a directory')
|
||||||
parser.add_argument("rules", type=str,
|
parser.add_argument('rules', type=str,
|
||||||
help="Path to directory containing rules")
|
help='Path to directory containing rules')
|
||||||
parser.add_argument("rule_name", type=str,
|
parser.add_argument('rule_name', type=str,
|
||||||
help="Name of rule to test")
|
help='Name of rule to test')
|
||||||
parser.add_argument("frozen_path", type=str,
|
parser.add_argument('frozen_path', type=str,
|
||||||
help="Path to frozen feature file or directory")
|
help='Path to frozen feature file or directory')
|
||||||
parser.add_argument("-f", "--fast", action="store_true",
|
parser.add_argument('-f', '--fast', action='store_true',
|
||||||
help="Don't test slow files")
|
help='Don't test slow files')
|
||||||
parser.add_argument("-o", "--only_matching", action="store_true",
|
parser.add_argument('-o', '--only_matching', action='store_true',
|
||||||
help="Print only if rule matches")
|
help='Print only if rule matches')
|
||||||
parser.add_argument("-s", "--save_image", action="store",
|
parser.add_argument('-s', '--save_image', action='store',
|
||||||
help="Directory to save exported images of function graphs")
|
help='Directory to save exported images of function graphs')
|
||||||
parser.add_argument("-v", "--verbose", action="count", default=0,
|
parser.add_argument('-v', '--verbose', action='count', default=0,
|
||||||
help="Increase output verbosity")
|
help='Increase output verbosity')
|
||||||
parser.add_argument("-q", "--quiet", action="store_true",
|
parser.add_argument('-q', '--quiet', action='store_true',
|
||||||
help="Disable all output but errors")
|
help='Disable all output but errors')
|
||||||
args = parser.parse_args(args=argv)
|
args = parser.parse_args(args=argv)
|
||||||
|
|
||||||
if args.quiet:
|
if args.quiet:
|
||||||
@@ -293,5 +293,5 @@ def main(argv=None):
|
|||||||
print_summary(args.verbose, time0)
|
print_summary(args.verbose, time0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -85,13 +85,13 @@ def get_function_names(fnames_file):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Launch IDA Pro in autonomous mode to dump function names of a file or of files in a directory")
|
description='Launch IDA Pro in autonomous mode to dump function names of a file or of files in a directory')
|
||||||
parser.add_argument("file_path", type=str,
|
parser.add_argument('file_path', type=str,
|
||||||
help="File or directory path to analyze")
|
help='File or directory path to analyze')
|
||||||
parser.add_argument("-r", "--reprocess", action="store_true", default=False,
|
parser.add_argument('-r', '--reprocess', action='store_true', default=False,
|
||||||
help="Overwrite existing analysis")
|
help='Overwrite existing analysis')
|
||||||
parser.add_argument("-v", "--verbose", action="store_true",
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
help="Enable verbose output")
|
help='Enable verbose output')
|
||||||
args = parser.parse_args(args=sys.argv[1:])
|
args = parser.parse_args(args=sys.argv[1:])
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
@@ -127,5 +127,5 @@ def main():
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -101,17 +101,17 @@ def get_md5_hexdigest(sample_path):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Launch IDA Pro in autonomous mode to export images of function graphs")
|
description='Launch IDA Pro in autonomous mode to export images of function graphs')
|
||||||
parser.add_argument("file_path", type=str,
|
parser.add_argument('file_path', type=str,
|
||||||
help="File to export from")
|
help='File to export from')
|
||||||
parser.add_argument("out_dir", type=str,
|
parser.add_argument('out_dir', type=str,
|
||||||
help="Export target directory")
|
help='Export target directory')
|
||||||
parser.add_argument("-f", "--functions", action="store",
|
parser.add_argument('-f', '--functions', action='store',
|
||||||
help="Comma separated list of functions to export")
|
help='Comma separated list of functions to export')
|
||||||
parser.add_argument("-m", "--manual", action="store_true",
|
parser.add_argument('-m', '--manual', action='store_true',
|
||||||
help="Manual mode: show IDA dialog boxes")
|
help='Manual mode: show IDA dialog boxes')
|
||||||
parser.add_argument("-v", "--verbose", action="store_true",
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
help="Enable verbose output")
|
help='Enable verbose output')
|
||||||
args = parser.parse_args(args=sys.argv[1:])
|
args = parser.parse_args(args=sys.argv[1:])
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
@@ -131,5 +131,5 @@ def main():
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
34
setup.py
34
setup.py
@@ -5,26 +5,26 @@ import setuptools
|
|||||||
|
|
||||||
|
|
||||||
requirements = [
|
requirements = [
|
||||||
"six",
|
'six',
|
||||||
"tqdm",
|
'tqdm',
|
||||||
"pyyaml",
|
'pyyaml',
|
||||||
"tabulate",
|
'tabulate',
|
||||||
]
|
]
|
||||||
|
|
||||||
if sys.version_info >= (3, 0):
|
if sys.version_info >= (3, 0):
|
||||||
# py3
|
# py3
|
||||||
requirements.append("networkx")
|
requirements.append('networkx')
|
||||||
else:
|
else:
|
||||||
# py2
|
# py2
|
||||||
requirements.append("enum34")
|
requirements.append('enum34')
|
||||||
requirements.append("vivisect")
|
requirements.append('vivisect')
|
||||||
requirements.append("viv-utils")
|
requirements.append('viv-utils')
|
||||||
requirements.append("networkx==2.2") # v2.2 is last version supported by Python 2.7
|
requirements.append('networkx==2.2') # v2.2 is last version supported by Python 2.7
|
||||||
|
|
||||||
# this sets __version__
|
# this sets __version__
|
||||||
# via: http://stackoverflow.com/a/7071358/87207
|
# via: http://stackoverflow.com/a/7071358/87207
|
||||||
# and: http://stackoverflow.com/a/2073599/87207
|
# and: http://stackoverflow.com/a/2073599/87207
|
||||||
with open(os.path.join("capa", "version.py"), "rb") as f:
|
with open(os.path.join('capa', 'version.py'), 'rb') as f:
|
||||||
exec(f.read())
|
exec(f.read())
|
||||||
|
|
||||||
|
|
||||||
@@ -35,17 +35,17 @@ def get_rule_paths():
|
|||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name='capa',
|
name='capa',
|
||||||
version=__version__,
|
version=__version__,
|
||||||
description="",
|
description='',
|
||||||
long_description="",
|
long_description='',
|
||||||
author="Willi Ballenthin, Moritz Raabe",
|
author='Willi Ballenthin, Moritz Raabe',
|
||||||
author_email='william.ballenthin@mandiant.com, moritz.raabe@mandiant.com',
|
author_email='william.ballenthin@mandiant.com, moritz.raabe@mandiant.com',
|
||||||
url='https://www.github.com/fireeye/capa',
|
url='https://www.github.com/fireeye/capa',
|
||||||
packages=setuptools.find_packages(exclude=['tests', 'testbed']),
|
packages=setuptools.find_packages(exclude=['tests', 'testbed']),
|
||||||
package_dir={'capa': 'capa'},
|
package_dir={'capa': 'capa'},
|
||||||
package_data={'capa': get_rule_paths()},
|
package_data={'capa': get_rule_paths()},
|
||||||
entry_points={
|
entry_points={
|
||||||
"console_scripts": [
|
'console_scripts': [
|
||||||
"capa=capa.main:main",
|
'capa=capa.main:main',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
@@ -56,7 +56,7 @@ setuptools.setup(
|
|||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 3 - Alpha',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Natural Language :: English',
|
'Natural Language :: English',
|
||||||
"Programming Language :: Python :: 2",
|
'Programming Language :: Python :: 2',
|
||||||
"Programming Language :: Python :: 3",
|
'Programming Language :: Python :: 3',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -159,12 +159,12 @@ def test_serialize_features():
|
|||||||
|
|
||||||
def test_freeze_sample(tmpdir, sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_freeze_sample(tmpdir, sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
# tmpdir fixture handles cleanup
|
# tmpdir fixture handles cleanup
|
||||||
o = tmpdir.mkdir("capa").join("test.frz").strpath
|
o = tmpdir.mkdir('capa').join('test.frz').strpath
|
||||||
assert capa.features.freeze.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, o, '-v']) == 0
|
assert capa.features.freeze.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, o, '-v']) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_freeze_load_sample(tmpdir, sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_freeze_load_sample(tmpdir, sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
o = tmpdir.mkdir("capa").join("test.frz")
|
o = tmpdir.mkdir('capa').join('test.frz')
|
||||||
viv_extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw,
|
viv_extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw,
|
||||||
sample_9324d1a8ae37a36ae560c37448c9705a.path)
|
sample_9324d1a8ae37a36ae560c37448c9705a.path)
|
||||||
with open(o.strpath, 'wb') as f:
|
with open(o.strpath, 'wb') as f:
|
||||||
|
|||||||
@@ -15,6 +15,21 @@ def test_main(sample_9324d1a8ae37a36ae560c37448c9705a):
|
|||||||
assert capa.main.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, '-v']) == 0
|
assert capa.main.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, '-v']) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_single_rule(sample_9324d1a8ae37a36ae560c37448c9705a, tmpdir):
|
||||||
|
# tests a single rule can be loaded successfully
|
||||||
|
RULE_CONTENT = textwrap.dedent('''
|
||||||
|
rule:
|
||||||
|
meta:
|
||||||
|
name: test rule
|
||||||
|
scope: file
|
||||||
|
features:
|
||||||
|
- string: test
|
||||||
|
''')
|
||||||
|
rule_file = tmpdir.mkdir('capa').join('rule.yml')
|
||||||
|
rule_file.write(RULE_CONTENT)
|
||||||
|
assert capa.main.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, '-v', '-r', rule_file.strpath]) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_main_shellcode(sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32):
|
def test_main_shellcode(sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32):
|
||||||
assert capa.main.main([sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32.path, '-v', '-f', 'sc32']) == 0
|
assert capa.main.main([sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32.path, '-v', '-f', 'sc32']) == 0
|
||||||
|
|
||||||
|
|||||||
@@ -82,14 +82,14 @@ def test_string_features(mimikatz):
|
|||||||
|
|
||||||
def test_byte_features(sample_9324d1a8ae37a36ae560c37448c9705a):
|
def test_byte_features(sample_9324d1a8ae37a36ae560c37448c9705a):
|
||||||
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
|
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
|
||||||
wanted = capa.features.Bytes(b"\xED\x24\x9E\xF4\x52\xA9\x07\x47\x55\x8E\xE1\xAB\x30\x8E\x23\x61")
|
wanted = capa.features.Bytes(b'\xED\x24\x9E\xF4\x52\xA9\x07\x47\x55\x8E\xE1\xAB\x30\x8E\x23\x61')
|
||||||
# use `==` rather than `is` because the result is not `True` but a truthy value.
|
# use `==` rather than `is` because the result is not `True` but a truthy value.
|
||||||
assert wanted.evaluate(features) == True
|
assert wanted.evaluate(features) == True
|
||||||
|
|
||||||
|
|
||||||
def test_byte_features64(sample_lab21_01):
|
def test_byte_features64(sample_lab21_01):
|
||||||
features = extract_function_features(viv_utils.Function(sample_lab21_01.vw, 0x1400010C0))
|
features = extract_function_features(viv_utils.Function(sample_lab21_01.vw, 0x1400010C0))
|
||||||
wanted = capa.features.Bytes(b"\x32\xA2\xDF\x2D\x99\x2B\x00\x00")
|
wanted = capa.features.Bytes(b'\x32\xA2\xDF\x2D\x99\x2B\x00\x00')
|
||||||
# use `==` rather than `is` because the result is not `True` but a truthy value.
|
# use `==` rather than `is` because the result is not `True` but a truthy value.
|
||||||
assert wanted.evaluate(features) == True
|
assert wanted.evaluate(features) == True
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user