def create_gephi_attr_dicts(self, asm_lines, filtered_methodcall = None): ''' Construct the attribute dictionary describing the graph style. Use the created dictionaries with the `add_to_graph` method. The attributes are: Calling Method: assembler code the method calls (filtered or not) Method: line number in the asm file address in the asm file surrounding lines Parameters ---------- asm_lines: string the assembler method as string filtered_methodcall: MethodCall, optional (default is None) the filtered `MethodCall` Returns ------- methodcall_sender_attr_dict: dict methodcall_calls_attr_list_dict: dict ''' method_lines_list = [str(methodcallitem) for methodcallitem in self.calls] if filtered_methodcall is None: filtered_methodcall = self # construct calls attribute dictionary methodcall_calls_attr_list_dict = [] cnt_surrounding_lines = setting_for_key(SETTINGS_CNT_SURROUNDING_LINES) if cnt_surrounding_lines >= 0: for i, methodcallitem in enumerate(filtered_methodcall.calls): # construct leading and trailing lines # index of the current line (filtered `MethodCall`) in the list of method lines current_line = str(filtered_methodcall.calls[i].call) linenr = methodcallitem.linenr idx_line = self.idx_methodcallitem(linenr) # idx_line = method_lines_list.index(current_line) lines_before, lines_after = Util.surrounding_elements_from_list(method_lines_list, idx_line, cnt_surrounding_lines) surrounding_lines = NodeAttributes.NPATTERN_SURROUND_LINES % (Util.strlist_to_str(lines_before), current_line, Util.strlist_to_str(lines_after)) methodcall_attr_dict = {NodeAttributes.NATTR_METHOD_SURROUNDING_LINES % cnt_surrounding_lines : surrounding_lines} methodcall_attr_dict.update(methodcallitem.get_gexf_viz_attr_dict()) methodcall_calls_attr_list_dict.append(methodcall_attr_dict) # add method signature to list of method lines method_lines_list.insert(0, filtered_methodcall.format_head() + ":\n") # construct sender attribute dictionary method_lines = Util.strlist_to_str(method_lines_list) methodcall_sender_attr_dict = {NodeAttributes.NATTR_METHOD : method_lines, NodeAttributes.NATTR_ASM_CODE : asm_lines} return (methodcall_sender_attr_dict, methodcall_calls_attr_list_dict)
def __load_filters(filtername_list, use_all_std_filters, arch): ''' Load all standard filters as well as the user supplied ones. They are assumed to be in the directory where the `SecurityFilter` class is located. Parameters ---------- filtername_list: list<str> list of filter names use_all_std_filters: boolean load all standard filters arch_str: string (see `Archs`) architecture Returns ------- installed_filters: list<SecurityFilter> all loaded filters [] if no filter has been loaded ''' flist = [] if filtername_list: flist = [] for f in filtername_list: base_fqn = SecurityFilter.__package__ f = '%s.%s' % (base_fqn, Util.remove_py_extension(f)) try: clazz = Util.class_for_fqn_mod_eq_class(f) fclass = clazz() flist.append(fclass) except (ImportError, ModuleNotSameClassNameException) as e: raise CLIError(str(e)), None, sys.exc_info()[2] if use_all_std_filters: # use all standard filters # do not take duplicates flist = list( set( map(lambda f: f(), SecurityFilters.security_filters(arch)) + flist)) fnames = map(lambda f: f.name, flist) if not fnames: clilog.info('no filters enabled') else: clilog.info('installed filters: %s' % ', '.join(fnames)) return flist
def log_n_read_superclasses(self): ''' Read and log the superclass information ''' # reading superclasses is only supported for x86_64 if is_x86_64(): clilog.info('reading superclasses') self.superclass_dict = self.read_superclasses() log_info( Util.format_dict_as_table(self.superclass_dict, 'subclass', 'base class')) clilog.info('')
def is_method_implementation(asmline): ''' Indicates if a line is a `CategoryClass` or method implementation. See `AsmRegEx.RE_CATEGORY` and `AsmRegEx.RE_METH_IMPL`. ''' funcs = [ AsmRegEx.compiled_vre(AsmRegEx.RE_CATEGORY), AsmRegEx.compiled_vre(AsmRegEx.RE_METH_IMPL) ] return Util.get_fst_not_none( imap(lambda f: getattr(f, 'search')(asmline), funcs))
def _check(iterable, func, steps = None, cur_steps = 0): if one_func_matches(iterable): return True if Util.is_iterable_no_string(iterable): for arg in iterable: # got given number of steps into iterable structure if steps is None or (steps is not None and cur_steps < steps): if _check(arg, func, steps, cur_steps + 1): return True if one_func_matches(arg): return True return one_func_matches(iterable)
def get_fst_match(asmline, functionlist): ''' Get the first function (lazy) that matches on the given input argument. Parameters ---------- asmline: string the argument to apply on the functions functionlist: list<functionrefs> the functions on which to apply the input argument ''' return Util.get_fst_not_none(imap(lambda x: x(asmline), functionlist))
def create_and_store_remaining_method_selector_arguments( self, start_addr, method_selector): ''' Create and store the remaining arguments for the `method_selector`. There will be as many arguments created as the `Selector` still needs and stored as `StackVar`. Parameters ---------- start_addr: int the suffix for the first `StackVar` argument (e.g. arg_4) method_selector: Selector the `Selector` describing the method that is currently being read by the `Cpu` Raises ------ SelectorOverloadedException if selector has more arguments than it needs ''' cpu = self.cpu if method_selector is not None: STACKVAR_PREFIX = 'arg' addr = start_addr start = method_selector.cnt_has_arguments() + 1 for i in range(start, method_selector.cnt_needs_arguments() + 1): method_sel_arg = ObjectiveCRuntime.create_method_selector_arg( i) # cut off the 0x prefix and use upper case for rest of address stackvar_name = '%s_%s' % ( STACKVAR_PREFIX, Util.hex_string_without_0x(addr).upper()) # save argument as StackVar stackvar = StackVar(stackvar_name) cpu.memory.registers.set_value_for_register_ann_method_ead( stackvar, method_sel_arg) method_selector.add_argument(method_sel_arg) log.debug('method_selector arg %s = %s', method_sel_arg, stackvar) addr += cpu.pointer_size()
def parse_register(self, register): ''' Parse a register. Parameters ---------- register: string Returns -------- register: `register_type` the register wrapped in `register_type` None if regular expression did not classify as register ''' register_match = regexp.compiled_vre( regexp.RE_REGISTER).search(register) if register_match is not None: ds_reg = register_match.group(regexp.RE_REGISTER_GR_DS_REG) reg = register_match.group(regexp.RE_REGISTER_GR_REG) qword_reg = register_match.group(regexp.RE_REGISTER_GR_QWORD_REG) return self.register_class( Util.filter_not_none((ds_reg, reg, qword_reg))[0]) return None
def ut_search_string(string, ut_search_string, search_substring=False, ignore_case=True): ''' Check if `string` is equal to `ut_search_string` or if `search_substring` `string` contains `search_substring`. Parameters ---------- string: string ut_search_string: string search_substring: bool (default is False) If True match even if just substring found (`find`). Otherwise check for exact equality via `==`. ignore_case: bool ignore case if enabled ''' if ignore_case: return Util.ignore_case_find( string, ut_search_string) if search_substring else str( string).upper() == ut_search_string.upper() return string.find(search_substring) != -1 if search_substring else str( string) == ut_search_string
def parse_stack_access(self, asmline): ''' Parse a stack access like e.g. "[sp, #0x8]" or "[r7, #0x8]" or "[sp] or "[sp], #0x4". The latter leads to an address of 0x0. Returns ------- tuple<string, string> Return the stack pointer name as well as the stack address as hex string. ''' cpu = self.cpu frame_reg, stack_reg = cpu.stack_pointer_register( ).register, cpu.frame_pointer_register().register stack_access_match = regexp.compiled_re_stack( frame_reg, stack_reg).search(asmline) if stack_access_match is not None: stack_addr = Util.get_fst_not_none( (stack_access_match.group(regexp.RE_STACK_GR_ADDRESS), stack_access_match.group(regexp.RE_STACK_GR_OFFSET))) stack_pointer_name = stack_access_match.group( regexp.RE_STACK_GR_STACK_POINTER) if stack_addr is None: stack_addr = '0x0' return stack_pointer_name, stack_addr return None
def main(argv=None): # IGNORE:C0111 '''Command line options.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_license = ''' Copyright 2013 Nils Schmidt This file is part of VizAsm. VizAsm is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. VizAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with VizAsm. If not, see <http://www.gnu.org/licenses/>. ''' try: # Setup argument parser action_parser = __create_action_parser() action_args, _ = action_parser.parse_known_args() # None value indicates to list filters but real value is arch list_filters_arch = action_args.list_filters if action_args.list_filters is not None: print SecurityFilters.format_available_filters(list_filters_arch) else: parser = __create_argument_parser(program_license, program_version_message) # process arguments args = parser.parse_args() arch_str = args.arch filters = init(arch_str, args.filters, args.all_filters, args.verbosity, args.quiet, args.graph, args.vlog, parser) # store settings asmfile = args.asmfile SETTINGS = { SETTINGS_ASM_FILEPATH: asmfile, SETTINGS_OUTPUT_FILEPATH: args.output, SETTINGS_GRAPH_FILEPATH: args.graph, SETTINGS_ARCHITECTURE: arch_str, SETTINGS_C_FUNC_HEURISTIC: args.c_func_heuristic, SETTINGS_CNT_SURROUNDING_LINES: args.surrounding_lines, SETTINGS_FILTERS: filters, SETTINGS_READ_ALL_METHODS: args.read_all_methods, SETTINGS_READ_SINGLE_PROCEDURE: args.read_single_procedure, SETTINGS_DONT_SKIP_EXCEPTION: args.dont_skip_exception } set_defaul_settings(SETTINGS) if DEBUG: print Util.pretty_format_dict(SETTINGS) # run program clilog.info('architecture: %s' % arch_str) clilog.info('analyzing file %s ...\n' % asmfile) asm_ana = AsmAnalyzerCli() asm_ana.analyze() return 0 except KeyboardInterrupt: ### handle keyboard interrupt ### return 0 except CLIError as e: clilog.exception(e) except Exception, e: log.exception(e) clilog.exception(e) return 2
def __repr__(self): return '%s(%s)' % (self.__class__.__name__, Util.pretty_format_dict(self.stack, True))
def __repr__(self): return '%s: %s\%s' % (self.__class__.__name__, repr(self.ivar_lookup), Util.pretty_format_dict(self.superclass_dict, True))
def __analyze_inner(self, cpu, method_dict): cnt_filtered_messages = cnt_total_messages = 0 dont_skip_exception = setting_for_key(SETTINGS_DONT_SKIP_EXCEPTION) hopper_annotater = hopanno hopanno.reset() for linenr, line in sorted(method_dict.items()): try: cpu.read_line(line, linenr) if hopper_annotater is not None and hopper_annotater.cur_meth_impl_start is None and cpu.address is not None: hopper_annotater.cur_meth_impl_start = cpu.address except (CpuException, RuntimeError) as e: # annotate exception hopanno.annotate(cpu.address, (HOPANNO_EXCEPTION_STR + (' <- %s: %s ...' % (e.__class__.__name__, str(e))) )[:HOPPER_EXCEPTION_LENGTH]) log.exception(e) if not dont_skip_exception: raise methodcall = cpu.get_method_call() cnt_total_messages += len(methodcall.calls) sa = self.security_analyzer sa.set_methodcall(methodcall) filtered_methodcall = methodcall # build a list of method lines if self.filtering_enabled(): # apply filters # do this after building the list of method lines, # because the lines before and after refer to the unfiltered lines filtered_methodcall = sa.apply_filters() # method as assembler code (string) lines_str_list = list(Util.sorted_dict_values_it(method_dict)) asm_lines = Util.strlist_to_str(lines_str_list) # create attribute dictionaries methodcall_sender_attr_dict, methodcall_calls_attr_list_dict = methodcall.create_gephi_attr_dicts( asm_lines, filtered_methodcall) # add to graph if self.filtering_enabled(): sa.add_to_graph(self.graph, methodcall_sender_attr_dict, methodcall_calls_attr_list_dict, sender_methodcall_edge_attr_dict=None) else: methodcall_sender_attr_dict.update(GraphUtil.viz_dict(size=50.0)) methodcall.add_to_graph(self.graph, methodcall_sender_attr_dict, methodcall_calls_attr_list_dict, sender_methodcall_edge_attr_dict=None) cnt_filtered_messages += len(filtered_methodcall.calls) # write `MethodCall` to file if not self.filtering_enabled(): self.__write_methodcall(filtered_methodcall) # annotate hopper if hopper_annotater is not None: hopper_annotater.annotate_from_methodcall(filtered_methodcall) hopper_annotater.reset() return (cnt_total_messages, cnt_filtered_messages)
def __str__(self): return '%s: %s' % (self.__class__.__name__, Util.pretty_format_dict(self.stack))
def node_color_green(self): return Util.random_rgb_val()
def node_color_blue(self): return Util.random_rgb_val()
def next_regs_for_spret(self, reg_list): ''' Same as `next_reg_for_spret`, but do it for the whole list of `Register` ''' return Util.filter_not_none( (self.next_reg_for_spret(reg) for reg in reg_list))