def visit_MLIL_CONST_PTR(self, expr): log.log_debug(f'MLIL_CONST_PTR: {expr.constant:x}') view = expr.function.source_function.view symbol = view.get_symbol_at(expr.constant) string = view.get_string_at(expr.constant) if string is not None: return [ InstructionTextToken(InstructionTextTokenType.StringToken, repr(string.value), string.start) ] elif symbol is not None: NormalSymbols = (SymbolType.FunctionSymbol, SymbolType.DataSymbol) ImportSymbols = (SymbolType.ImportedFunctionSymbol, SymbolType.ImportedDataSymbol) return [ InstructionTextToken( (InstructionTextTokenType.CodeSymbolToken if symbol.type in NormalSymbols else InstructionTextTokenType.ImportToken if symbol.type in ImportSymbols else InstructionTextTokenType.PossibleAddressToken), symbol.short_name, expr.constant, size=expr.size, address=expr.address) ]
def __init__(self, state): self.state = state self.debug_view = None self.last_ip = 0 self.regs = [] self.stack = [] registers_widget = self.widget("Debugger Registers") modules_widget = self.widget("Debugger Modules") threads_widget = self.widget("Debugger Threads") stack_widget = self.widget("Debugger Stack") bp_widget = self.widget("Debugger Breakpoints") #console_widget = self.widget('Debugger Console') if registers_widget is None or modules_widget is None or threads_widget is None or stack_widget is None or bp_widget is None: # One of the views failed to create, bail log_debug( "Creating Debugger UI for view with missing dock widgets!") return # Initial view data self.context_display() self.update_highlights() self.update_breakpoints() Settings().register_group("debugger", "Debugger") key = 'debugger.extra_annotations' if not Settings().contains(key): Settings().register_setting( key, '{"description" : "Enables automatic additional annotations to be added to the start of functions that will persist after the debugger has moved away. Must break or step across the start of a function to trigger. Currently uses comments but will be migrated to ephemeral comments when that system is finished.", "title" : "Debugger Function Start Annotations", "default" : false, "type" : "boolean"}' )
def run(self): if self.context == None: log_warn("Cannot run snippets outside of the UI at this time.") return if self.snippetChanged(): question = QMessageBox.question( self, self.tr("Confirm"), self.tr("You have unsaved changes, must save first. Save?")) if (question == QMessageBox.StandardButton.No): return else: self.save() actionText = actionFromSnippet(self.currentFile, self.snippetDescription.text()) UIActionHandler.globalActions().executeAction(actionText, self.context) log_debug("Saving snippet %s" % self.currentFile) outputSnippet = codecs.open(self.currentFile, "w", "utf-8") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets()
def loadSnippet(self): self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName()) log_debug("Loading %s as a snippet." % self.currentFile) (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(self.currentFile) self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("") self.keySequenceEdit.setKeySequence(snippetKey[0]) if len(snippetKey) != 0 else self.keySequenceEdit.setKeySequence(QKeySequence("")) self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("")
def run(self): if len(self.bv.platform.type_libraries) == 0: log_warn(f"No type libraries loaded for: {self.bv.platform.name}") return for sym in self.bv.get_symbols_of_type(SymbolType.ImportedFunctionSymbol): # log_debug(f"checking sym: {sym.name}") for typelib in self.bv.platform.type_libraries: sym_type = typelib.get_named_object(sym.name) # log_debug(f"Checking in typelib: {typelib}") if sym_type == None: continue # log_debug(f"Found type type: {sym_type}") func = self.bv.get_function_at(sym.address) if func == None: continue func.set_user_type(sym_type) log_debug("Updated sym %s at 0x%02X" % (sym.name, sym.address)) self.bv.update_analysis_and_wait()
def deleteSnippet(self): selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row snippetName = self.files.fileName(selection) question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName) if (question == QMessageBox.StandardButton.Yes): log_debug("Deleting snippet %s." % snippetName) self.clearSelection() self.files.remove(selection)
def save(self): log_debug("Saving snippet %s" % self.currentFile) outputSnippet = open(self.currentFile, "w") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets()
def rename(self): renamed = 0 newprocfn = self.bv.get_symbol_by_raw_name("go.runtime.newproc") xrefs = self.bv.get_code_refs(newprocfn.address) for xref in xrefs: log_info("found xref at 0x{:x}".format(xref.address)) addr = xref.address fn = self.get_function_around(addr) callinst = fn.get_low_level_il_at(addr) if callinst.operation != bn.LowLevelILOperation.LLIL_CALL: log_debug("not a call instruction {!r}".format(callinst)) continue params = [] # FIXME: this is such a dirty hack # get the previous two LIL instruction j = 1 while len(params) < 2: for i in range(1, 7): try: j += 1 inst = fn.get_low_level_il_at(addr - j) log_debug("instruction: -{} {!r}".format(j, inst)) break except IndexError: continue params.append(inst) # FIXME: does this work on non-x86? # check if 2 push instructions skip = True for inst in params: if inst.operation != bn.LowLevelILOperation.LLIL_PUSH: skip = True if skip: continue # get the address of the function pointer, which should be the # second push instruction inst = params[1] fptr = inst.src.value.value log_info("found call to newproc {!r} with fptr {!r}".format( callinst, fptr)) if fptr and not self.bv.get_symbol_at(fptr): a = self.get_pointer_at_virt(fptr) # target function tfn = self.bv.get_function_at(a) if tfn: varname = "fptr_" varname += sanitize_var_name(tfn.name) t = self.bv.parse_type_string("void*") self.bv.define_user_data_var(a, t[0]) sym = bn.types.Symbol('DataSymbol', a, varname, varname) self.bv.define_user_symbol(sym) renamed += 1 log_info( "renamed {} function pointers, passed to newproc".format(renamed))
def bb_mlil_analysis(bv, bb, og_bb_start, metadata): """Check authenticity of instructions in MLIL. Args: bv (BinaryView): top-level binary view handler. Lots of interesting methods can be accessed. bb (list): list of MLIL instructions. og_bb_start (long): address of where original basic block starts. isa_specific_data (dict): None or dictionary containing isa-specific info. Read in from "storage/non_generic_spec.json". Returns: bool: True if content of bb doesn't make sense. False if it's a legit. """ bb = bb2ilbb(bb, 'mlil', bv) if not bb: return False # first bb's instruction; instr.address faulting_addr = og_bb_start # check if isa is supported. If not, do not run this analysis isa_specific_data = metadata.spec['isa'].get(bv.arch.name) # result alerted_rules = list() # # rule: def_no_use_dep # if def_no_use_dep(bb, bv, isa_specific_data): # log_debug( # ('* def_no_use_dep: ' + 'bb start( 0x{0:02X} ) ' + # 'instr addr( 0x{1:02X} )').format(faulting_addr, # faulting_addr)) # alerted_rules.append('def_no_use_dep') for instr in bb: if instr.operation.name == 'MLIL_STORE': # rule: memaccess_self if memaccess_self(instr.ssa_form): log_debug( ('* memaccess_self: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('memaccess_self') # # rule: conditional_unused # if conditional_unused(instr): # log_debug( # ('* conditional_unused: ' + 'bb start( 0x{0:02X} ) ' + # 'instr addr( 0x{1:02X} )').format(faulting_addr, # instr.address)) # alerted_rules.append('conditional_unused') return alerted_rules
def type_discrepency_ptr_in_mult_div(instr): """ """ log_debug("[type_discrepency_ptr_in_mult_div]: entry " + hex(instr.address)) # check if it is multiply or division operation if instr.operation.name != 'LLIL_SET_REG': return False if instr.src.operation.value not in range(36, 43): # division and multiply values # LLIL_MUL, LLIL_MULU_DP, LLIL_MULS_DP, LLIL_DIVU # LLIL_DIVU_DP, LLIL_DIVS, LLIL_DIVS_DP return False # check if reg is also used as memory pointer # reg_oi: register of interest reg_oi = instr.ssa_form.dest if isinstance(reg_oi, ILRegister): return False log_debug("[type_discrepency_ptr_in_mult_div]: reg_io " + repr(reg_oi) + " at " + hex(instr.address)) for reg_use_il in instr.function.get_ssa_reg_uses(reg_oi): # dereference in destination mem_dest_struct = Tree( llil_type='LLIL_STORE', childs=[Tree(llil_type='LLIL_REG', childs=[Tree(llil_type='F')])]) # dereference in source mem_src_struct = Tree( llil_type='LLIL_LOAD', childs=[Tree(llil_type='LLIL_REG', childs=[Tree(llil_type='F')])]) # check for dereference in destination llil_tree = Tree() llil2tree(reg_use_il, llil_tree) if ((match_tree(mem_dest_struct, llil_tree) and mem_dest_struct.childs[0].childs[0].llil_type == str( reg_oi.reg))): if reg_use_il.size == 1: # memory access size is same as counter size. So, it is okay return False return True # check for dereference in source if not hasattr(reg_use_il, 'src'): return False llil_tree = Tree() llil2tree(reg_use_il.src, llil_tree) if ((match_tree(mem_src_struct, llil_tree) and mem_src_struct.childs[0].childs[0].llil_type == str(reg_oi.reg))): load_ils = list() get_type(reg_use_il.src, LowLevelILInstruction, 'LLIL_LOAD', load_ils, 'operation.name') for li in load_ils: if li.dest.reg == reg_oi.reg and li.size == 1: return False return True return False
def kaitaiParse(self, ksModuleName=None): log.log_debug('kaitaiParse() with len(bv)=%d and bv.file.filename=%s' % (len(self.binaryView), self.binaryView.file.filename)) if len(self.binaryView) == 0: return kaitaiIO = kshelpers.KaitaiBinaryViewIO(self.binaryView) parsed = kshelpers.parseIo(kaitaiIO, ksModuleName) if not parsed: return # it SEEMS as if parsing is finished at this moment, but some parsing # is postponed until attributes are accessed, so we must try/catch here tree = None if True: try: tree = kshelpers.buildQtree(parsed) except Exception as e: log.log_error( 'kaitai module %s threw exception, check file type' % ksModuleName) tree = None else: tree = kshelpers.buildQtree(parsed) if not tree: return self.ioRoot = tree.ksobj._io self.ioCurrent = tree.ksobj._io self.treeWidget.clear() self.treeWidget.setSortingEnabled(False) # temporarily, for efficiency # two options with how we create the hierarchy if False: # treat root as top level "file" container tree.setLabel('file') tree.setValue(None) tree.setStart(0) tree.setEnd(0) self.treeWidget.insertTopLevelItem(0, tree) else: # add root's children as top level items self.treeWidget.insertTopLevelItems(0, tree.takeChildren()) # enable sorting self.treeWidget.setSortingEnabled(True) self.treeWidget.sortByColumn(2, Qt.AscendingOrder) # TODO: select first item, maybe expand a few things self.rootSelectionStart = 0 self.rootSelectionEnd = 1 self.treeWidget.setUniformRowHeights(True) self.treeWidget.queueInitialPresentation = True
def read_cstring(self, address): self.br.seek(address) st = "" while "\x00" not in st and len(st) < 0x1000: x = self.br.read(255) if x: st += x self.br.seek(address + len(st)) else: break log_debug("{!r}".format(st)) return st
def read_cstring(self, address): self.br.seek(address) st = b"" while b"\x00" not in st and len(st) < 0x1000: x = self.br.read(255) if x: st += x self.br.seek(address + len(st)) else: break log_debug("{!r}".format(st)) return st[:st.find(b"\x00")].decode("UTF-8")
def newFileDialog(self): (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: ")) if ok and snippetName: if not snippetName.endswith(".py"): snippetName += ".py" index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): open(os.path.join(selection, snippetName), "w").close() else: open(os.path.join(snippetPath, snippetName), "w").close() log_debug("Snippet %s created." % snippetName)
def is_valid_for_data(self, data): for view_type in _macho_types: macho: BinaryView = data.get_view_of_type(view_type) if macho is not None and '_objc_msgSend' in macho.symbols: try: macho.query_metadata('objc_init') except: macho.store_metadata('objc_init', True) macho.add_analysis_completion_event(callback) log_debug(f"Found an Objective-C binary!") # Return False so we are not added as a valid BinaryView return False
def visit_MLIL_CALL(self, expr): log.log_debug(f'visit_MLIL_CALL: {expr}') output = [ InstructionTextToken(InstructionTextTokenType.LocalVariableToken, v.name, v.identifier) for v in expr.output ] dest = self.visit(expr.dest) params = [self.visit(p) for p in expr.params] for p in params[:-1]: p.append( InstructionTextToken(InstructionTextTokenType.TextToken, ', ')) log.log_debug(f'output: {output}') log.log_debug(f'dest: {dest}') log.log_debug(f'params: {list(chain(*params))}') return [ *output, InstructionTextToken(InstructionTextTokenType.TextToken, ' = ' if output else ''), *dest, InstructionTextToken(InstructionTextTokenType.TextToken, '('), *chain(*params), InstructionTextToken(InstructionTextTokenType.TextToken, ')') ]
def applySvd(self): selection = self.tree.selectedIndexes()[::self.columns][ 0] #treeview returns each selected element in the row svdName = self.files.fileName(selection) if (svdName != ""): question = QMessageBox.question( self, self.tr("Confirm"), self. tr(f"Confirm applying {svdName} to {os.path.basename(self.context.file.filename)} : " )) if (question == QMessageBox.StandardButton.Yes): log_debug("SVD Browser: Applying SVD %s." % svdName) load_svd(self.context, self.currentFile) self.close()
def conditional_unused(instr): """Check if conditional flags are set but no usage Args: instr (MediumLevelILInstruction): mlil instruction object. Returns: bool: True if conditional flags are set but not used else False. """ log_debug("enter conditional_unused: " + hex(instr.address)) # link to macro: # https://api.binary.ninja/_modules/binaryninja/enums.html#MediumLevelILOperation # noqa: E501 if instr.operation.value == 8: # MLIL_VAR, just a single variable and no assignment # ex: test instruction without usage # ignore if it is part of bool operation for a JCC instruction cur_i = instr.instr_index + 1 while instr.function[cur_i].address == instr.address: # native instruction can be broken down into multiple il instructions # if it is broken down, they will still share same VA if hasattr(instr.function[cur_i], 'dest') and \ hasattr(instr.function[cur_i].dest, 'type') and \ str(instr.function[cur_i].dest.type) == 'bool': return False cur_i += 1 return True # check if top level operation is mathematical operations. Should not be if instr.operation.value in range( 18, 45) or instr.operation.value in range(83, 90): # ignore if it is part of bool operation for a JCC instruction cur_i = instr.instr_index try: while instr.function[cur_i].address == instr.address: # native instruction can be broken down into multiple il instructions # if it is broken down, they will still share same VA if hasattr(instr.function[cur_i], 'dest') and \ hasattr(instr.function[cur_i].dest, 'type') and \ str(instr.function[cur_i].dest.type) == 'bool': return False cur_i += 1 except: # make sure original instruction index has corresponding instruction # if not return True pass return True return False
def newFileDialog(self): (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: "), flags=self.windowFlags()) if ok and snippetName: if not snippetName.endswith(".py"): snippetName += ".py" index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): path = os.path.join(selection, snippetName) else: path = os.path.join(snippetPath, snippetName) self.readOnly(False) open(path, "w").close() self.tree.setCurrentIndex(self.files.index(path)) log_debug("Snippet %s created." % snippetName)
def run(self): if self.context == None: log_warn("Cannot run snippets outside of the UI at this time.") return if self.snippetChanged(): self.save() actionText = actionFromSnippet(self.currentFile, self.snippetDescription.text()) UIActionHandler.globalActions().executeAction(actionText, self.context) log_debug("Saving snippet %s" % self.currentFile) outputSnippet = codecs.open(self.currentFile, "w", "utf-8") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets()
def get_address_from_inst(bv, addr): inst = None try: bbs = bv.get_basic_blocks_at(addr) if bbs: inst = bbs[0].function.get_low_level_il_at(addr) except IndexError: inst = None if inst is not None: log_debug("got inst: {!r}".format(inst)) if inst.src: if inst.src.value: if inst.src.value.value: return inst.src.value.value else: return None
def on_downloadRequested(self, download): old_path = download.url().path() # download.path() suffix = QFileInfo(old_path).suffix() if (suffix.lower() in ["zip", "svd", "pack", "patched"]): log_debug(f"SVD Browser: Downloading {str(download.url())}") if suffix.lower() == "svd" or suffix.lower() == "patched": download.setDownloadDirectory(svdPath) download.accept() else: with TemporaryDirectory() as tempfolder: log_debug( f"SVD Browser: Downloading pack/zip to {tempfolder}") fname = download.url().fileName() r = requests.get(download.url().toString(), allow_redirects=True) dlfile = os.path.join(tempfolder, fname) open(dlfile, "wb").write(r.content) ''' # TODO: See if the original QT Downloader can be fixed since it would # help with situations where the user entered credentials in the browser. download.setDownloadDirectory(tempfolder) download.accept() while not download.finished: import time time.sleep(100) ''' if fname.endswith(".zip") or fname.endswith(".pack"): destFolder = os.path.join(svdPath, os.path.splitext(fname)[0]) log_debug(f"SVD Browser: Creating {destFolder}") if not os.path.exists(destFolder): os.mkdir(destFolder) with ZipFile(dlfile, 'r') as zipp: for ifname in zipp.namelist(): if ifname.endswith(".svd"): info = zipp.getinfo(ifname) info.filename = os.path.basename( info.filename) log_debug( f"SVD Browser: Extracting {info.filename} from {ifname}" ) zipp.extract(info, path=destFolder) else: #Move file into place shutil.move(dlfile, svdPath) else: show_message_box( "Invalid file", "That download does not appear to be a valid SVD/ZIP/PACK file." ) download.cancel()
def crazy_mem_offset(instr, bv): """ """ log_debug("[crazy_mem_offset]: entry "+hex(instr.address)) # check dest for constant if instr.operation.name == 'LLIL_STORE_SSA': const_ils = list() get_type(instr.dest, LowLevelILInstruction, 'LLIL_CONST', const_ils, 'operation.name') log_debug("[crazy_mem_offset]: "+str(const_ils)) for c in const_ils: log_debug("[crazy_mem_offset]: const value "+str(c.value.value)) if bv.is_offset_readable(c.value.value) or bv.is_offset_writable(c.value.value): log_debug("[crazy_mem_offset]: offset is a valid virtual address") continue # greater than a certain value and less than a certain value if c.constant > 0x100000 or c.constant < -0x100000: return True # check src for constant if not hasattr(instr, 'src'): return False load_ils = list() get_type(instr.src, LowLevelILInstruction, 'LLIL_LOAD_SSA', load_ils, 'operation.name') for load_il in load_ils: const_ils = list() get_type(load_il, LowLevelILInstruction, 'LLIL_CONST', const_ils, 'operation.name') if not const_ils: continue for c in const_ils: if bv.is_offset_readable(c.value.value) or \ bv.is_offset_writable(c.value.value): log_debug("[crazy_mem_offset]: offset is a valid virtual address") continue # greater than a certain value and less than a certain value if c.constant > 0x100000 or c.constant < -0x100000: return True return False
def execute(self, state): """ Execute instruction that this class was initialized with. :state: Current active state """ operation = self.instruction.operation.name log.log_debug("Evaluating {}: {} @ {}".format( operation, self.instruction, hex(self.instruction.address))) try: if self.instruction.value.is_constant: size = self.instruction.size * 8 return [BitVecVal(self.instruction.value.value, size)] except AttributeError: pass executor = getattr(self, "evaluate_" + operation, None) if executor is not None: result = executor(state) else: raise NotImplementedError(repr(operation)) for i in range(len(result)): width = self.instruction.size * 8 if operation.endswith("_DP"): # Double precision width = width * 2 if width < result[i].size(): result[i] = Extract(width - 1, 0, result[i]) if width > result[i].size(): result[i] = ZeroExt(width - result[i].size(), result[i]) log.log_debug("Completed {}: {} @ {}".format( operation, self.instruction, hex(self.instruction.address))) return result
def rename_functions(self): renamed = 0 log_info("renaming functions based on .gopclntab section") gopclntab = self.get_section_by_name(".gopclntab") if gopclntab is None: pattern = "\xfb\xff\xff\xff\x00\x00" base_addr = self.bv.find_next_data(0, pattern) if base_addr is None: log_alert("Failed to find section '.gopclntab'") return else: base_addr = gopclntab.start size_addr = base_addr + 8 size = self.get_pointer_at(size_addr) log_info("found .gopclntab section at 0x{:x} with {} entries".format( base_addr, size / (self.ptr_size * 2))) start_addr = size_addr + self.ptr_size end_addr = base_addr + (size * self.ptr_size * 2) for addr in range(start_addr, end_addr, (2 * self.ptr_size)): log_debug("analyzing at 0x{:x}".format(addr)) func_addr = self.get_pointer_at(addr) entry_offset = self.get_pointer_at(addr + self.ptr_size) log_debug("func_addr 0x{:x}, entry offset 0x{:x}".format( func_addr, entry_offset)) name_str_offset = self.get_pointer_at( base_addr + entry_offset + self.ptr_size, 4) name_addr = base_addr + name_str_offset name = self.read_cstring(name_addr) log_debug("found name '{}' for address 0x{:x}".format( name, func_addr)) func = self.bv.get_function_at(func_addr) if not func: func = self.bv.create_user_function(func_addr) if name and len(name) > 2: name = GOFUNC_PREFIX + santize_gofunc_name(name) sym = bn.types.Symbol('FunctionSymbol', func_addr, name, name) self.bv.define_user_symbol(sym) renamed += 1 else: log_warn( ("not using function name {!r} for function at 0x{:x}" " in .gopclntab addr 0x{:x} name addr 0x{:x}").format( name, func_addr, addr, name_addr)) log_info("renamed {} go functions".format(renamed))
def bb_analysis(bv, bb, og_bb_start, metadata): """Check authenticity of `bb` based on instructions used. Args: bv (BinaryView): top-level binary view handler. Lots of interesting methods can be accessed. bb (BasicBlock): BinaryNinja.BasicBlock object. og_bb_start (long): address of where original basic block starts. isa_specific_data (dict): None or dictionary containing isa-specific info. Read in from "storage/non_generic_spec.json". Returns: bool: True if content of bb doesn't make sense. False if it's a legit. """ # first bb's instruction; instr.address faulting_addr = og_bb_start # check if isa is supported. If not, do not run this analysis isa_specific_data = metadata.spec['isa'].get(bv.arch.name) # result alerted_rules = list() # rule: weird_cutoff if weird_cutoff(bb, bv): log_debug(('* weird_cutoff: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, faulting_addr)) alerted_rules.append('weird_cutoff') # rule: prob_of_unimpl if prob_of_unimpl(bb, bv, isa_specific_data): log_debug(('* prob_of_unimpl: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, faulting_addr)) alerted_rules.append('prob_of_unimpl') for instr in bb: # rule: priviledged_instructions if priviledged_instructions(instr, bv, isa_specific_data): log_debug( ('* priviledged_instructions: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, faulting_addr)) alerted_rules.append('priviledged_instructions') return alerted_rules
def memaccess_self(instr): """Check if register used as pointer but also stored its ptr address to itself. Args: instr (MediumLevelILInstruction): mlil instruction object. Returns: bool: True if register used as pointer but also stored its ptr address to itself, else False. """ log_debug("enter memaccess_self: " + hex(instr.address)) # get vars read at destination # get vars used in memory access dest_vars_read = [ i.var.name for i in instr.dest.vars_read if hasattr(i, 'var') ] if not dest_vars_read: return False log_debug("[memaccess_self] dest_vars_read: " + str(dest_vars_read)) # get vars read at src that is not part of memory load # vars in src operands that are not a part of memory load! src_vars_read = list() # (1) check if it is a mov instruction for a subregister if instr.src.operation.name in [ 'MLIL_CONST', 'MLIL_VAR_SSA', 'MLIL_VAR_SSA_FIELD' ]: if instr.src.operation.name == 'MLIL_VAR_SSA_FIELD': src_vars_read.append(instr.src.src) # (2) heuristic for all other instructions else: for operand in instr.src.operands: if isinstance(operand, MediumLevelILInstruction): if operand.operation.name == 'MLIL_LOAD_SSA': # skip if vars is inside memory access continue for i in operand.vars_read: if hasattr(i, 'var'): src_vars_read.append(i.var.name) else: # SSAVariable object if hasattr(operand, 'var'): src_vars_read.append(operand.var.name) log_debug("[memaccess_self] src_vars_read: " + str(src_vars_read)) for src_var in src_vars_read: if src_var in dest_vars_read: return True return False
def bb_llil_analysis(bv, bb, og_bb_start, metadata): """Check authenticity of instructions in LLIL. Args: bv (BinaryView): top-level binary view handler. Lots of interesting methods can be accessed. bb (list): list of LLIL instructions. og_bb_start (long): address of where original basic block starts. Returns: bool: True if content of bb doesn't make sense. False if it's a legit. """ bb = bb2ilbb(bb, 'llil', bv) if not bb: return False isa_specific_data = metadata.spec['isa'].get(bv.arch.name) # first bb's instruction; instr.address faulting_addr = og_bb_start # result alerted_rules = list() for instr in bb: # rule: stack_pointer_oddity if stack_pointer_oddity(instr, bv, isa_specific_data): log_debug( ('* stack_pointer_oddity: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('stack_pointer_oddity') # rule: crazy_mem_offset if crazy_mem_offset(instr.ssa_form, bv): log_debug( ('* crazy_mem_offset: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('crazy_mem_offset') # rule: type_discrepency_ptr_in_mult_div if type_discrepency_ptr_in_mult_div(instr): log_debug( ('* type_discrepency_ptr_in_mult_div: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format( faulting_addr, instr.address)) alerted_rules.append('type_discrepency_ptr_in_mult_div') if instr.operation == LowLevelILOperation.LLIL_STORE: # rule: memaccess_nonexist if memaccess_nonexist(instr, bv): log_debug( ('* memaccess_nonexist: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('memaccess_nonexist') # rule: memaccess_src_dest_discrepancy if memaccess_src_dest_discrepancy(instr, bv): log_debug( ('* memaccess_src_dest_discrepancy: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('memaccess_src_dest_discrepancy') if instr.operation == LowLevelILOperation.LLIL_CALL: # rule: call_dest_nonexist if call_dest_nonexist(instr, bv): log_debug( ('* call_dest_nonexist: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('call_dest_nonexist') # rule: jmp_dest_nonexist if instr.operation == LowLevelILOperation.LLIL_JUMP: if jmp_dest_nonexist(instr, bv): log_debug( ('* jmp_dest_nonexist: ' + 'bb start( 0x{0:02X} ) ' + 'instr addr( 0x{1:02X} )').format(faulting_addr, instr.address)) alerted_rules.append('jmp_dest_nonexist') return alerted_rules
def callback(self): log_debug(f"I'm in an analysis completion event! {self.view}")
def stack_pointer_oddity(instr, bv, isa_specific_data): """ (1) stack pointer should not be assigned a constant (2) in a memory operation (LLIL_LOAD), stack pointer should only be in LLIL_ADD or LLIL_SUB (3) usage of stack pointer should be in a memory access """ log_debug("[stack_pointer_oddity]: entry "+hex(instr.address)) # ignore stack pointer usages relating to function prologue/epilogue if not instr.function.get_medium_level_il_instruction_index(instr.instr_index): return False # ignore stack pointer operations for restoring stack frame if instr.il_basic_block[-1].operation.value in [57, 58]: return False # ignore instructions that are not completely lifted if instr.operation.name == 'LLIL_UNIMPL_MEM': return False stack_ptrs = list() if not isa_specific_data: stack_ptrs = isa_specific_data['stack_pointers'] else: stack_ptrs.append(bv.arch.stack_pointer) for stack_ptr in stack_ptrs: if not contain_type(instr, ILRegister, stack_ptr, temp=[]): continue log_debug("[stack_pointer_oddity]: contain_type({}, {}, {}) " .format(str(instr), str(ILRegister), str(stack_ptr))) log_debug("[stack_pointer_oddity]: contains stack_ptr "+str(stack_ptr)) # check for memory access with stack pointer stack_semantics_add = list() stack_semantics_load = list() stack_semantics_store = list() get_type(instr, LowLevelILInstruction, 'LLIL_ADD', stack_semantics_add, 'operation.name') get_type(instr, LowLevelILInstruction, 'LLIL_STORE', stack_semantics_store, 'operation.name') get_type(instr, LowLevelILInstruction, 'LLIL_LOAD', stack_semantics_load, 'operation.name') log_debug("[stack_pointer_oddity]: stack semantics "+str(stack_semantics_add)) if contain_type(instr, LowLevelILInstruction, 'LLIL_LOAD', temp=[]) or \ contain_type(instr, LowLevelILInstruction, 'LLIL_STORE', temp=[]): # stack pointer used in memory access if (not any([il for il in stack_semantics_add if il.left.operation.name == 'LLIL_REG' and il.left.src.name == stack_ptr])) and \ (not any([il for il in stack_semantics_store if il.dest.operation.name == 'LLIL_REG' and il.dest.src.name == stack_ptr])) and \ (not any([il for il in stack_semantics_load if il.src.operation.name == 'LLIL_REG' and il.src.src.name == stack_ptr])): log_debug('[stack_pointer_oddity]: found(1)') return True # copying stack pointer value is fine elif (hasattr(instr, 'dest') and hasattr(instr, 'src') and hasattr(instr.src, 'src') and isinstance(instr.dest, ILRegister) and instr.operation.name == 'LLIL_SET_REG' and instr.src.operation.name == 'LLIL_REG' and stack_ptr == instr.src.src.name): pass else: # adding or substracting stack offsets # have to take care of 'esp = esp + 4' or 'lea, [esp+0x38]' if not (hasattr(instr, 'dest') and hasattr(instr, 'src') and \ isinstance(instr.dest, ILRegister) and \ instr.src.operation.value in [22, 24] and \ stack_ptr in [str(i) for i in instr.src.tokens]): # the only other acceptable form: esp = esp + <const> # 22, 24 == LLIL_ADD, LLIL_SUB log_debug('[stack_pointer_oddity]: found(2) at :'+hex(instr.address)) return True return False