mirror of
https://github.com/mandiant/capa.git
synced 2025-12-22 23:26:21 -08:00
dos2unix
This commit is contained in:
@@ -1,112 +1,111 @@
|
|||||||
"""
|
"""
|
||||||
Binary Ninja plugin that imports a capa report,
|
Binary Ninja plugin that imports a capa report,
|
||||||
produced via `capa --json /path/to/sample`,
|
produced via `capa --json /path/to/sample`,
|
||||||
into the current database.
|
into the current database.
|
||||||
|
|
||||||
It will mark up functions with their capa matches, like:
|
It will mark up functions with their capa matches, like:
|
||||||
|
|
||||||
; capa: print debug messages (host-interaction/log/debug/write-event)
|
; capa: print debug messages (host-interaction/log/debug/write-event)
|
||||||
; capa: delete service (host-interaction/service/delete)
|
; capa: delete service (host-interaction/service/delete)
|
||||||
; Attributes: bp-based frame
|
; Attributes: bp-based frame
|
||||||
|
|
||||||
public UninstallService
|
public UninstallService
|
||||||
UninstallService proc near
|
UninstallService proc near
|
||||||
...
|
...
|
||||||
|
|
||||||
To use, invoke from the Binary Ninja Tools menu, or from the
|
To use, invoke from the Binary Ninja Tools menu, or from the command-palette.
|
||||||
command-palette.
|
|
||||||
|
Adapted for Binary Ninja by @psifertex
|
||||||
Adapted for Binary Ninja by @psifertex
|
|
||||||
|
This script will verify that the report matches the workspace.
|
||||||
This script will verify that the report matches the workspace.
|
Check the log window for any errors, and/or the summary of changes.
|
||||||
Check the log window for any errors, and/or the summary of changes.
|
|
||||||
|
Derived from: https://github.com/fireeye/capa/blob/master/scripts/import-to-ida.py
|
||||||
Derived from: https://github.com/fireeye/capa/blob/master/scripts/import-to-ida.py
|
"""
|
||||||
"""
|
import os
|
||||||
import os
|
import json
|
||||||
import json
|
|
||||||
|
from binaryninja import *
|
||||||
from binaryninja import *
|
|
||||||
|
|
||||||
|
def append_func_cmt(bv, va, cmt):
|
||||||
def append_func_cmt(bv, va, cmt):
|
"""
|
||||||
"""
|
add the given comment to the given function,
|
||||||
add the given comment to the given function,
|
if it doesn't already exist.
|
||||||
if it doesn't already exist.
|
"""
|
||||||
"""
|
func = bv.get_function_at(va)
|
||||||
func = bv.get_function_at(va)
|
if not func:
|
||||||
if not func:
|
raise ValueError("not a function")
|
||||||
raise ValueError("not a function")
|
|
||||||
|
if cmt in func.comment:
|
||||||
if cmt in func.comment:
|
return
|
||||||
return
|
|
||||||
|
func.comment = func.comment + "\n" + cmt
|
||||||
func.comment = func.comment + "\n" + cmt
|
|
||||||
|
|
||||||
|
def load_analysis(bv):
|
||||||
def load_analysis(bv):
|
shortname = os.path.splitext(os.path.basename(bv.file.filename))[0]
|
||||||
shortname = os.path.splitext(os.path.basename(bv.file.filename))[0]
|
dirname = os.path.dirname(bv.file.filename)
|
||||||
dirname = os.path.dirname(bv.file.filename)
|
log_info(f"dirname: {dirname}\nshortname: {shortname}\n")
|
||||||
log_info(f"dirname: {dirname}\nshortname: {shortname}\n")
|
if os.access(os.path.join(dirname, shortname + ".js"), os.R_OK):
|
||||||
if os.access(os.path.join(dirname, shortname + ".js"), os.R_OK):
|
path = os.path.join(dirname, shortname + ".js")
|
||||||
path = os.path.join(dirname, shortname + ".js")
|
elif os.access(os.path.join(dirname, shortname + ".json"), os.R_OK):
|
||||||
elif os.access(os.path.join(dirname, shortname + ".json"), os.R_OK):
|
path = os.path.join(dirname, shortname + ".json")
|
||||||
path = os.path.join(dirname, shortname + ".json")
|
else:
|
||||||
else:
|
path = interaction.get_open_filename_input("capa report:", "JSON (*.js *.json);;All Files (*)")
|
||||||
path = interaction.get_open_filename_input("capa report:", "JSON (*.js *.json);;All Files (*)")
|
if not path or not os.access(path, os.R_OK):
|
||||||
if not path or not os.access(path, os.R_OK):
|
log_error("Invalid filename.")
|
||||||
log_error("Invalid filename.")
|
return 0
|
||||||
return 0
|
log_info("Using capa file %s" % path)
|
||||||
log_info("Using capa file %s" % path)
|
|
||||||
|
with open(path, "rb") as f:
|
||||||
with open(path, "rb") as f:
|
doc = json.loads(f.read().decode("utf-8"))
|
||||||
doc = json.loads(f.read().decode("utf-8"))
|
|
||||||
|
if "meta" not in doc or "rules" not in doc:
|
||||||
if "meta" not in doc or "rules" not in doc:
|
log_error("doesn't appear to be a capa report")
|
||||||
log_error("doesn't appear to be a capa report")
|
return -1
|
||||||
return -1
|
|
||||||
|
a = doc["meta"]["sample"]["md5"].lower()
|
||||||
a = doc["meta"]["sample"]["md5"].lower()
|
md5 = Transform["MD5"]
|
||||||
md5 = Transform["MD5"]
|
rawhex = Transform["RawHex"]
|
||||||
rawhex = Transform["RawHex"]
|
b = rawhex.encode(md5.encode(bv.parent_view.read(bv.parent_view.start, bv.parent_view.end))).decode("utf-8")
|
||||||
b = rawhex.encode(md5.encode(bv.parent_view.read(bv.parent_view.start, bv.parent_view.end))).decode("utf-8")
|
if not a == b:
|
||||||
if not a == b:
|
log_error("sample mismatch")
|
||||||
log_error("sample mismatch")
|
return -2
|
||||||
return -2
|
|
||||||
|
rows = []
|
||||||
rows = []
|
for rule in doc["rules"].values():
|
||||||
for rule in doc["rules"].values():
|
if rule["meta"].get("lib"):
|
||||||
if rule["meta"].get("lib"):
|
continue
|
||||||
continue
|
if rule["meta"].get("capa/subscope"):
|
||||||
if rule["meta"].get("capa/subscope"):
|
continue
|
||||||
continue
|
if rule["meta"]["scope"] != "function":
|
||||||
if rule["meta"]["scope"] != "function":
|
continue
|
||||||
continue
|
|
||||||
|
name = rule["meta"]["name"]
|
||||||
name = rule["meta"]["name"]
|
ns = rule["meta"].get("namespace", "")
|
||||||
ns = rule["meta"].get("namespace", "")
|
for va in rule["matches"].keys():
|
||||||
for va in rule["matches"].keys():
|
va = int(va)
|
||||||
va = int(va)
|
rows.append((ns, name, va))
|
||||||
rows.append((ns, name, va))
|
|
||||||
|
# order by (namespace, name) so that like things show up together
|
||||||
# order by (namespace, name) so that like things show up together
|
rows = sorted(rows)
|
||||||
rows = sorted(rows)
|
for ns, name, va in rows:
|
||||||
for ns, name, va in rows:
|
if ns:
|
||||||
if ns:
|
cmt = "%s (%s)" % (name, ns)
|
||||||
cmt = "%s (%s)" % (name, ns)
|
else:
|
||||||
else:
|
cmt = "%s" % (name,)
|
||||||
cmt = "%s" % (name,)
|
|
||||||
|
log_info("0x%x: %s" % (va, cmt))
|
||||||
log_info("0x%x: %s" % (va, cmt))
|
try:
|
||||||
try:
|
# message will look something like:
|
||||||
# message will look something like:
|
#
|
||||||
#
|
# capa: delete service (host-interaction/service/delete)
|
||||||
# capa: delete service (host-interaction/service/delete)
|
append_func_cmt(bv, va, "capa: " + cmt)
|
||||||
append_func_cmt(bv, va, "capa: " + cmt)
|
except ValueError:
|
||||||
except ValueError:
|
continue
|
||||||
continue
|
|
||||||
|
log_info("ok")
|
||||||
log_info("ok")
|
|
||||||
|
|
||||||
|
PluginCommand.register("Load capa file", "Loads an analysis file from capa", load_analysis)
|
||||||
PluginCommand.register("Load capa file", "Loads an analysis file from capa", load_analysis)
|
|
||||||
|
|||||||
@@ -1,117 +1,117 @@
|
|||||||
"""
|
"""
|
||||||
IDA Pro script that imports a capa report,
|
IDA Pro script that imports a capa report,
|
||||||
produced via `capa --json /path/to/sample`,
|
produced via `capa --json /path/to/sample`,
|
||||||
into the current database.
|
into the current database.
|
||||||
|
|
||||||
It will mark up functions with their capa matches, like:
|
It will mark up functions with their capa matches, like:
|
||||||
|
|
||||||
; capa: print debug messages (host-interaction/log/debug/write-event)
|
; capa: print debug messages (host-interaction/log/debug/write-event)
|
||||||
; capa: delete service (host-interaction/service/delete)
|
; capa: delete service (host-interaction/service/delete)
|
||||||
; Attributes: bp-based frame
|
; Attributes: bp-based frame
|
||||||
|
|
||||||
public UninstallService
|
public UninstallService
|
||||||
UninstallService proc near
|
UninstallService proc near
|
||||||
...
|
...
|
||||||
|
|
||||||
To use, invoke from the IDA Pro scripting dialog,
|
To use, invoke from the IDA Pro scripting dialog,
|
||||||
such as via Alt-F9,
|
such as via Alt-F9,
|
||||||
and then select the existing capa report from the file system.
|
and then select the existing capa report from the file system.
|
||||||
|
|
||||||
This script will verify that the report matches the workspace.
|
This script will verify that the report matches the workspace.
|
||||||
Check the output window for any errors, and/or the summary of changes.
|
Check the output window for any errors, and/or the summary of changes.
|
||||||
|
|
||||||
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
|
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at: [package root]/LICENSE.txt
|
You may obtain a copy of the License at: [package root]/LICENSE.txt
|
||||||
Unless required by applicable law or agreed to in writing, software distributed under the License
|
Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and limitations under the License.
|
See the License for the specific language governing permissions and limitations under the License.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import idc
|
import idc
|
||||||
import idautils
|
import idautils
|
||||||
import ida_idaapi
|
import ida_idaapi
|
||||||
import ida_kernwin
|
import ida_kernwin
|
||||||
|
|
||||||
logger = logging.getLogger("capa")
|
logger = logging.getLogger("capa")
|
||||||
|
|
||||||
|
|
||||||
def append_func_cmt(va, cmt, repeatable=False):
|
def append_func_cmt(va, cmt, repeatable=False):
|
||||||
"""
|
"""
|
||||||
add the given comment to the given function,
|
add the given comment to the given function,
|
||||||
if it doesn't already exist.
|
if it doesn't already exist.
|
||||||
"""
|
"""
|
||||||
func = ida_funcs.get_func(va)
|
func = ida_funcs.get_func(va)
|
||||||
if not func:
|
if not func:
|
||||||
raise ValueError("not a function")
|
raise ValueError("not a function")
|
||||||
|
|
||||||
existing = ida_funcs.get_func_cmt(func, repeatable) or ""
|
existing = ida_funcs.get_func_cmt(func, repeatable) or ""
|
||||||
if cmt in existing:
|
if cmt in existing:
|
||||||
return
|
return
|
||||||
|
|
||||||
new = existing + "\n" + cmt
|
new = existing + "\n" + cmt
|
||||||
ida_funcs.set_func_cmt(func, new, repeatable)
|
ida_funcs.set_func_cmt(func, new, repeatable)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
path = ida_kernwin.ask_file(False, "*", "capa report")
|
path = ida_kernwin.ask_file(False, "*", "capa report")
|
||||||
if not path:
|
if not path:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
doc = json.loads(f.read().decode("utf-8"))
|
doc = json.loads(f.read().decode("utf-8"))
|
||||||
|
|
||||||
if "meta" not in doc or "rules" not in doc:
|
if "meta" not in doc or "rules" not in doc:
|
||||||
logger.error("doesn't appear to be a capa report")
|
logger.error("doesn't appear to be a capa report")
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
# in IDA 7.4, the MD5 hash may be truncated, for example:
|
# in IDA 7.4, the MD5 hash may be truncated, for example:
|
||||||
# wanted: 84882c9d43e23d63b82004fae74ebb61
|
# wanted: 84882c9d43e23d63b82004fae74ebb61
|
||||||
# found: b'84882C9D43E23D63B82004FAE74EBB6\x00'
|
# found: b'84882C9D43E23D63B82004FAE74EBB6\x00'
|
||||||
#
|
#
|
||||||
# see: https://github.com/idapython/bin/issues/11
|
# see: https://github.com/idapython/bin/issues/11
|
||||||
a = doc["meta"]["sample"]["md5"].lower()
|
a = doc["meta"]["sample"]["md5"].lower()
|
||||||
b = idautils.GetInputFileMD5().decode("ascii").lower().rstrip("\x00")
|
b = idautils.GetInputFileMD5().decode("ascii").lower().rstrip("\x00")
|
||||||
if not a.startswith(b):
|
if not a.startswith(b):
|
||||||
logger.error("sample mismatch")
|
logger.error("sample mismatch")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for rule in doc["rules"].values():
|
for rule in doc["rules"].values():
|
||||||
if rule["meta"].get("lib"):
|
if rule["meta"].get("lib"):
|
||||||
continue
|
continue
|
||||||
if rule["meta"].get("capa/subscope"):
|
if rule["meta"].get("capa/subscope"):
|
||||||
continue
|
continue
|
||||||
if rule["meta"]["scope"] != "function":
|
if rule["meta"]["scope"] != "function":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = rule["meta"]["name"]
|
name = rule["meta"]["name"]
|
||||||
ns = rule["meta"].get("namespace", "")
|
ns = rule["meta"].get("namespace", "")
|
||||||
for va in rule["matches"].keys():
|
for va in rule["matches"].keys():
|
||||||
va = int(va)
|
va = int(va)
|
||||||
rows.append((ns, name, va))
|
rows.append((ns, name, va))
|
||||||
|
|
||||||
# order by (namespace, name) so that like things show up together
|
# order by (namespace, name) so that like things show up together
|
||||||
rows = sorted(rows)
|
rows = sorted(rows)
|
||||||
for ns, name, va in rows:
|
for ns, name, va in rows:
|
||||||
if ns:
|
if ns:
|
||||||
cmt = "%s (%s)" % (name, ns)
|
cmt = "%s (%s)" % (name, ns)
|
||||||
else:
|
else:
|
||||||
cmt = "%s" % (name,)
|
cmt = "%s" % (name,)
|
||||||
|
|
||||||
logger.info("0x%x: %s", va, cmt)
|
logger.info("0x%x: %s", va, cmt)
|
||||||
try:
|
try:
|
||||||
# message will look something like:
|
# message will look something like:
|
||||||
#
|
#
|
||||||
# capa: delete service (host-interaction/service/delete)
|
# capa: delete service (host-interaction/service/delete)
|
||||||
append_func_cmt(va, "capa: " + cmt, repeatable=False)
|
append_func_cmt(va, "capa: " + cmt, repeatable=False)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info("ok")
|
logger.info("ok")
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user