Skip to content

Instantly share code, notes, and snippets.

@cetfor
Last active February 17, 2020 01:00
Show Gist options
  • Save cetfor/bb626d34e50063c5cfd53aba76640d70 to your computer and use it in GitHub Desktop.
Save cetfor/bb626d34e50063c5cfd53aba76640d70 to your computer and use it in GitHub Desktop.
import binaryninja
import networkx as nx
target = "cwe369B_ARM32"
RETURN_MAP = {
'atoi': 0,
}
DIVISION_WRAPPERS = {
# name, denominator argument zero-indexed
"__aeabi_idiv": 1,
"__aeabi_uidiv": 1,
}
target_operations = [
binaryninja.MediumLevelILOperation.MLIL_DIVS,
binaryninja.MediumLevelILOperation.MLIL_DIVS_DP,
binaryninja.MediumLevelILOperation.MLIL_DIVU,
binaryninja.MediumLevelILOperation.MLIL_DIVU_DP,
binaryninja.MediumLevelILOperation.MLIL_FDIV,
binaryninja.MediumLevelILOperation.MLIL_CALL_SSA,
]
def get_function_name_from_address(bv, instr):
if instr.operation == binaryninja.MediumLevelILOperation.MLIL_CALL_SSA:
try:
addr = instr.operands[1].value.value # [<mem>, <func_addr>, [<parameters>]]
for func in bv.functions:
if func.start == addr:
return func.name
except AttributeError:
pass # likely a dynamic call to a register/variable
return None
def build_symbol_graph(bv, func):
graph = nx.DiGraph()
for func in bv.functions:
for block in func.medium_level_il.ssa_form:
for instr in block:
# process MLIL_SET_VAR_SSA and MLIL_VAR_PHI operations
if instr.operation in [binaryninja.MediumLevelILOperation.MLIL_SET_VAR_SSA, binaryninja.MediumLevelILOperation.MLIL_VAR_PHI]:
try:
for var_written in instr.vars_written:
vw_str = "{}#{}".format(var_written.var, int(var_written.version))
for var_read in instr.vars_read:
vr_str = "{}#{}".format(var_read.var, int(var_read.version))
graph.add_edge(vr_str, vw_str)
if instr.src.operation == binaryninja.MediumLevelILOperation.MLIL_CONST:
graph.add_edge(str(instr.src.value.value), vw_str)
except AttributeError as e:
pass
# MLIL_CALL_SSA
if instr.operation in [binaryninja.MediumLevelILOperation.MLIL_CALL_SSA]:
func_name = get_function_name_from_address(bv, instr)
if func_name in RETURN_MAP:
value = RETURN_MAP[func_name]
for var_written in instr.vars_written:
vw_str = "{}#{}".format(var_written.var, int(var_written.version))
graph.add_edge(str(value), vw_str)
return graph
def find_all_paths_from(graph, symbol):
all_paths = []
roots = (v for v, d in graph.in_degree() if d == 0)
for root in roots:
paths = nx.all_simple_paths(graph, root, symbol)
while paths:
try:
all_paths.append(next(paths))
return all_paths
except StopIteration:
break
print("Analyzing file: {}".format(target))
bv = binaryninja.BinaryViewType.get_view_of_file(target)
bv.add_analysis_option('linearsweep')
alert_count = 0
for func in bv.functions:
if func.name != "main": continue
print("Function: {}".format(func.name))
for block in func.medium_level_il.ssa_form:
for instr in block:
for op in instr.postfix_operands:
if op in target_operations:
denom_str = None
if op == binaryninja.MediumLevelILOperation.MLIL_CALL_SSA:
fname = get_function_name_from_address(bv, instr)
if fname in DIVISION_WRAPPERS:
denom_index = DIVISION_WRAPPERS[fname]
denom_str = str(instr.params[denom_index])
else:
try:
denom = instr.vars_read[-1]
denom_str = "{}#{}".format(denom.var, denom.version)
except IndexError:
continue
if denom_str:
sg = build_symbol_graph(bv, func)
paths_from = find_all_paths_from(sg, denom_str)
for pf in paths_from:
if pf[0] == '0':
alert_count += 1
print("\n[ALERT {}]: Possible divide by zero detected.".format(alert_count))
print(" Function: {}".format(func.name))
print(" Index: {}".format(instr.instr_index))
print(" Address: {}".format(hex(instr.address).rstrip("L")))
print(" Operation: {}".format(instr.operation.name))
print(" Instruction: {}".format(instr))
print(" Variable: {}".format(denom_str))
print(" Chain: {}".format(pf[::-1]))
print("\n>> CWE-369 analyzer found {} issues.".format(alert_count))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment