summaryrefslogtreecommitdiff
path: root/suite/testcommon.py
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2024-06-14 12:20:46 -0400
committerMason Reed <mason@vector35.com>2024-06-14 12:20:46 -0400
commit3c58f74fe8e8bb9029cfe560a836303a2ce3bbdb (patch)
tree8566be9997805944141e3c745c26fdbe4bc07b3e /suite/testcommon.py
parentf4eb81635006d8bbfe1a4a7550d62ae3163117a3 (diff)
Delete unmaintained test suite
Diffstat (limited to 'suite/testcommon.py')
-rw-r--r--suite/testcommon.py2306
1 files changed, 0 insertions, 2306 deletions
diff --git a/suite/testcommon.py b/suite/testcommon.py
deleted file mode 100644
index d0271588..00000000
--- a/suite/testcommon.py
+++ /dev/null
@@ -1,2306 +0,0 @@
-import tempfile
-import os
-import sys
-import zipfile
-import inspect
-from platform import system
-import binaryninja as binja
-from binaryninja.binaryview import BinaryViewType, BinaryView
-from binaryninja.filemetadata import FileMetadata
-from binaryninja.datarender import DataRenderer
-from binaryninja.function import InstructionTextToken, DisassemblyTextLine
-from binaryninja.enums import InstructionTextTokenType, FindFlag,\
- FunctionGraphType, NamedTypeReferenceClass, ReferenceType, SegmentFlag, SectionSemantics
-from binaryninja.types import (Type, BoolWithConfidence, EnumerationBuilder, NamedTypeReferenceBuilder
- EnumerationBuilder, NamedTypeReferenceBuilder)
-import subprocess
-import re
-
-
-# Alright so this one is here for Binja functions that output <in set([blah, blah, blah])>
-def fixSet(string):
- # Apply regular expression
- splitList = (re.split(r"((?<=<in set\(\[).*(?=\]\)>))", string))
- if len(splitList) > 1:
- return splitList[0] + ', '.join(sorted(splitList[1].split(', '))) + splitList[2]
- else:
- return string
-
-
-def fixStrRepr(string):
- # Python 2 and Python 3 represent Unicode character reprs differently
- return string.replace(b"\xe2\x80\xa6".decode("utf8"), "\\xe2\\x80\\xa6")
-
-def get_file_list(test_store_rel):
- test_store = os.path.join(os.path.dirname(__file__), test_store_rel)
- all_files = []
- for root, _, files in os.walk(test_store):
- for file in files:
- all_files.append(os.path.join(root, file))
- return all_files
-
-def remove_low_confidence(type_string):
- low_confidence_types = ["int32_t", "void"]
- for lct in low_confidence_types:
- type_string = type_string.replace(lct + " ", '') # done to resolve confidence ties
- return type_string
-
-class Builder(object):
- def __init__(self, test_store):
- self.test_store = test_store
- # binja.log.log_to_stdout(binja.LogLevel.DebugLog) # Uncomment for more info
-
- def methods(self):
- methodnames = []
- for methodname, _ in inspect.getmembers(self, predicate=inspect.ismethod):
- if methodname.startswith("test_"):
- methodnames.append(methodname)
- return methodnames
-
- def unpackage_file(self, filename):
- path = os.path.join(os.path.dirname(__file__), self.test_store, filename)
- if not os.path.exists(path):
- with zipfile.ZipFile(path + ".zip", "r") as zf:
- zf.extractall(path = os.path.dirname(__file__))
- assert os.path.exists(path)
- return os.path.relpath(path)
-
- def delete_package(self, filename):
- path = os.path.join(os.path.dirname(__file__), self.test_store, filename)
- os.unlink(path)
-
-class BinaryViewTestBuilder(Builder):
- """ The BinaryViewTestBuilder is for test that are verified against a binary.
- The tests are first run on your dev machine to base line then run again
- on the build machine to verify they are correct.
-
- - Function that are tests should start with 'test_'
- - Function doc string used as 'on error' message
- - Should return: list of strings
- """
- def __init__(self, filename, options=None):
- self.filename = os.path.join(os.path.dirname(__file__), filename)
- _bv = binja.load(self.filename, options=options)
- assert _bv is not None, f"{filename} is not an executable format"
- self.bv = _bv
-
- @classmethod
- def get_root_directory(cls):
- return os.path.dirname(__file__)
-
- def test_available_types(self):
- """Available types don't match"""
- bv = BinaryView(FileMetadata()).open(self.filename)
- assert bv is not None
- return ["Available Type: " + x.name for x in bv.available_view_types]
-
- def test_function_starts(self):
- """Function starts list doesn't match"""
- result = []
- for x in self.bv.functions:
- result.append("Function start: " + hex(x.start))
- return result
-
- def test_function_symbol_names(self):
- """Function.symbol.name list doesnt' match"""
- result = []
- for x in self.bv.functions:
- result.append("Symbol: " + x.symbol.name + ' ' + str(x.symbol.type) + ' ' + hex(x.symbol.address) + ' ' + str(x.symbol.namespace))
- return result
-
- def test_function_can_return(self):
- """Function.can_return list doesn't match"""
- result = []
- for x in self.bv.functions:
- result.append("function name: " + x.symbol.name + ' type: ' + str(x.symbol.type) + ' address: ' + hex(x.symbol.address) + ' can_return: ' + str(bool(x.can_return)))
- return result
-
- def test_function_basic_blocks(self):
- """Function basic_block list doesn't match (start, end, has_undetermined_outgoing_edges)"""
- bblist = []
- for func in self.bv.functions:
- for bb in func.basic_blocks:
- bblist.append(f"basic block {bb} start: {bb.start:#x} end: {bb.end:#x} undetermined outgoing edges: {bb.has_undetermined_outgoing_edges} incoming edges: {bb.incoming_edges} outgoing edges: {bb.outgoing_edges}")
- for anno in func.get_block_annotations(bb.start):
- bblist.append(f"basic block {bb} function annotation: {anno}")
- bblist.append(f"basic block {bb} test get self: {func.get_basic_block_at(bb.start)}")
- return bblist
-
- def test_function_low_il_basic_blocks(self):
- """Function low_il_basic_block list doesn't match"""
- ilbblist = []
- for func in self.bv.functions:
- for bb in func.low_level_il.basic_blocks:
- ilbblist.append("LLIL basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' outgoing edges: ' + str(len(bb.outgoing_edges)))
- return ilbblist
-
- def test_function_med_il_basic_blocks(self):
- """Function med_il_basic_block list doesn't match"""
- ilbblist = []
- for func in self.bv.functions:
- for bb in func.mlil.basic_blocks:
- ilbblist.append("MLIL basic block {} start: ".format(str(bb)) + hex(bb.start) + ' end: ' + hex(bb.end) + ' outgoing_edges: ' + str(len(bb.outgoing_edges)))
- return ilbblist
-
- def test_symbols(self):
- """Symbols list doesn't match"""
- return ["Symbol: " + str(i) for i in sorted(self.bv.symbols)]
-
- def test_symbol_namespaces(self):
- """Symbol namespaces don't match"""
- return self.bv.namespaces
-
- def test_internal_external_namespaces(self):
- """Symbol namespaces don't match"""
- return [BinaryView.internal_namespace(), BinaryView.external_namespace()]
-
- def test_strings(self):
- """Strings list doesn't match"""
- return ["String: " + str(x.value) + ' type: ' + str(x.type) + ' at: ' + hex(x.start) for x in self.bv.strings]
-
- def test_low_il_instructions(self):
- """LLIL instructions produced different output"""
- retinfo = []
- for func in self.bv.functions:
- for bb in func.low_level_il.basic_blocks:
- for ins in bb:
- retinfo.append("Function: {:x} Instruction: {:x} ADDR->LiftedILS: {}".format(func.start, ins.address, str(sorted(list(map(str, func.get_lifted_ils_at(ins.address)))))))
- retinfo.append("Function: {:x} Instruction: {:x} ADDR->LLILS: {}".format(func.start, ins.address, str(sorted(list(map(str, func.get_llils_at(ins.address)))))))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL->MLIL: {}".format(func.start, ins.address, str(ins.mlil)))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL->MLILS: {}".format(func.start, ins.address, str(sorted(list(map(str, ins.mlils))))))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL->HLIL: {}".format(func.start, ins.address, str(ins.hlil)))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL->HLILS: {}".format(func.start, ins.address, str(sorted(list(map(str, ins.hlils))))))
- retinfo.append("Function: {:x} Instruction: {:x} Mapped MLIL: {}".format(func.start, ins.address, str(ins.mapped_medium_level_il)))
- retinfo.append("Function: {:x} Instruction: {:x} Value: {}".format(func.start, ins.address, str(ins.value)))
- retinfo.append("Function: {:x} Instruction: {:x} Possible Values: {}".format(func.start, ins.address, str(ins.possible_values)))
-
- prefixList = []
- for i in ins.prefix_operands:
- if isinstance(i, dict):
- contents = []
- for j in sorted(i.keys()):
- contents.append((j, i[j]))
- prefixList.append(str(contents))
- else:
- prefixList.append(i)
- retinfo.append("Function: {:x} Instruction: {:x} Prefix operands: {}".format(func.start, ins.address, fixStrRepr(str(prefixList))))
-
- postfixList = []
- for i in ins.postfix_operands:
- if isinstance(i, dict):
- contents = []
- for j in sorted(i.keys()):
- contents.append((j, i[j]))
- postfixList.append(str(contents))
- else:
- postfixList.append(i)
- retinfo.append("Function: {:x} Instruction: {:x} Postfix operands: {}".format(func.start, ins.address, fixStrRepr(str(postfixList))))
-
- retinfo.append("Function: {:x} Instruction: {:x} SSA form: {}".format(func.start, ins.address, str(ins.ssa_form)))
- retinfo.append("Function: {:x} Instruction: {:x} Non-SSA form: {}".format(func.start, ins.address, str(ins.non_ssa_form)))
- return retinfo
-
- def test_low_il_ssa(self):
- """LLIL ssa produced different output"""
- retinfo = []
- for func in self.bv.functions:
- func = func.low_level_il
- arch = self.bv.arch
- assert arch is not None, "Architecture is None"
- source_function = func.source_function
- assert source_function is not None, "source_function is None"
- for reg_name in sorted(arch.regs):
- reg = binja.SSARegister(reg_name, 1)
- retinfo.append("Function: {:x} Reg {} SSA definition: {}".format(source_function.start, reg_name, str(getattr(func.get_ssa_reg_definition(reg), 'instr_index', None))))
- retinfo.append("Function: {:x} Reg {} SSA uses: {}".format(source_function.start, reg_name, str(list(map(lambda instr: instr.instr_index, func.get_ssa_reg_uses(reg))))))
- retinfo.append("Function: {:x} Reg {} SSA value: {}".format(source_function.start, reg_name, str(func.get_ssa_reg_value(reg))))
- for flag_name in sorted(arch.flags):
- flag = binja.SSAFlag(flag_name, 1)
- retinfo.append("Function: {:x} Flag {} SSA uses: {}".format(source_function.start, flag_name, str(list(map(lambda instr: instr.instr_index, func.get_ssa_flag_uses(flag))))))
- retinfo.append("Function: {:x} Flag {} SSA value: {}".format(source_function.start, flag_name, str(func.get_ssa_flag_value(flag))))
- for bb in func.basic_blocks:
- for ins in bb:
- tempind = func.get_non_ssa_instruction_index(ins.instr_index)
- retinfo.append("Function: {:x} Instruction: {:x} Non-SSA instruction index: {}".format(source_function.start, ins.address, str(tempind)))
- retinfo.append("Function: {:x} Instruction: {:x} SSA instruction index: {}".format(source_function.start, ins.address, str(func.get_ssa_instruction_index(tempind))))
- retinfo.append("Function: {:x} Instruction: {:x} MLIL instruction index: {}".format(source_function.start, ins.address, str(func.get_medium_level_il_instruction_index(ins.instr_index))))
- retinfo.append("Function: {:x} Instruction: {:x} Mapped MLIL instruction index: {}".format(source_function.start, ins.address, str(func.get_mapped_medium_level_il_instruction_index(ins.instr_index))))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL_SSA->MLIL: {}".format(source_function.start, ins.address, str(ins.mlil)))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL_SSA->MLILS: {}".format(source_function.start, ins.address, str(sorted(list(map(str, ins.mlils))))))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL_SSA->HLIL: {}".format(source_function.start, ins.address, str(ins.hlil)))
- retinfo.append("Function: {:x} Instruction: {:x} LLIL_SSA->HLILS: {}".format(source_function.start, ins.address, str(sorted(list(map(str, ins.hlils))))))
- return retinfo
-
- def test_med_il_instructions(self):
- """MLIL instructions produced different output"""
- retinfo = []
- for func in self.bv.functions:
- for bb in func.mlil.basic_blocks:
- for ins in bb:
- retinfo.append("Function: {:x} Instruction: {:x} Expression type: {}".format(func.start, ins.address, str(ins.expr_type)))
- retinfo.append("Function: {:x} Instruction: {:x} MLIL->LLIL: {}".format(func.start, ins.address, str(ins.llil)))
- retinfo.append("Function: {:x} Instruction: {:x} MLIL->LLILS: {}".format(func.start, ins.address, str(sorted(list(map(str, ins.llils))))))
- retinfo.append("Function: {:x} Instruction: {:x} MLIL->HLIL: {}".format(func.start, ins.address, str(ins.hlil)))
- retinfo.append("Function: {:x} Instruction: {:x} MLIL->HLILS: {}".format(func.start, ins.address, str(sorted(list(map(str, ins.hlils))))))
- retinfo.append("Function: {:x} Instruction: {:x} Value: {}".format(func.start, ins.address, str(ins.value)))
- retinfo.append("Function: {:x} Instruction: {:x} Possible values: {}".format(func.start, ins.address, str(ins.possible_values)))
- retinfo.append("Function: {:x} Instruction: {:x} Branch dependence: {}".format(func.start, ins.address, str(sorted(ins.branch_dependence.items()))))
-
- prefixList = []
- for i in ins.prefix_operands:
- if isinstance(i, float) and 'e' in str(i):
- prefixList.append(str(round(i, 21)))
- elif isinstance(i, float):
- prefixList.append(str(round(i, 11)))
- elif isinstance(i, dict):
- contents = []
- for j in sorted(i.keys()):
- contents.append((j, i[j]))
- prefixList.append(str(contents))
- else:
- prefixList.append(str(i))
- retinfo.append("Function: {:x} Instruction: {:x} Prefix operands: {}".format(func.start, ins.address, fixStrRepr(str(sorted(prefixList)))))
- postfixList = []
- for i in ins.postfix_operands:
- if isinstance(i, float) and 'e' in str(i):
- postfixList.append(str(round(i, 21)))
- elif isinstance(i, float):
- postfixList.append(str(round(i, 11)))
- elif isinstance(i, dict):
- contents = []
- for j in sorted(i.keys()):
- contents.append((j, i[j]))
- postfixList.append(str(contents))
- else:
- postfixList.append(str(i))
-
- retinfo.append("Function: {:x} Instruction: {:x} Postfix operands: {}".format(func.start, ins.address, fixStrRepr(str(sorted(postfixList)))))
- retinfo.append("Function: {:x} Instruction: {:x} SSA form: {}".format(func.start, ins.address, str(ins.ssa_form)))
- retinfo.append("Function: {:x} Instruction: {:x} Non-SSA form: {}".format(func.start, ins.address, str(ins.non_ssa_form)))
- return retinfo
-
- def test_med_il_vars(self):
- """Function med_il_vars doesn't match"""
- varlist = []
- for func in self.bv.functions:
- func = func.mlil
- for bb in func.basic_blocks:
- for instruction in bb:
- instruction = instruction.ssa_form
- for var in (instruction.vars_read + instruction.vars_written):
- if hasattr(var, "var"):
- varlist.append(f"Function: {func.source_function.start:x} Instruction {instruction.address:x} SSA var definition: {getattr(func.get_ssa_var_definition(var), 'instr_index', None)}")
- varlist.append(f"Function: {func.source_function.start:x} Instruction {instruction.address:x} SSA var uses: {list(map(lambda instr: instr.instr_index, func.get_ssa_var_uses(var)))}")
- varlist.append(f"Function: {func.source_function.start:x} Instruction {instruction.address:x} SSA var value: {func.get_ssa_var_value(var)}")
- varlist.append(f"Function: {func.source_function.start:x} Instruction {instruction.address:x} SSA var possible values: {fixSet(str(instruction.get_ssa_var_possible_values(var)))}")
- varlist.append(f"Function: {func.source_function.start:x} Instruction {instruction.address:x} SSA var version: {instruction.get_ssa_var_version(var.var)}")
- return varlist
-
- def test_function_stack(self):
- """Function stack produced different output"""
- funcinfo = []
- for func in self.bv.functions:
- for i, var in enumerate(func.stack_layout):
- funcinfo.append(f"Function: {func.start:x} Stack position {i}: {var}")
-
- funcinfo.append(f"Function: {func.start:x} Stack adjustment: {func.stack_adjustment.value}")
- funcinfo.append(f"Function: {func.start:x} Register stack adjustment: {[v.value for v in func.reg_stack_adjustments.values()]}")
-
- func.stack_adjustment = func.stack_adjustment
- func.reg_stack_adjustments = func.reg_stack_adjustments
- func.create_user_stack_var(0, binja.Type.int(4), "testuservar")
- # The following test has been commented as it leads to non-deterministic test results
- # This is likely due to an extra update coming along afterward and removing sometimes
- # This test would need to be conducted in an analysis pass to be consistent and accurate
- # func.create_auto_stack_var(4, binja.Type.int(4), "testautovar")
-
-
-
- funcinfo.append(f"Function: {func.start:x} Stack content sample: {func.get_stack_contents_at(func.start + 0x10, 0, 0x10)}")
- funcinfo.append(f"Function: {func.start:x} Stack content range sample: {func.get_stack_contents_after(func.start + 0x10, 0, 0x10)}")
- funcinfo.append(f"Function: {func.start:x} Sample stack var: {func.get_stack_var_at_frame_offset(0, 0)}")
- func.delete_user_stack_var(0)
- func.delete_auto_stack_var(0)
- return funcinfo
-
- def test_function_llil(self):
- """Function LLIL produced different output"""
- retinfo = []
- for func in self.bv.functions:
- for llil_bb in func.llil_basic_blocks:
- retinfo.append(f"Function: {func.start:x} LLIL basic block: {llil_bb}")
- for llil_ins in func.llil.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {llil_ins.address:x} LLIL instruction: {llil_ins}")
- for mlil_bb in func.mlil_basic_blocks:
- retinfo.append(f"Function: {func.start:x} MLIL basic block: {mlil_bb}")
- for mlil_ins in func.mlil.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {mlil_ins.address:x} MLIL instruction: {mlil_ins}")
- for hlil_ins in func.hlil.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {hlil_ins.address:x} HLIL instruction: {hlil_ins}")
- for ins in func.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {ins[1]:#x}: {''.join([str(i) for i in ins[0]])}")
- return retinfo
-
- def test_function_hlil(self):
- """Function HLIL produced different output"""
- retinfo = []
- for func in self.bv.functions:
- if func.hlil is None or func.hlil.root is None:
- continue
- for line in func.hlil.root.lines:
- retinfo.append(f"Function: {func.start:x} HLIL line: {line}")
- for hlilins in func.hlil.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->LLIL instruction: {str(hlilins.llil)}")
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->MLIL instruction: {str(hlilins.mlil)}")
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->MLILS instruction: {str(sorted(list(map(str, hlilins.mlils))))}")
- return retinfo
-
- def test_function_type(self):
- """Function types don't match"""
- retinfo = []
- for func in self.bv.functions:
- if func.hlil is None or func.hlil.root is None:
- continue
- retinfo.append(f"Function: {func.start:x} Type: {func.function_type}")
- return retinfo
-
- def test_functions_attributes(self):
- """Function attributes don't match"""
- funcinfo = []
- for func in self.bv.functions:
- func.comment = "testcomment " + func.name
- func.name = func.name
- func.can_return = func.can_return
- func.function_type = func.function_type
- func.return_type = func.return_type
- func.return_regs = func.return_regs
- func.calling_convention = func.calling_convention
- func.parameter_vars = func.parameter_vars
- func.has_variable_arguments = func.has_variable_arguments
- func.analysis_skipped = func.analysis_skipped
- func.clobbered_regs = func.clobbered_regs
- func.set_user_instr_highlight(func.start, binja.highlight.HighlightColor(red=0xff, blue=0xff, green=0))
- func.set_auto_instr_highlight(func.start, binja.highlight.HighlightColor(red=0xff, blue=0xfe, green=0))
-
- for var in func.vars:
- funcinfo.append("Function {} var: ".format(func.name) + str(var))
-
- for (arch, addr, tag) in func.address_tags:
- funcinfo.append("Function {} tag at ({}, {:x}): ".format(func.name, arch.name, addr) + str(tag))
- for tag in func.function_tags:
- funcinfo.append("Function {} tag: ".format(func.name) + str(tag))
-
- for branch in func.indirect_branches:
- funcinfo.append("Function {} indirect branch: ".format(func.name) + str(branch))
- funcinfo.append("Function {} session data: ".format(func.name) + str(func.session_data))
- funcinfo.append("Function {} analysis perf length: ".format(func.name) + str(len(func.analysis_performance_info)))
- for cr in func.clobbered_regs:
- funcinfo.append("Function {} clobbered reg: ".format(func.name) + str(cr))
- funcinfo.append("Function {} explicitly defined type: ".format(func.name) + str(func.explicitly_defined_type))
- funcinfo.append("Function {} needs update: ".format(func.name) + str(func.needs_update))
- funcinfo.append("Function {} global pointer value: ".format(func.name) + str(func.global_pointer_value))
- funcinfo.append("Function {} comment: ".format(func.name) + str(func.comment))
- funcinfo.append("Function {} too large: ".format(func.name) + str(func.too_large))
- funcinfo.append("Function {} analysis skipped: ".format(func.name) + str(func.analysis_skipped))
- funcinfo.append("Function {} first ins LLIL: ".format(func.name) + str(func.get_low_level_il_at(func.start)))
- funcinfo.append("Function {} LLIL exit test: ".format(func.name) + str(func.get_low_level_il_exits_at(func.start+0x100)))
- funcinfo.append("Function {} regs read test: ".format(func.name) + str(func.get_regs_read_by(func.start)))
- funcinfo.append("Function {} regs written test: ".format(func.name) + str(func.get_regs_written_by(func.start)))
- funcinfo.append("Function {} stack var test: ".format(func.name) + str(func.get_stack_vars_referenced_by(func.start)))
- funcinfo.append("Function {} constant reference test: ".format(func.name) + str(func.get_constants_referenced_by(func.start)))
- funcinfo.append("Function {} first ins lifted IL: ".format(func.name) + str(func.get_lifted_il_at(func.start)))
- funcinfo.append("Function {} flags read by lifted IL ins: ".format(func.name) + str(func.get_flags_read_by_lifted_il_instruction(0)))
- funcinfo.append("Function {} flags written by lifted IL ins: ".format(func.name) + str(func.get_flags_written_by_lifted_il_instruction(0)))
- funcinfo.append("Function {} create graph: ".format(func.name) + str(func.create_graph()))
- funcinfo.append("Function {} indirect branches test: ".format(func.name) + str(func.get_indirect_branches_at(func.start+0x10)))
- funcinfo.append("Function {} test instr highlight: ".format(func.name) + str(func.get_instr_highlight(func.start)))
- for token in func.get_type_tokens():
- token = str(token)
- token = remove_low_confidence(token)
- funcinfo.append("Function {} type token: ".format(func.name) + str(token))
- return funcinfo
-
- def test_BinaryView(self):
- """BinaryView produced different results"""
- retinfo = []
-
- for type in sorted([str(i) for i in self.bv.types.items()]):
- retinfo.append(f"BV Type: {type}")
- for segment in sorted([str(i) for i in self.bv.segments]):
- retinfo.append(f"BV segment: {segment}")
- for section in sorted(self.bv.sections):
- retinfo.append(f"BV section: {section}")
- for allrange in self.bv.allocated_ranges:
- retinfo.append(f"BV allocated range: {allrange}")
- retinfo.append(f"Session Data: {self.bv.session_data}")
- for (addr, tag) in self.bv.data_tags:
- retinfo.append(f"BV tag: {addr:x} {repr(tag)}")
- for tag_type in self.bv.tag_types:
- retinfo.append(f"BV tag type: {repr(tag_type)}")
- vars = self.bv.data_vars
- for addr in sorted(vars.keys()):
- retinfo.append(f"BV data var: {vars[addr]}")
- retinfo.append(f"BV Entry function: {repr(self.bv.entry_function)}")
- for i in self.bv:
- retinfo.append(f"BV function: {repr(i)}")
- retinfo.append(f"BV entry point: {self.bv.entry_point:#x}")
- retinfo.append(f"BV start: {self.bv.start:#x}")
- retinfo.append(f"BV length: {len(self.bv):#x}")
-
- return retinfo
-
- def test_dominators(self):
- """Dominators don't match oracle"""
- retinfo = []
- for func in self.bv.functions:
- for bb in func:
- for dom in sorted(bb.dominators, key=lambda x: x.start):
- retinfo.append("Dominator: %x of %x" % (dom.start, bb.start))
- for pdom in sorted(bb.post_dominators, key=lambda x: x.start):
- retinfo.append("PostDominator: %x of %x" % (pdom.start, bb.start))
- return retinfo
-
- def test_liveness(self):
- """Liveness results don't match oracle"""
- retinfo1 = []
- retinfo2 = []
- for hlil in self.bv.hlil_functions():
- vars = hlil.vars
- hlil_ssa = hlil.ssa_form
- ssa_vars = hlil_ssa.ssa_vars
- name = hlil.source_function.name
- for instr_index in range(0, len(hlil)):
- for var in vars:
- retinfo1.append(f"{name}-hlil@{instr_index}: {hlil.is_var_live_at(var, binja.highlevelil.InstructionIndex(instr_index))}")
- for instr_index in range(0, len(hlil_ssa)):
- for var in ssa_vars:
- retinfo2.append(f"{name}-hlil-ssa@{instr_index}: {hlil_ssa.is_ssa_var_live_at(var, binja.highlevelil.InstructionIndex(instr_index))}")
-
- return retinfo1 + retinfo2
-
-
-class TestBuilder(Builder):
- """ The TestBuilder is for tests that need to be checked against a
- stored oracle data that isn't from a binary. These test are
- generated on your local machine then run again on the build
- machine to verify correctness.
-
- - Function that are tests should start with 'test_'
- - Function doc string used as 'on error' message
- - Should return: list of strings
- """
-
- def test_BinaryViewType_list(self):
- """BinaryViewType list doesn't match"""
- return ["BinaryViewType: " + x.name for x in binja.BinaryViewType]
-
- def test_deprecated_BinaryViewType(self):
- """deprecated BinaryViewType list doesn't match"""
- file_name = self.unpackage_file("fat_macho_9arch.bndb")
- if not os.path.exists(file_name):
- return [""]
-
- view_types = []
- with binja.filemetadata.FileMetadata().open_existing_database(file_name, None) as bv:
- for view_type in bv.available_view_types:
- if view_type.is_deprecated:
- view_types.append('BinaryViewType: %s (deprecated)' % view_type.name)
- else:
- view_types.append('BinaryViewType: %s' % view_type.name)
-
- self.delete_package("fat_macho_9arch.bndb")
- return view_types
-
- def test_Architecture_list(self):
- """Architecture list doesn't match"""
- return ["Arch name: " + arch.name for arch in binja.Architecture]
-
- def test_Assemble(self):
- """unexpected assemble result"""
- result = []
-
- # success cases
- result.append(f"x86 assembly: {binja.Architecture['x86'].assemble('xor eax, eax')}")
- result.append(f"x86_64 assembly: {binja.Architecture['x86_64'].assemble('xor rax, rax')}")
- result.append(f"mips32 assembly: {binja.Architecture['mips32'].assemble('move $ra, $zero')}")
- result.append(f"armv7 assembly: {binja.Architecture['armv7'].assemble('str r2, [sp, #-0x4]!')}")
- result.append(f"aarch64 assembly: {binja.Architecture['aarch64'].assemble('mov x0, x0')}")
- result.append(f"thumb2 assembly: {binja.Architecture['thumb2'].assemble('ldr r4, [r4]')}")
- result.append(f"thumb2eb assembly: {binja.Architecture['thumb2eb'].assemble('ldr r4, [r4]')}")
-
- # fail cases
- try:
- strResult = binja.Architecture["x86"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'x86'")
- try:
- strResult = binja.Architecture["x86_64"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'x86_64'")
- try:
- strResult = binja.Architecture["mips32"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'mips32'")
- try:
- strResult = binja.Architecture["mipsel32"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'mipsel32'")
- try:
- strResult = binja.Architecture["armv7"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'armv7'")
- try:
- strResult = binja.Architecture["aarch64"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'aarch64'")
- try:
- strResult = binja.Architecture["thumb2"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'thumb2'")
- try:
- strResult = binja.Architecture["thumb2eb"].assemble("thisisnotaninstruction")
- except ValueError:
- result.append("Assemble Failed As Expected; 'thisisnotaninstruction' is not an instruction on 'thumb2eb'")
- return result
-
- def test_Architecture(self):
- """Architectures produced different results"""
- retinfo = []
-
- flag_reg0 = binja.FlagName("reg0")
- reg_reg0 = binja.RegisterName("reg0")
- reg_fwidthreg0 = binja.RegisterName("fwidth_reg0")
- stack_reg0 = binja.RegisterStackName("reg0")
- semgrp_flggrp0 = binja.SemanticGroupName("flggrp0")
- semcls_cls0 = binja.SemanticClassName("cls0")
-
- class ArchTest(binja.Architecture):
- name = "ArchTest"
- address_size = -1
- default_int_size = -1
- instr_alignment = -1
- opcode_display_length = 1
- max_instr_length = -1
- regs = {
- reg_reg0: binja.RegisterInfo(reg_reg0, 2)
- }
- stack_pointer = "reg0"
- flags = [flag_reg0, binja.FlagName("reg0_flg")]
- flag_write_types = [binja.FlagWriteTypeName("*")]
- flag_roles = {
- flag_reg0: binja.FlagRole.SpecialFlagRole,
- "reg0_flg": "SpecialFlagRole"
- }
- flags_required_for_flag_condition = {
- binja.LowLevelILFlagCondition.LLFC_UGE: [flag_reg0]
- }
- flags_written_by_flag_write_type = {
- "*": ["reg0"]
- }
- full_width_regs = {
- reg_fwidthreg0: binja.RegisterInfo(reg_fwidthreg0, 3)
- }
- reg_stacks = {
- stack_reg0: binja.RegisterStackInfo([stack_reg0], [stack_reg0], stack_reg0, 0)
- }
- semantic_flag_groups = {
- semgrp_flggrp0: 0
- }
- flags_required_by_semantic_flag_group = {
- semgrp_flggrp0: [0]
- }
- flags_required_for_semantic_flag_group = {
- semgrp_flggrp0: [flag_reg0]
- }
- semantic_flag_classes = {
- semcls_cls0: 0
- }
- # flag_conditions_for_semantic_flag_group = {
- # semgrp_flggrp0: semcls_cls0
- # }
-
- def get_instruction_text(self, data, addr):
- return [binja.InstructionTextToken(binja.InstructionTextTokenType.TextToken, "not hooked")], 1
-
- class EmptyArch(binja.Architecture):
- pass
-
- ArchTest.register()
-
- try:
- EmptyArch.register()
- assert False # Registering an empty arch should fail (this code should not be reached)
- except:
- pass
-
- at = binja.Architecture['ArchTest']
-
- assert len(at.type_libraries) == 0
-
- assert at.can_assemble == False
- assert binja.Architecture['x86'].can_assemble == True
-
- for arch in binja.Architecture:
- retinfo.append(f"Arch calling convention: {arch.calling_conventions}")
- retinfo.append(f"Arch regs: {arch.regs}")
- retinfo.append(f"Arch full width regs: {arch.full_width_regs}")
- retinfo.append(f"Arch standalone platform: {arch.standalone_platform}")
- retinfo.append(f"Arch repr: {repr(arch)}")
- retinfo.append(f"Arch endianness: {arch.endianness}")
- retinfo.append(f"Arch address size: {arch.address_size}")
- retinfo.append(f"Arch default int size: {arch.default_int_size}")
- retinfo.append(f"Arch instr alignment: {arch.instr_alignment}")
- retinfo.append(f"Arch max instr length: {arch.max_instr_length}")
- retinfo.append(f"Arch opcode display length: {arch.opcode_display_length}")
- retinfo.append(f"Arch stack pointer: {arch.stack_pointer}")
- retinfo.append(f"Arch link reg: {arch.link_reg}")
- retinfo.append(f"Arch & address: {arch.get_associated_arch_by_address(0)}")
-
- assert binja.Architecture['x86'] == binja.Architecture['x86']
- assert binja.Architecture['x86'] != binja.Architecture['x86_64']
- return retinfo
-
- def test_ArchitectureHook(self):
- class ArchTestHook(binja.ArchitectureHook):
- def get_instruction_text(self, data, addr):
- return [binja.InstructionTextToken(binja.InstructionTextTokenType.TextToken, "hooked")], 1
-
- class ArchTestHook2(binja.ArchitectureHook):
- pass
-
- at = binja.Architecture["ArchTest"]
- instr_text = at.get_instruction_text(b'\x00', 1)
-
- ArchTestHook(at).register()
- instr_text_hooked = at.get_instruction_text(b'\x00', 1)
-
- # Register empty hook
- ArchTestHook2(at).register()
-
- assert instr_text != instr_text_hooked
- assert instr_text == instr_text
-
- ath2 = ArchTestHook2(at)
- assert ath2.base_arch == at
- ath2.base_arch = binja.Architecture["x86"]
- assert ath2.base_arch == binja.Architecture["x86"]
- ath2.base_arch = at
- assert ath2.base_arch == at
- return [f"{at}"]
-
-
- def test_Function(self):
- """Function produced different result"""
- inttype = binja.Type.int(4)
- testfunction = binja.Type.function(inttype, [inttype, inttype, inttype])
- return ["Test_function params: " + str(testfunction.parameters), "Test_function pointer: " + str(testfunction.pointer(binja.Architecture["x86"], testfunction))]
-
- def test_Simplifier(self):
- """Template Simplification"""
- result = [binja.demangle.simplify_name_to_string(s) for s in [
- # Minimal exhaustive examples of simplifier (these are replicated in testcommon)
- "std::basic_string<T, std::char_traits<T>, std::allocator<T> >",
- "std::vector<T, std::allocator<T> >",
- "std::vector<T, std::allocator<T>, std::lessthan<T> >",
- "std::deque<T, std::allocator<T> >",
- "std::forward_list<T, std::allocator<T> >",
- "std::list<T, std::allocator<T> >",
- "std::stack<T, std::deque<T> >",
- "std::queue<T, std::deque<T> >",
- "std::set<T, std::less<T>, std::allocator<T> >",
- "std::multiset<T, std::less<T>, std::allocator<T> >",
- "std::map<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2> > >",
- "std::multimap<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2> > >",
- "std::unordered_set<T, std::hash<T>, std::equal_to<T>, std::allocator<T> >",
- "std::unordered_multiset<T, std::hash<T>, std::equal_to<T>, std::allocator<T> >",
- "std::unordered_map<T1, T2, std::hash<T1>, std::equal_to<T1>, std::allocator<std::pair<const T1, T2> > >",
- "std::unordered_multimap<T1, T2, std::hash<T1>, std::equal_to<T1>, std::allocator<std::pair<const T1, T2> > >",
-
- "std::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >",
- "std::basic_istringstream<char, std::char_traits<char>, std::allocator<char> >",
- "std::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >",
- "std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >",
- "std::basic_stringbuf<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >",
- "std::basic_istringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >",
- "std::basic_ostringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >",
- "std::basic_stringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >",
- "std::basic_stringbuf<T, std::char_traits<T>, std::allocator<T> >",
- "std::basic_istringstream<T, std::char_traits<T>, std::allocator<T> >",
- "std::basic_ostringstream<T, std::char_traits<T>, std::allocator<T> >",
- "std::basic_stringstream<T, std::char_traits<T>, std::allocator<T> >",
-
- "std::basic_ios<char, std::char_traits<char> >",
- "std::basic_streambuf<char, std::char_traits<char> >",
- "std::basic_istream<char, std::char_traits<char> >",
- "std::basic_ostream<char, std::char_traits<char> >",
- "std::basic_iostream<char, std::char_traits<char> >",
- "std::basic_filebuf<char, std::char_traits<char> >",
- "std::basic_ifstream<char, std::char_traits<char> >",
- "std::basic_ofstream<char, std::char_traits<char> >",
- "std::basic_fstream<char, std::char_traits<char> >",
- "std::basic_ios<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_streambuf<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_istream<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_ostream<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_iostream<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_filebuf<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_ifstream<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_ofstream<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_fstream<wchar_t, std::char_traits<wchar_t> >",
- "std::basic_ios<T, std::char_traits<T> >",
- "std::basic_streambuf<T, std::char_traits<T> >",
- "std::basic_istream<T, std::char_traits<T> >",
- "std::basic_ostream<T, std::char_traits<T> >",
- "std::basic_iostream<T, std::char_traits<T> >",
- "std::basic_filebuf<T, std::char_traits<T> >",
- "std::basic_ifstream<T, std::char_traits<T> >",
- "std::basic_ofstream<T, std::char_traits<T> >",
- "std::basic_fstream<T, std::char_traits<T> >",
-
- # The following simplifiers should probably be done as typedefs some where as they can appear both
- # as the simplified and unsimplified name in the type libraries and in mangled names
- # "std::fpos<__mbstate_t>",
- # "std::_Ios_Iostate",
- # "std::_Ios_Seekdir",
- # "std::_Ios_Openmode",
- # "std::_Ios_Fmtflags",
-
- # The following 5 entries are the simplified versions of the above so we don't have to re-generate
- # unit test results.
- "std::streampos",
- "std::ios_base::iostate",
- "std::ios_base::seekdir",
- "std::ios_base::openmode",
- "std::ios_base::fmtflags",
-
- "std::foo<T, std::char_traits<T> >",
- "std::bar<T, std::char_traits<T> >::bar",
- "std::foo<T, std::char_traits<T> >::~foo",
- "std::foo<T, std::char_traits<T> >::bar",
-
- "std::foo<bleh::T, std::char_traits<bleh::T> >",
- "std::bar<bleh::T, std::char_traits<bleh::T> >::bar",
- "std::foo<bleh::T, std::char_traits<bleh::T> >::~foo",
- "std::foo<bleh::T, std::char_traits<bleh::T> >::bar",
-
- "std::foo<foo::bleh::T, std::char_traits<foo::bleh::T> >",
- "std::bar<foo::bleh::T, std::char_traits<foo::bleh::T> >::bar",
- "std::foo<foo::bleh::T, std::char_traits<foo::bleh::T> >::~foo",
- "std::foo<foo::bleh::T, std::char_traits<foo::bleh::T> >::bar",
-
- # More complex examples:
- "AddRequiredUIPluginDependency(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)",
- "std::vector<std::vector<BinaryNinja::InstructionTextToken, std::allocator<BinaryNinja::InstructionTextToken> >, std::allocator<std::vector<BinaryNinja::InstructionTextToken, std::allocator<BinaryNinja::InstructionTextToken> > > >::_M_check_len(uint64_t, char const*) const",
- "std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::array<uint32_t, 5ul> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::array<uint32_t, 5ul> > > >::_M_default_append(uint64_t)",
- "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string",
- "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::~basic_string",
- ]]
-
- # Test all the APIs
- qName = binja.types.QualifiedName(["std", "__cxx11", "basic_string<T, std::char_traits<T>, std::allocator<T> >"])
- result.append(binja.demangle.simplify_name_to_string(qName))
- result.append(str(binja.demangle.simplify_name_to_qualified_name(qName)))
- result.append(str(binja.demangle.simplify_name_to_qualified_name(str(qName))))
- result.append(str(binja.demangle.simplify_name_to_qualified_name(str(qName), False).name))
- result.append("::".join(binja.demangle_gnu3(binja.Architecture['x86_64'], "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm", False)[1]))
- result.append("::".join(binja.demangle_gnu3(binja.Architecture['x86_64'], "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm", True)[1]))
-
- return result
-
- def test_Struct(self):
- """Struct produced different result"""
- retinfo = []
- inttype = binja.Type.int(4)
- struct = binja.TypeBuilder.structure()
- struct.insert(0, inttype)
- struct.append(inttype)
- struct.replace(0, inttype)
- struct.remove(1)
- for i in struct.members:
- retinfo.append("Struct member: " + str(i))
- retinfo.append("Struct width: " + str(struct.width))
- struct.width = 16
- retinfo.append("Struct width after adjustment: " + str(struct.width))
- retinfo.append("Struct alignment: " + str(struct.alignment))
- struct.alignment = 8
- retinfo.append("Struct alignment after adjustment: " + str(struct.alignment))
- retinfo.append("Struct packed: " + str(struct.packed))
- struct.packed = True
- retinfo.append("Struct packed after adjustment: " + str(struct.packed))
- retinfo.append("Struct type: " + str(struct.type))
- assert struct == struct, "Structs are not equal"
- assert not (struct != struct), "Structs are not not not equal"
- retinfo.append("False") # TODO Remove when regenerating this
- return retinfo
-
- def test_Enumeration(self):
- """Enumeration produced different result"""
- retinfo = []
- enum = binja.TypeBuilder.enumeration()
- enum.append("a", 1)
- enum.append("b", 2)
- enum.replace(0, "a", 2)
- enum.remove(0)
- retinfo.append(str(enum))
- retinfo.append(str((enum == enum) and not (enum != enum)))
- return retinfo
-
- def test_Types(self):
- """Types produced different result"""
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
-
- preprocessed = binja.preprocess_source("""
- #ifdef nonexistant
- int foo = 1;
- long long foo1 = 1;
- #else
- int bar = 2;
- long long bar1 = 2;
- #endif
- """)
- source = '\n'.join([i for i in preprocessed[0].split('\n') if not '#line' in i and len(i) > 0])
- typelist = bv.platform.parse_types_from_source(source)
- inttype = binja.Type.int(4)
-
- namedtype = binja.NamedTypeReferenceBuilder.create()
- tokens = inttype.get_tokens() + inttype.get_tokens_before_name() + inttype.get_tokens_after_name()
- retinfo = []
- for i in range(len(typelist.variables)):
- for j in typelist.variables.popitem():
- retinfo.append("Type: " + str(j))
- retinfo.append("Named Type: " + str(namedtype))
-
- retinfo.append("Type equality: " + str((inttype == inttype) and not (inttype != inttype)))
- return retinfo
- finally:
- self.delete_package("helloworld")
-
- def test_TypeBuilders_and_Types(self):
- """Test TypeBuilders"""
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- with binja.StructureBuilder.builder(bv, 'Foo') as s:
- s.packed = True
- s.append(Type.int(2))
- s.append(Type.int(4))
- s.append(Type.void())
- s.append(Type.bool())
- s.append(Type.char())
- s.append(Type.char("char_alt_name"))
- s.append(Type.float(2, "half"))
- s.append(Type.float(4) )
- s.append(Type.float(8))
- s.append(Type.float(16))
- s.append(Type.wide_char(4, "wchar32_t"))
- s.append(Type.structure_type(binja.StructureBuilder.create([Type.int(1)])))
- s.append(Type.named_type(NamedTypeReferenceBuilder.create(NamedTypeReferenceClass.UnknownNamedTypeClass, "id", "name")))
- s.append(Type.named_type_from_type_and_id("id2", ["qualified", "name"]))
- s.append(Type.generate_named_type_reference("guid", [b"byte", b"name"]))
- s.append(Type.enumeration_type(bv.arch, EnumerationBuilder.create([("Member1", 1)])))
- try:
- Type.pointer(None, None) # test the failure case
- except ValueError:
- pass
- s.append(Type.pointer_of_width(8, Type.int(4), BoolWithConfidence(True, 255), BoolWithConfidence(False, 255), ReferenceType.RValueReferenceType))
- s.append(Type.array(Type.int(4), 4))
- s.append(Type.structure([(Type.int(4), "field1")]))
- s.append(Type.enumeration(bv.arch, [binja.types.EnumerationMember("Mem-1", 1), binja.types.EnumerationMember("Mem-2")]))
- s.append(Type.enumeration(bv.arch, [binja.types.EnumerationMember("Mem2-1", 1), binja.types.EnumerationMember("Mem2-2")], 2))
- s.append(Type.enumeration(bv.arch, [binja.types.EnumerationMember("Mem3-1", 1), binja.types.EnumerationMember("Mem3-2")], 2, True))
- s.append(Type.enumeration(bv.arch, None))
- tid = Type.generate_auto_demangled_type_id("auto_demangled_tid")
- tid_source = Type.get_auto_demangled_type_id_source()
- s.append(Type.named_type_reference(NamedTypeReferenceClass.UnknownNamedTypeClass, "Someothername", tid, 4, 4, True, True))
- try:
- Type.int(4).name
- assert False, "trying to access name of integer succeeded when it shouldn't have"
- except NotImplementedError:
- pass
-
- members = s.members
- const = s.const
- volatile = s.volatile
- s = bv.types['Foo']
- assert members == s.members
- assert const == s.const
- assert volatile == s.volatile
- return [str(s.members)]
- finally:
- self.delete_package("helloworld")
-
- def test_Plugin_bin_info(self):
- """print_syscalls plugin produced different result"""
- file_name = self.unpackage_file("helloworld")
- try:
- bin_info_path = os.path.join(os.path.dirname(__file__), '..', 'python', 'examples', 'bin_info.py')
- if sys.platform == "win32":
- python_bin = ["py", "-3"]
- else:
- python_bin = ["python3"]
- result = subprocess.Popen(python_bin + [bin_info_path, file_name], stdout=subprocess.PIPE).communicate()[0]
- # normalize line endings and path sep
- return [line for line in result.replace(b"\\", b"/").replace(b"\r\n", b"\n").decode("charmap").split("\n")]
- finally:
- self.delete_package("helloworld")
-
- def test_linear_disassembly(self):
- """linear_disassembly produced different result"""
- file_name = self.unpackage_file("helloworld")
- try:
- bv = binja.BinaryViewType['ELF'].open(file_name)
- disass = bv.linear_disassembly
- retinfo = []
- for i in disass:
- i = str(i)
- i = remove_low_confidence(i)
- retinfo.append(i)
- return retinfo
- finally:
- self.delete_package("helloworld")
-
- def test_data_renderer(self):
- """data renderer produced different result"""
- file_name = self.unpackage_file("helloworld")
- class ElfHeaderDataRenderer(DataRenderer):
- def __init__(self):
- DataRenderer.__init__(self)
- def perform_is_valid_for_data(self, ctxt, view, addr, type, context):
- return DataRenderer.is_type_of_struct_name(type, "Elf64_Header", context)
- def perform_get_lines_for_data(self, ctxt, view, addr, type, prefix, width, context):
- prefix.append(InstructionTextToken(InstructionTextTokenType.TextToken, "I'm in ur Elf64_Header"))
- return [DisassemblyTextLine(prefix, addr)]
- def __del__(self):
- pass
- try:
- bv = binja.BinaryViewType['ELF'].open(file_name)
- ElfHeaderDataRenderer().register_type_specific()
- disass = bv.linear_disassembly
- retinfo = []
- for i in disass:
- i = str(i)
- i = remove_low_confidence(i)
- retinfo.append(i)
- return retinfo
- finally:
- self.delete_package("helloworld")
-
- # def test_partial_register_dataflow(self):
- # """partial_register_dataflow produced different results"""
- # file_name = self.unpackage_file("partial_register_dataflow")
- # result = []
- # reg_list = ['ch', 'cl', 'ah', 'edi', 'al', 'cx', 'ebp', 'ax', 'edx', 'ebx', 'esp', 'esi', 'dl', 'dh', 'di', 'bl', 'bh', 'eax', 'dx', 'bx', 'ecx', 'sp', 'si']
- # bv = binja.load(file_name)
- # for func in bv.functions:
- # llil = func.low_level_il
- # for i in range(0, llil.__len__()-1):
- # for x in reg_list:
- # result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_reg_value(x)).replace('L', ''))
- # result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_possible_reg_values(x)).replace('L', ''))
- # result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_reg_value_after(x)).replace('L', ''))
- # result.append("LLIL:" + str(i).replace('L', '') + ":" + x + ":" + str(llil[i].get_possible_reg_values_after(x)).replace('L', ''))
- # bv.file.close()
- # del bv
- # return result
-
-
- def test_low_il_stack(self):
- """LLIL stack produced different output"""
- file_name = self.unpackage_file("jumptable_reordered")
- try:
- with binja.load(file_name) as bv:
- # reg_list = ['ch', 'cl', 'ah', 'edi', 'al', 'cx', 'ebp', 'ax', 'edx', 'ebx', 'esp', 'esi', 'dl', 'dh', 'di', 'bl', 'bh', 'eax', 'dx', 'bx', 'ecx', 'sp', 'si']
- flag_list = ['c', 'p', 'a', 'z', 's', 'o']
- retinfo = []
- for func in bv.functions:
- for bb in func.low_level_il.basic_blocks:
- for ins in bb:
- retinfo.append("LLIL first stack element: " + str(ins.get_stack_contents(0,1)))
- retinfo.append("LLIL second stack element: " + str(ins.get_stack_contents_after(0,1)))
- retinfo.append("LLIL possible first stack element: " + str(ins.get_possible_stack_contents(0,1)))
- retinfo.append("LLIL possible second stack element: " + str(ins.get_possible_stack_contents_after(0,1)))
- for flag in flag_list:
- retinfo.append("LLIL flag {} value at {}: {}".format(flag, hex(ins.address), str(ins.get_flag_value(flag))))
- retinfo.append("LLIL flag {} value after {}: {}".format(flag, hex(ins.address), str(ins.get_flag_value_after(flag))))
- retinfo.append("LLIL flag {} possible value at {}: {}".format(flag, hex(ins.address), str(ins.get_possible_flag_values(flag))))
- retinfo.append("LLIL flag {} possible value after {}: {}".format(flag, hex(ins.address), str(ins.get_possible_flag_values_after(flag))))
- return retinfo
- finally:
- self.delete_package("jumptable_reordered")
-
- def test_med_il_stack(self):
- """MLIL stack produced different output"""
- file_name = self.unpackage_file("jumptable_reordered")
- try:
- with binja.load(file_name) as bv:
- reg_list = ['ch', 'cl', 'ah', 'edi', 'al', 'cx', 'ebp', 'ax', 'edx', 'ebx', 'esp', 'esi', 'dl', 'dh', 'di', 'bl', 'bh', 'eax', 'dx', 'bx', 'ecx', 'sp', 'si']
- flag_list = ['c', 'p', 'a', 'z', 's', 'o']
- retinfo = []
- for func in bv.functions:
- for bb in func.mlil.basic_blocks:
- for ins in bb:
- retinfo.append(f"MLIL stack begin var: {ins.get_var_for_stack_location(0)}")
- retinfo.append(f"MLIL first stack element: {ins.get_stack_contents(0, 1)}")
- retinfo.append(f"MLIL second stack element: {ins.get_stack_contents_after(0, 1)}")
- retinfo.append(f"MLIL possible first stack element: {ins.get_possible_stack_contents(0, 1)}")
- retinfo.append(f"MLIL possible second stack element: {ins.get_possible_stack_contents_after(0, 1)}")
-
- for reg in reg_list:
- retinfo.append(f"MLIL reg {reg} var at {ins.address:#x}: {ins.get_var_for_reg(reg)}")
- retinfo.append(f"MLIL reg {reg} value at {ins.address:#x}: {ins.get_reg_value(reg)}")
- retinfo.append(f"MLIL reg {reg} value after {ins.address:#x}: {ins.get_reg_value_after(reg)}")
- retinfo.append(f"MLIL reg {reg} possible value at {ins.address:#x}: {ins.get_possible_reg_values(reg)}")
- retinfo.append(f"MLIL reg {reg} possible value after {ins.address:#x}: {ins.get_possible_reg_values_after(reg)}")
-
- for flag in flag_list:
- retinfo.append("MLIL flag {} value at {}: {}".format(flag, hex(ins.address), str(ins.get_flag_value(flag))))
- retinfo.append("MLIL flag {} value after {}: {}".format(flag, hex(ins.address), str(ins.get_flag_value_after(flag))))
- retinfo.append("MLIL flag {} possible value at {}: {}".format(flag, hex(ins.address), fixSet(str(ins.get_possible_flag_values(flag)))))
- retinfo.append("MLIL flag {} possible value after {}: {}".format(flag, hex(ins.address), fixSet(str(ins.get_possible_flag_values(flag)))))
- return retinfo
- finally:
- self.delete_package("jumptable_reordered")
-
- def test_events(self):
- """Event failure"""
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
-
- bv.update_analysis_and_wait()
- results = []
-
- def simple_complete(self):
- results.append("analysis complete")
- _ = binja.AnalysisCompletionEvent(bv, simple_complete)
-
- class NotifyTest(binja.BinaryDataNotification):
- def data_written(self, view, offset, length):
- results.append("data written: offset {0} length {1}".format(hex(offset), hex(length)))
-
- def data_inserted(self, view, offset, length):
- results.append("data inserted: offset {0} length {1}".format(hex(offset), hex(length)))
-
- def data_removed(self, view, offset, length):
- results.append("data removed: offset {0} length {1}".format(hex(offset), hex(length)))
-
- def function_added(self, view, func):
- results.append("function added: {0}".format(func.name))
-
- def function_removed(self, view, func):
- results.append("function removed: {0}".format(func.name))
-
- def data_var_added(self, view, var):
- results.append("data var added: {0}".format(hex(var.address)))
-
- def data_var_removed(self, view, var):
- results.append("data var removed: {0}".format(hex(var.address)))
-
- def string_found(self, view, string_type, offset, length):
- results.append("string found: offset {0} length {1}".format(hex(offset), hex(length)))
-
- def string_removed(self, view, string_type, offset, length):
- results.append("string removed: offset {0} length {1}".format(hex(offset), hex(length)))
-
- def type_defined(self, view, name, type):
- results.append("type defined: {0}".format(name))
-
- def type_undefined(self, view, name, type):
- results.append("type undefined: {0}".format(name))
-
- def type_ref_changed(self, view, name, type):
- results.append("type reference changed: {0}".format(name))
-
- def type_field_ref_changed(self, view, name, offset):
- results.append("type field reference changed: {0}, offset {1}".format(name, hex(offset)))
-
- def segment_added(self, view, segment):
- results.append("segment added: {0}".format(segment))
-
- def segment_updated(self, view, segment):
- results.append("segment updated: {0}".format(segment))
-
- def segment_removed(self, view, segment):
- results.append("segment removed: {0}".format(segment))
-
- def section_added(self, view, section):
- results.append("section added: {0} {1}".format(section, section.semantics))
-
- def section_updated(self, view, section):
- results.append("section updated: {0} {1}".format(section, section.semantics))
-
- def section_removed(self, view, section):
- results.append("section removed: {0} {1}".format(section, section.semantics))
-
- test = NotifyTest()
- bv.register_notification(test)
- sacrificial_addr = 0x84fc
-
- type, name = bv.parse_type_string("int foo")
- type_id = Type.generate_auto_type_id("source", name)
-
- bv.define_type(type_id, name, type)
- bv.undefine_type(type_id)
-
- bv.update_analysis_and_wait()
-
- bv.insert(sacrificial_addr, b"AAAA")
- bv.update_analysis_and_wait()
-
- bv.define_data_var(sacrificial_addr, binja.types.Type.int(4))
- bv.update_analysis_and_wait()
-
- bv.define_data_var(sacrificial_addr + 4, "int")
- bv.update_analysis_and_wait()
-
- bv.write(sacrificial_addr, b"BBBB")
- bv.update_analysis_and_wait()
-
- bv.add_function(sacrificial_addr)
- bv.update_analysis_and_wait()
-
- bv.remove_function(bv.get_function_at(sacrificial_addr))
- bv.update_analysis_and_wait()
-
- bv.undefine_data_var(sacrificial_addr)
- bv.update_analysis_and_wait()
-
- bv.undefine_data_var(sacrificial_addr + 4)
- bv.update_analysis_and_wait()
-
- bv.remove(sacrificial_addr, 4)
- bv.update_analysis_and_wait()
-
- type, _ = bv.parse_type_string("struct { uint64_t bar; }")
- bv.define_user_type('foo', type)
- bv.define_user_type('bar', "struct { uint64_t bas; }")
- func = bv.get_function_at(0x8440)
- func.return_type = binja.Type.named_type_from_type('foo', type)
- bv.update_analysis_and_wait()
-
- bv.add_user_segment(0, 5, 0, 5, SegmentFlag.SegmentReadable)
- bv.update_analysis_and_wait()
-
- bv.add_user_segment(0, 5, 0, 5, SegmentFlag.SegmentWritable)
- bv.update_analysis_and_wait()
-
- bv.remove_user_segment(0, 5)
- bv.update_analysis_and_wait()
-
- bv.add_user_section("test_section", 0, 5, SectionSemantics.ReadOnlyDataSectionSemantics)
- bv.update_analysis_and_wait()
-
- bv.add_user_section("test_section", 0, 5, SectionSemantics.ReadWriteDataSectionSemantics)
- bv.update_analysis_and_wait()
-
- bv.remove_user_section("test_section")
- bv.update_analysis_and_wait()
-
- bv.unregister_notification(test)
-
- return sorted(results)
- finally:
- self.delete_package("helloworld")
-
- def test_type_xref(self):
- """Type xref failure"""
-
- def dump_type_xref_info(type_name, code_refs, data_refs, type_refs, offset = None):
- retinfo = []
- if offset is None:
- for ref in code_refs:
- retinfo.append('type {} is referenced by code {}'.format(type_name, ref))
- for ref in data_refs:
- retinfo.append('type {} is referenced by data {}'.format(type_name, ref))
- for ref in type_refs:
- retinfo.append('type {} is referenced by type {}'.format(type_name, ref))
- else:
- for ref in code_refs:
- retinfo.append('type field {}, offset {} is referenced by code {}'.format(type_name, hex(offset), ref))
- for ref in data_refs:
- retinfo.append('type field {}, offset {} is referenced by data {}'.format(type_name, hex(offset), ref))
- for ref in type_refs:
- retinfo.append('type field {}, offset {} is referenced by type {}'.format(type_name, hex(offset), ref))
-
- return retinfo
-
- retinfo = []
- file_name = self.unpackage_file("type_xref.bndb")
- if not os.path.exists(file_name):
- return retinfo
-
- with binja.load(file_name) as bv:
- if bv is None:
- return retinfo
-
- types = bv.types
- test_types = ['A', 'B', 'C', 'D', 'E', 'F']
- for test_type in test_types:
- code_refs = bv.get_code_refs_for_type(test_type)
- data_refs = bv.get_data_refs_for_type(test_type)
- type_refs = bv.get_type_refs_for_type(test_type)
- retinfo.extend(dump_type_xref_info(test_type, code_refs, data_refs, type_refs))
-
- t = types[test_type]
- if not t:
- continue
-
- for member in t.members:
- offset = member.offset
- code_refs = bv.get_code_refs_for_type_field(test_type, offset)
- data_refs = bv.get_data_refs_for_type_field(test_type, offset)
- type_refs = bv.get_type_refs_for_type_field(test_type, offset)
- retinfo.extend(dump_type_xref_info(test_type, code_refs, data_refs, type_refs, offset))
-
- self.delete_package("type_xref.bndb")
- return sorted(retinfo)
-
- def test_variable_xref(self):
- """Variable xref failure"""
-
- def dump_var_xref_info(var, var_refs):
- retinfo = []
- for ref in var_refs:
- retinfo.append('var {} is referenced at {}'.format(repr(var), repr(ref)))
- return retinfo
-
- retinfo = []
- file_name = self.unpackage_file("type_xref.bndb")
- if not os.path.exists(file_name):
- return retinfo
-
- with binja.load(file_name) as bv:
- if bv is None:
- return retinfo
-
- func = bv.get_function_at(0x1169)
- for var in func.vars:
- mlil_refs = func.get_mlil_var_refs(var)
- retinfo.extend(dump_var_xref_info(var, mlil_refs))
- hlil_refs = func.get_hlil_var_refs(var)
- retinfo.extend(dump_var_xref_info(var, hlil_refs))
-
- mlil_range_var_refs = func.get_mlil_var_refs_from(0x1175, 0x8c)
- for ref in mlil_range_var_refs:
- retinfo.append(f"var {ref.var} is referenced at {ref.src}")
-
- hlil_range_var_refs = func.get_hlil_var_refs_from(0x1175, 0x8c)
- for ref in hlil_range_var_refs:
- retinfo.append(f"var {ref.var} is referenced at {ref.src}")
-
- self.delete_package("type_xref.bndb")
- return sorted(retinfo)
-
- # INSANE HACK AHEAD
- # The name `test_all_search` is VERY special here. It reorders this test to
- # before the binary tests. This is EXTREMELY important to the speed of the
- # unit tests on Linux. No one knows why. There be dragons here.
- def test_all_search(self):
- """Search"""
- retinfo = []
- file_name = self.unpackage_file("type_xref.bndb")
- if not os.path.exists(file_name):
- return retinfo
-
- with binja.load(file_name) as bv:
- if bv is None:
- return retinfo
-
- for addr, match in bv.find_all_data(bv.start, bv.end, b'\xc3'):
- retinfo.append('byte 0xc3 is found at address 0x%lx with DataBuffer %s' %
- (addr, match.escape()))
-
- for addr, match, line in bv.find_all_text(bv.start, bv.end, 'test'):
- retinfo.append('text "test" is found at address 0x%lx with string %s \
- line %s' % (addr, match, line))
-
- for addr, line in bv.find_all_constant(bv.start, bv.end, 0x58):
- retinfo.append('constant 0x58 is found at address 0x%lx with line %s' %\
- (addr, line))
-
- def data_callback(addr, match):
- retinfo.append('match found at address: 0x%lx with DataBuffer %s' % (addr, match.escape()))
-
- bv.find_all_data(bv.start, bv.end, b'\xc3', FindFlag.FindCaseSensitive, None,
- data_callback)
-
- def string_callback(addr, match, line):
- retinfo.append('match found at address: 0x%lx with string %s, line %s' %\
- (addr, match, line))
-
- bv.find_all_text(bv.start, bv.end, 'test', None, FindFlag.FindCaseSensitive,
- FunctionGraphType.NormalFunctionGraph, None, string_callback)
-
- def constant_callback(addr, line):
- retinfo.append('match found at address: 0x%lx with constant 0x58, line %s'\
- % (addr, line))
-
- bv.find_all_constant(bv.start, bv.end, 0x58, None,\
- FunctionGraphType.NormalFunctionGraph, None, constant_callback)
-
- self.delete_package("type_xref.bndb")
- return sorted(retinfo)
-
- def test_auto_create_struct(self):
- """Automatically create a structure"""
- retinfo = []
- file_name = self.unpackage_file("auto_create_members.bndb")
- if not os.path.exists(file_name):
- return retinfo
-
- with binja.load(file_name) as bv:
- if bv is None:
- return retinfo
-
- test_types = ['struct_1', 'struct_2', 'struct_3']
- for test_type in test_types:
- offsets = bv.get_all_fields_referenced(test_type)
- for offset in offsets:
- retinfo.append(f'type {test_type}, offset {offset:#x} is referenced')
-
- refs = bv.get_all_sizes_referenced(test_type)
- for offset in refs:
- sizes = refs[offset]
- for size in sizes:
- retinfo.append(f'type {test_type}, offset {offset:#x} is referenced of size {size:#x}')
-
- refs = bv.get_all_types_referenced(test_type)
- for offset in refs:
- types = refs[offset]
- for refType in types:
- retinfo.append(f'type {test_type}, offset {offset:#x} is referenced of type {refType}')
-
- struct = bv.create_structure_from_offset_access(test_type)
- for member in struct.members:
- retinfo.append(f'type {test_type}, member: {member}')
-
- self.delete_package("auto_create_members.bndb")
- return sorted(retinfo)
-
- def test_hlil_arrays(self):
- """HLIL array resolution failure"""
-
- retinfo = []
- file_name = self.unpackage_file("array_test.bndb")
- if not os.path.exists(file_name):
- return retinfo
-
- with binja.load(file_name) as bv:
- if bv is None:
- return retinfo
-
- for func in bv.functions:
- for line in func.hlil.root.lines:
- retinfo.append(f"Function: {func.start:x} HLIL line: {line}")
- for hlilins in func.hlil.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->LLIL instruction: {hlilins.llil}")
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->MLIL instruction: {hlilins.mlil}")
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->MLILS instruction: {sorted(list(map(str, hlilins.mlils)))}")
-
- self.delete_package("array_test.bndb")
-
- retinfo = []
- file_name = self.unpackage_file("struct_array8.bndb")
- if not os.path.exists(file_name):
- return retinfo
-
- with binja.load(file_name) as bv:
- if bv is None:
- return retinfo
-
- for func in bv.functions:
- for line in func.hlil.root.lines:
- retinfo.append(f"Function: {func.start:x} HLIL line: {line}")
- for hlilins in func.hlil.instructions:
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->LLIL instruction: {hlilins.llil}")
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->MLIL instruction: {hlilins.mlil}")
- retinfo.append(f"Function: {func.start:x} Instruction: {hlilins.address:x} HLIL->MLILS instruction: {sorted(list(map(str, hlilins.mlils)))}")
-
- self.delete_package("struct_array8.bndb")
- return sorted(retinfo)
-
- def test_x87_uniqueness(self):
- """
- Verify fix for fmul: that different assembly strings do not disassemble the same
- Vector35/arch-x86#29
- """
- pairs = [
- ("x86", "fadd st0, st1", "fadd st1, st0"),
- ("x86", "fsub st0, st1", "fsub st1, st0"),
- ("x86", "fsubr st0, st1", "fsubr st1, st0"),
- ("x86", "fmul st0, st1", "fmul st1, st0"),
- ("x86", "fdiv st0, st1", "fdiv st1, st0"),
- ("x86", "fdivr st0, st1", "fdivr st1, st0"),
- ]
- for (arch, asm1, asm2) in pairs:
- a = binja.Architecture[arch]
- code1 = a.assemble(asm1)
- code2 = a.assemble(asm2)
- text1 = ''.join(str(t) for t in a.get_instruction_text(code1, 0)[0])
- text2 = ''.join(str(t) for t in a.get_instruction_text(code2, 0)[0])
- assert code1 != code2
- assert text1 != text2, f"{asm1} and {asm2} are different but both disassemble to {text1}"
-
- def test_merge_vars(self):
- """Variable merging produced different output"""
- file_name = self.unpackage_file("array_test.bndb")
- try:
- with binja.load(file_name) as bv:
- func = bv.get_function_at(0x100003920)
- target = None
- sources = []
- for var in func.vars:
- if var.storage == -0x758:
- target = var
- func.delete_user_var(var)
- if var.storage in [-0x760, -0x768, -0x778]:
- sources.append(var)
- func.delete_user_var(var)
-
- func.merge_vars(target, sources)
- bv.update_analysis_and_wait()
-
- retinfo = ["HLIL after merge: " + x for x in str(func.hlil).split("\n")]
-
- sources = sources[1:]
- func.unmerge_vars(target, sources)
- bv.update_analysis_and_wait()
-
- retinfo += ["HLIL after unmerge: " + x for x in str(func.hlil).split("\n")]
- return retinfo
- finally:
- self.delete_package("array_test.bndb")
-
- def test_live_instrs_for_var(self):
- """Live instructions for variable produced different output"""
- file_name = self.unpackage_file("array_test.bndb")
- try:
- with binja.load(file_name) as bv:
- func = bv.get_function_at(0x100003920)
- retinfo = []
- for var in func.vars:
- instrs = func.mlil.get_live_instructions_for_var(var)
- for instr in instrs:
- retinfo += [f"MLIL live instr for {var}: {repr(instr)}"]
- return retinfo
- finally:
- self.delete_package("array_test.bndb")
-
-
-class VerifyBuilder(Builder):
- """ The VerifyBuilder is for tests that verify
- Binary Ninja against expected output.
-
- - Function that are tests should start with 'test_'
- - Function doc string used as 'on error' message
- - Should return: boolean
- """
-
- def __init__(self, test_store):
- super(VerifyBuilder, self).__init__(test_store)
-
- def get_functions(self, bv):
- return [x.start for x in bv.functions]
-
- def get_comments(self, bv):
- return next(bv.functions).comments
-
- def test_possiblevalueset_parse(self):
- """ Failed to parse PossibleValueSet from string"""
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- # ConstantValue
- lhs = bv.parse_possiblevalueset("0", binja.RegisterValueType.ConstantValue)
- rhs = binja.PossibleValueSet.constant(0)
- assert lhs == rhs
- lhs = bv.parse_possiblevalueset("$here + 2", binja.RegisterValueType.ConstantValue, 0x2000)
- rhs = binja.PossibleValueSet.constant(0x2000 + 2)
- assert lhs == rhs
- # ConstantPointerValue
- lhs = bv.parse_possiblevalueset("0x8000", binja.RegisterValueType.ConstantPointerValue)
- rhs = binja.PossibleValueSet.constant_ptr(0x8000)
- assert lhs == rhs
- # StackFrameOffset
- lhs = bv.parse_possiblevalueset("16", binja.RegisterValueType.StackFrameOffset)
- rhs = binja.PossibleValueSet.stack_frame_offset(0x16)
- assert lhs == rhs
- # SignedRangeValue
- lhs = bv.parse_possiblevalueset("-10:0:2", binja.RegisterValueType.SignedRangeValue)
- rhs = binja.PossibleValueSet.signed_range_value([binja.ValueRange(-0x10, 0, 2)])
- assert lhs == rhs
- lhs = bv.parse_possiblevalueset("-10:0:2,2:5:1", binja.RegisterValueType.SignedRangeValue)
- rhs = binja.PossibleValueSet.signed_range_value([binja.ValueRange(-0x10, 0, 2), binja.ValueRange(2, 5, 1)])
- assert lhs == rhs
- # UnsignedRangeValue
- lhs = bv.parse_possiblevalueset("1:10:1", binja.RegisterValueType.UnsignedRangeValue)
- rhs = binja.PossibleValueSet.unsigned_range_value([binja.ValueRange(1, 0x10, 1)])
- assert lhs == rhs
- lhs = bv.parse_possiblevalueset("1:10:1, 2:20:2", binja.RegisterValueType.UnsignedRangeValue)
- rhs = binja.PossibleValueSet.unsigned_range_value([binja.ValueRange(1, 0x10, 1), binja.ValueRange(2, 0x20, 2)])
- assert lhs == rhs
- # InSetOfValues
- lhs = bv.parse_possiblevalueset("1,2,3,3,4", binja.RegisterValueType.InSetOfValues)
- rhs = binja.PossibleValueSet.in_set_of_values([1,2,3,4])
- assert lhs == rhs
- # NotInSetOfValues
- lhs = bv.parse_possiblevalueset("1,2,3,4,4", binja.RegisterValueType.NotInSetOfValues)
- rhs = binja.PossibleValueSet.not_in_set_of_values([1,2,3,4])
- assert lhs == rhs
- # UndeterminedValue
- lhs = bv.parse_possiblevalueset("", binja.RegisterValueType.UndeterminedValue)
- rhs = binja.PossibleValueSet.undetermined()
- assert lhs == rhs
- return True
- finally:
- self.delete_package("helloworld")
-
- def test_expression_parse(self):
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- assert bv.parse_expression("1 + 1") == 2
- assert bv.parse_expression("-1 + 1") == 0
- assert bv.parse_expression("1 - 1") == 0
- assert bv.parse_expression("1 + -1") == 0
- assert bv.parse_expression("[0x8000]") == 0x464c457f
- assert bv.parse_expression("[0x8000]b") == 0
- assert bv.parse_expression("[0x8000].b") == 0x7f
- assert bv.parse_expression("[0x8000].w") == 0x457f
- assert bv.parse_expression("[0x8000].d") == 0x464c457f
- assert bv.parse_expression("[0x8000].q") == 0x10101464c457f
- assert bv.parse_expression("$here + 1", 12345) == 12345 + 1
- assert bv.parse_expression("_start") == 0x830c
- assert bv.parse_expression("_start + 4") == 0x8310
- return True
- finally:
- self.delete_package("helloworld")
-
- def test_get_il_vars(self):
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- main_func = bv.get_functions_by_name("main")[0]
- value = sorted(list(map(lambda v: str(v), main_func.vars)))
- oracle = ['__saved_r11', 'arg_0', 'argc', 'argv', 'envp', 'r0', 'r3', 'var_10', 'var_4', 'var_c']
- assert value == oracle, f"test result from 'main_func.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.lifted_il.vars)))
- oracle = []
- assert value == oracle, f"test result from 'main_func.lifted_il.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.lifted_il.ssa_vars)))
- oracle = []
- assert value == oracle, f"test result from 'main_func.lifted_il.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.vars)))
- oracle = ['lr', 'r0', 'r1', 'r11', 'r12', 'r2', 'r3', 'sp', 'temp0']
- assert value == oracle, f"test result from 'main_func.llil.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.ssa_vars)))
- oracle = ['<ssa lr version 0>', '<ssa lr version 1>', '<ssa lr version 2>', '<ssa lr version 3>', '<ssa r0 version 0>', '<ssa r0 version 1>', '<ssa r0 version 2>', '<ssa r0 version 3>', '<ssa r0 version 4>', '<ssa r0 version 5>', '<ssa r0 version 6>', '<ssa r1 version 0>', '<ssa r1 version 1>', '<ssa r1 version 2>', '<ssa r1 version 3>', '<ssa r11 version 0>', '<ssa r11 version 1>', '<ssa r11 version 2>', '<ssa r12 version 1>', '<ssa r12 version 2>', '<ssa r12 version 3>', '<ssa r2 version 1>', '<ssa r2 version 2>', '<ssa r2 version 3>', '<ssa r3 version 1>', '<ssa r3 version 2>', '<ssa r3 version 3>', '<ssa r3 version 4>', '<ssa r3 version 5>', '<ssa sp version 0>', '<ssa sp version 1>', '<ssa sp version 2>', '<ssa sp version 3>', '<ssa sp version 4>', '<ssa sp version 5>', '<ssa sp version 6>', '<ssa temp0 version 1>']
- assert value == oracle, f"test result from 'main_func.llil.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.ssa_form.vars)))
- oracle = ['lr', 'r0', 'r1', 'r11', 'r12', 'r2', 'r3', 'sp', 'temp0']
- assert value == oracle, f"test result from 'main_func.llil.ssa_form.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.ssa_form.ssa_registers)))
- oracle = ['<ssa lr version 0>', '<ssa lr version 1>', '<ssa lr version 2>', '<ssa lr version 3>', '<ssa r0 version 0>', '<ssa r0 version 1>', '<ssa r0 version 2>', '<ssa r0 version 3>', '<ssa r0 version 4>', '<ssa r0 version 5>', '<ssa r0 version 6>', '<ssa r1 version 0>', '<ssa r1 version 1>', '<ssa r1 version 2>', '<ssa r1 version 3>', '<ssa r11 version 0>', '<ssa r11 version 1>', '<ssa r11 version 2>', '<ssa r12 version 1>', '<ssa r12 version 2>', '<ssa r12 version 3>', '<ssa r2 version 1>', '<ssa r2 version 2>', '<ssa r2 version 3>', '<ssa r3 version 1>', '<ssa r3 version 2>', '<ssa r3 version 3>', '<ssa r3 version 4>', '<ssa r3 version 5>', '<ssa sp version 0>', '<ssa sp version 1>', '<ssa sp version 2>', '<ssa sp version 3>', '<ssa sp version 4>', '<ssa sp version 5>', '<ssa sp version 6>', '<ssa temp0 version 1>']
- assert value == oracle, f"test result from 'main_func.llil.ssa_form.ssa_registers' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.ssa_form.ssa_register_stacks)))
- oracle = []
- assert value == oracle, f"test result from 'main_func.llil.ssa_form.ssa_register_stacks' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.ssa_form.ssa_flags)))
- oracle = []
- assert value == oracle, f"test result from 'main_func.llil.ssa_form.ssa_flags' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.mapped_medium_level_il.vars)))
- oracle = ['__saved_r11', 'argc', 'argv', 'envp', 'lr', 'r11', 'r12', 'r3', 'sp', 'temp0', 'var_10', 'var_4', 'var_c']
- assert value == oracle, f"test result from 'main_func.llil.mapped_medium_level_il.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.mapped_medium_level_il.ssa_vars)))
- oracle = ['<ssa __saved_r11 version 1>', '<ssa argc version 0>', '<ssa argc version 1>', '<ssa argc version 2>', '<ssa argc version 3>', '<ssa argc version 4>', '<ssa argc version 5>', '<ssa argc version 6>', '<ssa argv version 0>', '<ssa argv version 1>', '<ssa argv version 2>', '<ssa argv version 3>', '<ssa envp version 1>', '<ssa envp version 2>', '<ssa envp version 3>', '<ssa lr version 0>', '<ssa lr version 1>', '<ssa lr version 2>', '<ssa lr version 3>', '<ssa r11 version 0>', '<ssa r11 version 1>', '<ssa r11 version 2>', '<ssa r12 version 1>', '<ssa r12 version 2>', '<ssa r12 version 3>', '<ssa r3 version 1>', '<ssa r3 version 2>', '<ssa r3 version 3>', '<ssa r3 version 4>', '<ssa r3 version 5>', '<ssa sp version 1>', '<ssa sp version 2>', '<ssa sp version 3>', '<ssa sp version 4>', '<ssa sp version 5>', '<ssa sp version 6>', '<ssa temp0 version 1>', '<ssa var_10 version 1>', '<ssa var_4 version 1>', '<ssa var_c version 1>']
- assert value == oracle, f"test result from 'main_func.llil.mapped_medium_level_il.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.mapped_medium_level_il.ssa_form.vars)))
- oracle = ['__saved_r11', 'argc', 'argv', 'envp', 'lr', 'r11', 'r12', 'r3', 'sp', 'temp0', 'var_10', 'var_4', 'var_c']
- assert value == oracle, f"test result from 'main_func.llil.mapped_medium_level_il.ssa_form.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.llil.mapped_medium_level_il.ssa_form.ssa_vars)))
- oracle = ['<ssa __saved_r11 version 1>', '<ssa argc version 0>', '<ssa argc version 1>', '<ssa argc version 2>', '<ssa argc version 3>', '<ssa argc version 4>', '<ssa argc version 5>', '<ssa argc version 6>', '<ssa argv version 0>', '<ssa argv version 1>', '<ssa argv version 2>', '<ssa argv version 3>', '<ssa envp version 1>', '<ssa envp version 2>', '<ssa envp version 3>', '<ssa lr version 0>', '<ssa lr version 1>', '<ssa lr version 2>', '<ssa lr version 3>', '<ssa r11 version 0>', '<ssa r11 version 1>', '<ssa r11 version 2>', '<ssa r12 version 1>', '<ssa r12 version 2>', '<ssa r12 version 3>', '<ssa r3 version 1>', '<ssa r3 version 2>', '<ssa r3 version 3>', '<ssa r3 version 4>', '<ssa r3 version 5>', '<ssa sp version 1>', '<ssa sp version 2>', '<ssa sp version 3>', '<ssa sp version 4>', '<ssa sp version 5>', '<ssa sp version 6>', '<ssa temp0 version 1>', '<ssa var_10 version 1>', '<ssa var_4 version 1>', '<ssa var_c version 1>']
- assert value == oracle, f"test result from 'main_func.llil.mapped_medium_level_il.ssa_form.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.mlil.vars)))
- oracle = ['argc', 'argv', 'r0', 'r3', 'var_10', 'var_c']
- assert value == oracle, f"test result from 'main_func.mlil.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.mlil.ssa_vars)))
- oracle = ['<ssa argc version 0>', '<ssa argv version 0>', '<ssa r0 version 1>', '<ssa r3 version 1>', '<ssa var_10 version 1>', '<ssa var_c version 1>']
- assert value == oracle, f"test result from 'main_func.mlil.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.mlil.ssa_form.vars)))
- oracle = ['argc', 'argv', 'r0', 'r3', 'var_10', 'var_c']
- assert value == oracle, f"test result from 'main_func.mlil.ssa_form.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.mlil.ssa_form.ssa_vars)))
- oracle = ['<ssa argc version 0>', '<ssa argv version 0>', '<ssa r0 version 1>', '<ssa r3 version 1>', '<ssa var_10 version 1>', '<ssa var_c version 1>']
- assert value == oracle, f"test result from 'main_func.mlil.ssa_form.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.hlil.vars)))
- oracle = ['argc', 'argv', 'var_10']
- assert value == oracle, f"test result from 'main_func.hlil.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.hlil.ssa_vars)))
- oracle = ['<ssa argc version 0>', '<ssa argv version 0>', '<ssa var_10 version 1>']
- assert value == oracle, f"test result from 'main_func.hlil.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.hlil.ssa_form.vars)))
- oracle = ['argc', 'argv', 'var_10']
- assert value == oracle, f"test result from 'main_func.hlil.ssa_form.vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), main_func.hlil.ssa_form.ssa_vars)))
- oracle = ['<ssa argc version 0>', '<ssa argv version 0>', '<ssa var_10 version 1>']
- assert value == oracle, f"test result from 'main_func.hlil.ssa_form.ssa_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
-
- start_func = bv.get_functions_by_name("_start")[0]
- value = sorted(list(map(lambda v: str(v), start_func.mlil.aliased_vars)))
- oracle = ['arg_4']
- assert value == oracle, f"test result from 'start_func.mlil.aliased_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), start_func.mlil.ssa_form.aliased_vars)))
- oracle = ['arg_4']
- assert value == oracle, f"test result from 'start_func.mlil.ssa_form.aliased_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), start_func.hlil.aliased_vars)))
- oracle = ['arg_4']
- assert value == oracle, f"test result from 'start_func.hlil.aliased_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
- value = sorted(list(map(lambda v: str(v), start_func.hlil.ssa_form.aliased_vars)))
- oracle = ['arg_4']
- assert value == oracle, f"test result from 'start_func.hlil.ssa_form.aliased_vars' = \n\t{value}\nwhich is != to oracle: \n\t{oracle}"
-
- return True
- finally:
- self.delete_package("helloworld")
-
- def test_verify_BNDB_round_trip(self):
- """Binary Ninja Database output doesn't match its input"""
- # This will test Binja's ability to save and restore databases
- # By:
- # - Creating a binary view
- # - Make modification that impact the database
- # - Record those modification
- # - Save the database
- # - Restore the datbase
- # - Validate that the modifications are present
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- bv.update_analysis_and_wait()
- # Make some modifications to the binary view
-
- # Add a comment
- f = next(bv.functions)
- f.set_comment(f.start, "Function start")
- # Add a new function
- bv.add_function(f.start + 4)
- temp_name = next(tempfile._get_candidate_names()) + ".bndb"
-
- comments = self.get_comments(bv)
- functions = self.get_functions(bv)
- bv.create_database(temp_name)
- bv.file.close()
- del bv
-
- bv = binja.FileMetadata(temp_name).open_existing_database(temp_name).get_view_of_type('ELF')
- bv.update_analysis_and_wait()
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- # force windows to close the handle to the bndb that we want to delete
- bv.file.close()
- del bv
- os.unlink(temp_name)
- return [str(functions == bndb_functions and comments == bndb_comments)]
- finally:
- self.delete_package("helloworld")
-
- def test_verify_persistent_undo(self):
- file_name = self.unpackage_file("helloworld")
- try:
- temp_name = next(tempfile._get_candidate_names()) + ".bndb"
-
- with binja.load(file_name) as bv:
-
- bv.update_analysis_and_wait()
-
- bv.begin_undo_actions()
- f = next(bv.functions)
- f.set_comment(f.start, "Function start")
- bv.commit_undo_actions()
-
- bv.update_analysis_and_wait()
- comments = self.get_comments(bv)
- functions = self.get_functions(bv)
-
- bv.begin_undo_actions()
- f.set_comment(f.start, "Function start!")
- bv.commit_undo_actions()
-
- bv.begin_undo_actions()
- bv.create_user_function(bv.start)
- bv.commit_undo_actions()
-
- bv.update_analysis_and_wait()
- bv.create_database(temp_name)
-
- with binja.FileMetadata(temp_name).open_existing_database(temp_name).get_view_of_type('ELF') as bv:
-
- bv.update_analysis_and_wait()
-
- bv.undo()
- bv.undo()
-
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
-
- os.unlink(temp_name)
- return functions == bndb_functions and comments == bndb_comments
-
- finally:
- self.delete_package("helloworld")
-
- def test_memory_leaks(self):
- """Detected memory leaks during analysis"""
- # This test will attempt to detect object leaks during headless analysis
- file_name = self.unpackage_file("helloworld")
- try:
- # Open the binary once and let any persistent structures be created (typically types)
- bv = binja.BinaryViewType['ELF'].open(file_name)
- bv.update_analysis_and_wait()
- # Hold on to a graph reference while tearing down the binary view. This will keep a reference
- # in the core. If we directly free the view, the teardown will happen in a worker thread and
- # we will not be able to get a reliable object count. By keeping a reference in a different
- # object in the core, the teardown will occur immediately upon freeing the other object.
- graph = next(bv.functions).create_graph()
- bv.file.close()
- del bv
- import gc
- gc.collect()
- del graph
- gc.collect()
-
- initial_object_counts = binja.get_memory_usage_info()
-
- # Analyze the binary again
- bv = binja.BinaryViewType['ELF'].open(file_name)
- bv.update_analysis_and_wait()
- graph = next(bv.functions).create_graph()
- bv.file.close()
- del bv
- gc.collect()
- del graph
- gc.collect()
-
- # Capture final object count
- final_object_counts = binja.get_memory_usage_info()
-
- # Check for leaks
- ok = True
- for i in initial_object_counts.keys():
- if final_object_counts[i] > initial_object_counts[i]:
- ok = False
- return ok
- finally:
- self.delete_package("helloworld")
-
- def test_univeral_loader(self):
- """Universal Mach-O Loader Tests"""
- file_name = self.unpackage_file("fat_macho_9arch")
- save_setting_value = binja.Settings().get_string_list("files.universal.architecturePreference")
- binja.Settings().reset("files.universal.architecturePreference")
- try:
- # test with default arch preference
- with binja.load(file_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "x86")
- assert(bv.start == 0x1000)
- load_setting_keys = bv.get_load_settings("Mach-O")
- assert(load_setting_keys is not None)
- assert(len(bv.get_load_settings("Mach-O").keys()) == 1)
- assert(bv.get_load_settings("Mach-O").get_integer("loader.macho.universalImageOffset") == 0x1000)
-
- # save temp bndb for round trip testing
- f = next(bv.functions)
- f.set_comment(f.start, "Function start")
- comments = self.get_comments(bv)
- functions = self.get_functions(bv)
- temp_name = next(tempfile._get_candidate_names()) + ".bndb"
- bv.create_database(temp_name)
-
- # test binja.load open path
- binja.Settings().reset("files.universal.architecturePreference")
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "x86")
- assert(bv.start == 0x1000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
-
- # test binja.load open path
- binja.Settings().reset("files.universal.architecturePreference")
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "x86")
- assert(bv.start == 0x1000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
-
- # test binja.load open path (modified architecture preference)
- binja.Settings().set_string_list("files.universal.architecturePreference", ["arm64"])
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "x86")
- assert(bv.start == 0x1000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
-
- # test binja.load open path (modified architecture preference)
- binja.Settings().set_string_list("files.universal.architecturePreference", ["x86_64", "arm64"])
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "x86")
- assert(bv.start == 0x1000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
- os.unlink(temp_name)
-
- # test with overridden arch preference
- binja.Settings().set_string_list("files.universal.architecturePreference", ["arm64"])
- with binja.load(file_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "aarch64")
- assert(bv.start == 0x100000000)
- load_setting_keys = bv.get_load_settings("Mach-O")
- assert(load_setting_keys is not None)
- assert(len(bv.get_load_settings("Mach-O").keys()) == 1)
- assert(bv.get_load_settings("Mach-O").get_integer("loader.macho.universalImageOffset") == 0x4c000)
-
- # save temp bndb for round trip testing
- f = next(bv.functions)
- f.set_comment(f.start, "Function start")
- comments = self.get_comments(bv)
- functions = self.get_functions(bv)
- temp_name = next(tempfile._get_candidate_names()) + ".bndb"
- bv.create_database(temp_name)
-
- # test binja.load open path
- binja.Settings().reset("files.universal.architecturePreference")
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "aarch64")
- assert(bv.start == 0x100000000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
-
- # test binja.load open path
- binja.Settings().reset("files.universal.architecturePreference")
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "aarch64")
- assert(bv.start == 0x100000000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
-
- # test binja.load open path (modified architecture preference)
- binja.Settings().set_string_list("files.universal.architecturePreference", ["x86"])
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "aarch64")
- assert(bv.start == 0x100000000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
-
- # test binja.load open path (modified architecture preference)
- binja.Settings().set_string_list("files.universal.architecturePreference", ["x86_64", "arm64"])
- with binja.load(temp_name) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "aarch64")
- assert(bv.start == 0x100000000)
- bndb_functions = self.get_functions(bv)
- bndb_comments = self.get_comments(bv)
- assert([str(functions == bndb_functions and comments == bndb_comments)])
- bv.file.close()
- os.unlink(temp_name)
-
-
- binja.Settings().set_string_list("files.universal.architecturePreference", ["x86_64", "arm64"])
- with binja.load(file_name, options={'loader.imageBase': 0xfffffff0000}) as bv:
- assert(bv.view_type == "Mach-O")
- assert(bv.arch.name == "x86_64")
- assert(bv.start == 0xfffffff0000)
- load_setting_keys = bv.get_load_settings("Mach-O")
- assert(load_setting_keys is not None)
- assert(len(bv.get_load_settings("Mach-O").keys()) == 8)
- assert(bv.get_load_settings("Mach-O").get_integer("loader.macho.universalImageOffset") == 0x8000)
-
- binja.Settings().set_string_list("files.universal.architecturePreference", save_setting_value)
- return True
-
- finally:
- binja.Settings().set_string_list("files.universal.architecturePreference", save_setting_value)
- self.delete_package("fat_macho_9arch")
-
- def test_user_informed_dataflow(self):
- """User-informed dataflow tests"""
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- func = bv.get_function_at(0x00008440)
-
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
- assert(ins.operation == binja.MediumLevelILOperation.MLIL_IF)
- assert(len(ins.vars_read) == 1)
- var = ins.vars_read[0]
- defs = func.mlil.get_var_definitions(var)
- assert(len(defs) == 1)
- def_site = defs[0].address
-
- # Set variable value to 0
- bv.begin_undo_actions()
- func.set_user_var_value(var, def_site, binja.PossibleValueSet.constant(0))
- bv.commit_undo_actions()
- bv.update_analysis_and_wait()
-
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
- assert(ins.operation == binja.MediumLevelILOperation.MLIL_IF)
- # test if condition value is updated to true
- assert(ins.condition.value == True)
- # test if register value is updated to 0
- assert(ins.get_reg_value_after('r3') == 0)
- # test if branch is eliminated in hlil
- for hlil_ins in func.hlil.instructions:
- assert(hlil_ins.operation != binja.HighLevelILOperation.HLIL_IF)
-
- # test undo action
- bv.undo()
- bv.update_analysis_and_wait()
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
- assert(ins.operation == binja.MediumLevelILOperation.MLIL_IF)
- # test if condition value is updated to undetermined
- assert(ins.condition.value.type == binja.RegisterValueType.UndeterminedValue)
- # test if register value is updated to undetermined
- assert(ins.get_reg_value_after('r3').type == binja.RegisterValueType.EntryValue)
- # test if branch is restored in hlil
- found = False
- for hlil_ins in func.hlil.instructions:
- if hlil_ins.operation == binja.HighLevelILOperation.HLIL_IF:
- found = True
- assert(found)
-
- # test redo action
- bv.redo()
- bv.update_analysis_and_wait()
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
- assert(ins.operation == binja.MediumLevelILOperation.MLIL_IF)
- # test if condition value is updated to true
- assert(ins.condition.value == True)
- # test if register value is updated to 0
- assert(ins.get_reg_value_after('r3') == 0)
- # test if branch is eliminated in hlil
- for hlil_ins in func.hlil.instructions:
- assert(hlil_ins.operation != binja.HighLevelILOperation.HLIL_IF)
-
- # test bndb round trip
- temp_name = next(tempfile._get_candidate_names()) + ".bndb"
- bv.create_database(temp_name)
-
- with binja.load(temp_name) as bv:
- func = bv.get_function_at(0x00008440)
-
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
- assert(ins.operation == binja.MediumLevelILOperation.MLIL_IF)
- # test if condition value is updated to true
- assert(ins.condition.value == True)
- # test if register value is updated to 0
- assert(ins.get_reg_value_after('r3') == 0)
- # test if branch is eliminated in hlil
- for hlil_ins in func.hlil.instructions:
- assert(hlil_ins.operation != binja.HighLevelILOperation.HLIL_IF)
-
- # test undo after round trip
- bv.undo()
- bv.update_analysis_and_wait()
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
- assert(ins.operation == binja.MediumLevelILOperation.MLIL_IF)
- # test if condition value is updated to undetermined
- assert(ins.condition.value.type == binja.RegisterValueType.UndeterminedValue)
- # test if register value is updated to undetermined
- assert(ins.get_reg_value_after('r3').type == binja.RegisterValueType.EntryValue)
- # test if branch is restored in hlil
- found = False
- for hlil_ins in func.hlil.instructions:
- if hlil_ins.operation == binja.HighLevelILOperation.HLIL_IF:
- found = True
- assert(found)
-
- os.unlink(temp_name)
- return True
-
- finally:
- self.delete_package("helloworld")
-
- def test_possiblevalueset_ser_and_deser(self):
- """PossibleValueSet serialization and deserialization"""
- def test_helper(value):
- file_name = self.unpackage_file("helloworld")
- try:
- with binja.load(file_name) as bv:
- func = bv.get_function_at(0x00008440)
-
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
-
- var = ins.vars_read[0]
- defs = func.mlil.get_var_definitions(var)
- def_site = defs[0].address
-
- func.set_user_var_value(var, def_site, value)
- bv.update_analysis_and_wait()
-
- def_ins_idx = func.mlil.get_instruction_start(def_site)
- def_ins = func.mlil[def_ins_idx]
-
- assert(def_ins.get_possible_reg_values_after('r3') == value)
-
- temp_name = next(tempfile._get_candidate_names()) + ".bndb"
- bv.create_database(temp_name)
-
- with binja.load(temp_name) as bv:
- func = bv.get_function_at(0x00008440)
-
- ins_idx = func.mlil.get_instruction_start(0x845c)
- ins = func.mlil[ins_idx]
-
- def_ins_idx = func.mlil.get_instruction_start(def_site)
- def_ins = func.mlil[def_ins_idx]
-
- assert(def_ins.get_possible_reg_values_after('r3') == value)
-
- os.unlink(temp_name)
- return True
-
- finally:
- self.delete_package("helloworld")
-
- assert(test_helper(binja.PossibleValueSet.constant(0)))
- assert(test_helper(binja.PossibleValueSet.constant_ptr(0x8000)))
- assert(test_helper(binja.PossibleValueSet.unsigned_range_value([binja.ValueRange(1, 10, 2)])))
- # assert(test_helper(binja.PossibleValueSet.signed_range_value([binja.ValueRange(-10, 0, 2)])))
- assert(test_helper(binja.PossibleValueSet.in_set_of_values([1,2,3,4])))
- assert(test_helper(binja.PossibleValueSet.not_in_set_of_values([1,2,3,4])))
- return True
-
- def test_binaryview_callbacks(self):
- """BinaryView finalized callback and analysis completion callback"""
- file_name = self.unpackage_file("helloworld")
-
- # Currently, there is no way to unregister a BinaryView event callback.
- # This boolean tells the callback function whether it should run or just return
- callback_should_run = True
-
- def bv_finalized_callback(bv):
- if callback_should_run:
- bv.store_metadata('finalized', 'yes')
-
- def bv_finalized_callback_2(bv):
- if callback_should_run:
- bv.store_metadata('finalized_2', 'yes')
-
- def bv_analysis_completion_callback(bv):
- if callback_should_run:
- bv.store_metadata('analysis_completion', 'yes')
-
- BinaryViewType.add_binaryview_finalized_event(bv_finalized_callback)
- BinaryViewType.add_binaryview_finalized_event(bv_finalized_callback_2)
- BinaryViewType.add_binaryview_initial_analysis_completion_event(bv_analysis_completion_callback)
-
- try:
- with binja.load(file_name) as bv:
- finalized = bv.query_metadata('finalized') == 'yes'
- finalized_2 = bv.query_metadata('finalized_2') == 'yes'
- analysis_completion = bv.query_metadata('analysis_completion') == 'yes'
- return finalized and finalized_2 and analysis_completion
-
- finally:
- self.delete_package("helloworld")
- callback_should_run = False
-
- def test_load_old_database(self):
- """Load a database produced by older versions of Binary Ninja"""
- for version in ["binja_v1.2.1921_bin_ls.bndb", "binja_v2.2.2487_bin_ls.bndb", "binja_v2.5.3112_bin_ls.bndb"]:
- file_name = self.unpackage_file(version)
- if not os.path.exists(file_name):
- return False
-
- binja.Settings().set_bool("analysis.database.suppressReanalysis", True)
- ret = None
- with binja.load(file_name) as bv:
- if bv is None:
- ret = False
- if bv.file.snapshot_data_applied_without_error:
- ret = True
-
- binja.Settings().reset("analysis.database.suppressReanalysis")
- self.delete_package(version)
-
- if not ret:
- return ret
- return True
-
- def test_struct_type_leakage(self):
- """
- Define a structure, then assign a variable to it. There should only be NTRs (and not dereffed types) in func.vars
- See: #2428
- """
- file_name = self.unpackage_file("basic_struct")
-
- ret = True
- try:
- with binja.load(file_name) as bv:
- # struct A { uint64_t a; uint64_t b; };
- with binja.StructureBuilder.builder(bv, "A") as s:
- s.width = 0x10
- s.append(binja.Type.int(8, False), "a")
- s.append(binja.Type.int(8, False), "b")
-
- # Find main and the var it sets to malloc(0x10)
- func = [f for f in bv.functions if f.name == '_main'][0]
- for v in func.vars:
- d = func.mlil.get_var_definitions(v)
- if len(d) == 0:
- continue
-
- if d[0].operation == binja.MediumLevelILOperation.MLIL_CALL:
- var = v
-
- # Change var type to struct A*
- vt = binja.Type.pointer(bv.arch, binja.Type.named_type_from_registered_type(bv, 'A'))
- func.create_user_var(var, vt, 'test')
- bv.update_analysis_and_wait()
-
- for v in func.vars:
- if isinstance(v.type, binja.types.PointerType):
- if isinstance(v.type.target, binja.types.StructureType):
- ret = False
- print(f"Found ptr to raw structure: {v.type} {v}")
- finally:
- self.delete_package("basic_struct")
-
- return ret
-
- def test_old_tags(self):
- """
- New builds use string-based ids for tags, whereas older builds used integers. Make sure the old builds still work
- """
-
- file_name = self.unpackage_file("old_tags.bndb")
- assert file_name is not None
- ret = True
- try:
- binja.Settings().set_bool("analysis.database.suppressReanalysis", True)
- with binja.load(file_name) as bv:
- if bv is None:
- ret = False
- raise Exception("File binja.load error")
- if not bv.file.snapshot_data_applied_without_error:
- ret = False
- raise Exception("Snapshot apply error")
-
- # Make sure the tags exist and are where we expect them
- _start = bv.get_function_at(bv.start + 0x1060)
- assert _start is not None
- sub_1012 = bv.get_function_at(bv.start + 0x1012)
-
- assert len(bv.get_data_tags_at(bv.start + 0x6030)) == 1
- assert bv.get_data_tags_at(bv.start + 0x6030)[0].type.name == 'Bookmarks'
- assert bv.get_data_tags_at(bv.start + 0x6030)[0].data == '2'
- assert bv.get_data_tags_at(bv.start + 0x6030)[0].id == '7'
-
- assert len(bv.get_data_tags_at(bv.start + 0x6040)) == 2
- assert bv.get_data_tags_at(bv.start + 0x6040)[0].type.name == 'Crashes'
- assert bv.get_data_tags_at(bv.start + 0x6040)[0].data == 'New Tag'
- assert bv.get_data_tags_at(bv.start + 0x6040)[0].id == '8'
- assert bv.get_data_tags_at(bv.start + 0x6040)[1].type.name == 'Library'
- assert bv.get_data_tags_at(bv.start + 0x6040)[1].data == 'New Tag'
- assert bv.get_data_tags_at(bv.start + 0x6040)[1].id == '9'
-
- function_tags = list(_start.function_tags)
- assert len(function_tags) == 1
- assert function_tags[0].type.name == 'Library'
- assert function_tags[0].data == 'New Tag'
- assert function_tags[0].id == '1'
-
- function_tags = list(sub_1012.function_tags)
- assert len(function_tags) == 2
- assert function_tags[0].type.name == 'Library'
- assert function_tags[0].data == 'New Tag'
- assert function_tags[0].id == '3'
- assert function_tags[1].type.name == 'Bugs'
- assert function_tags[1].data == 'New Tag'
- assert function_tags[1].id == '10'
-
- address_tags = list(_start.get_address_tags_at(bv.start + 0x1097))
- assert len(address_tags) == 1
- assert address_tags[0].type.name == 'Important'
- assert address_tags[0].data == 'New Tag'
- assert address_tags[0].id == '4'
-
- address_tags = list(_start.get_address_tags_at(bv.start + 0x1116))
- assert len(address_tags) == 2
- assert address_tags[0].type.name == 'Crashes'
- assert address_tags[0].data == 'New Tag'
- assert address_tags[0].id == '5'
- assert address_tags[1].type.name == 'Needs Analysis'
- assert address_tags[1].data == 'New Tag'
- assert address_tags[1].id == '6'
-
- binja.Settings().reset("analysis.database.suppressReanalysis")
- finally:
- self.delete_package("old_tags.bndb")
-
- return ret
-
- def test_get_paths(self):
- """Get install directory and bundled plugin directory"""
- core_platform = system()
-
- install_dir = binja.get_install_directory()
- if not os.path.isdir(install_dir):
- return False
-
- files = os.listdir(install_dir)
- if core_platform == "Darwin":
- if not 'libbinaryninjacore.dylib' in files:
- return False
- elif core_platform == "Linux":
- if not 'libbinaryninjacore.so.1' in files:
- return False
- elif core_platform == "Windows":
- if not 'binaryninjacore.dll' in files:
- return False
- else:
- return False
-
- plugin_dir = binja.bundled_plugin_path()
- if not os.path.isdir(plugin_dir):
- return False
-
- files = os.listdir(plugin_dir)
- if core_platform == "Darwin":
- if not 'libarch_x86.dylib' in files:
- return False
- elif core_platform == "Linux":
- if not 'libarch_x86.so' in files:
- return False
- elif core_platform == "Windows":
- if not 'arch_x86.dll' in files:
- return False
- else:
- return False
-
- return True