def methods_it(file_iterator, read_all_procedures=False): ''' Returns an iterator running over the methods. For each method a dictionary is yield with the line number as key and the method line as value. Parameters ---------- read_all_procedures: boolean, optional (default is False) If True, all procedures will be read. If False, only those recognized as a `CategoryClass` or method implementation will be read. See `AsmRegEx.is_method_implementation` for details. file_iterator: iterator<tuple<int, string>> the iterator to use Returns ------- method_dict iterator: iterator<dict<int, string>> ''' re_procedure = AsmRegEx.compiled_re(AsmRegEx.RE_PROCEDURE) # list of lines of the current method method_dict = {} first_method_found = False def init_proper_method(): ''' indicates if the method is a method implementation or a category method ''' return True if read_all_procedures else False proper_method = init_proper_method() first_method_fix = False for linenr, line in file_iterator: procedure_match = re_procedure.search(line) # `sections_it` cuts the lines before the first occurrence of section # use this pattern to detect first method anyway if not first_method_fix: first_method_fix = line.find('; Section') != -1 # check if proper method if not read_all_procedures and not proper_method: proper_method = any((anare.is_method_implementation(line), anare.is_entrypoint(line))) # procedure found if procedure_match is not None: # not the first method_dict if proper_method: yield method_dict method_dict = {} first_method_found = True proper_method = init_proper_method() # start collecting the lines of a method after the first method pattern has been found if first_method_found or (first_method_fix and proper_method): method_dict.update({linenr: line}) # yield rest if proper_method and method_dict: yield method_dict
def compiled_re_stack(stack_pointer_name, frame_pointer_name): ''' Return a compiled regular expression that matches e.g. "[sp, #0x8]" or "[sp], #0x4" Use `RE_STACK_GR_ADDRESS` group key to get the stack address. Parameters ---------- stack_pointer_name: string the name of stack pointer register (e.g. "sp") ''' RE_STACK = r''' \[ # [ (?P<%s>%s|%s) # stack pointer name or frame pointer name ( ( \] ,\s+ # , [#] # # (?P<%s>0x\w+) # 0x8 ) | # or ( ,\s+ # , [#] (?P<%s>0x\w+) # #0x8 \] # ] ) | \] # ] ) ''' % (AsmRegEx_arm.RE_STACK_GR_STACK_POINTER, stack_pointer_name, frame_pointer_name, AsmRegEx_arm.RE_STACK_GR_OFFSET, AsmRegEx_arm.RE_STACK_GR_ADDRESS) return AsmRegEx.compiled_vre(RE_STACK)
def parse_hex(self, asmline): ''' Parse a line containing a hex value and convert it to an int. Parameters ---------- asmline: string Returns ------- int: int hex value converted to int None if regular expression did not classify as hex value ''' hex_match = regexp.compiled_vre(regexp.RE_HEX_VALUE).search(asmline) hex_value, res = None, None if hex_match is not None: hex_value = hex_match.group(regexp.RE_HEX_VALUE_GR_HEX_VALUE) try: res = int(hex_value, 16) except TypeError: log.warn('%s is no hex value!', hex_value) return res
def parse_c_string(self, asmline): ''' Create a `CString` from a c string like e.g. "defaults" ''' c_string_match = regexp.compiled_vre(regexp.RE_C_STRING).search(asmline) if c_string_match is not None: c_string = c_string_match.group(regexp.RE_C_STRING_GR_NAME) return CString(c_string) return None
def parse_nsstring(self, asmline): ''' Create a `NSString` from e.g. @"defaults" ''' string_match = regexp.compiled_vre(regexp.RE_STRING).search(asmline) if string_match is not None: name = string_match.group(regexp.RE_STRING_GR_STRING) return NSString(name) return None
def __read_superclasses_from_sections(section_it): ''' Read the superclasses from the specified section name Parameters ---------- section_it: iterator<tuple<int, string>> iterator over the sections where the superclass infos are stored Returns ------- dict<ObjcClass, ObjcClass> dict with object as key and the superclass as item ''' superclasses_dict = {} for (_, i) in section_it: CLASS_PARENT_MATCH = AsmRegEx.compiled_vre( AsmRegEx.RE_CLASS_PARENT).search(i) if CLASS_PARENT_MATCH is not None: clazzname = CLASS_PARENT_MATCH.group( AsmRegEx.RE_CLASS_PARENT_GR_CLASS) superclazz_name = CLASS_PARENT_MATCH.group( AsmRegEx.RE_CLASS_PARENT_GR_PARENT) superclasses_dict[ObjcClass(clazzname)] = ObjcClass( superclazz_name) return superclasses_dict
def read_assignment(self, asmline): ''' Read an assigment and try to split it. Parameters ---------- asmline : string line of assembler ''' AssignmentMatchingSystem.reset(self) assignment_match = AsmRegEx.compiled_vre( regexp.RE_ASSINGMENT_SPLIT).search(asmline) if assignment_match is not None: self.is_assignment = True val1 = assignment_match.group(regexp.RE_ASSINGMENT_SPLIT_GR_VAL1) val2 = assignment_match.group(regexp.RE_ASSINGMENT_SPLIT_GR_VAL2) pu = self.parse_util # stack push stack_access = pu.parse_stack_access(val1) if stack_access is not None: _, addr = stack_access self.is_stack_push = True self.stack_address = addr # save stack access in args_since_last_call to determine number of stack arguments needed for next call self.stack_usage_since_last_call.add(stack_access) self._log_assignment_split(val1, val2) stackvar1 = pu.parse_stackvar(val1) var_assign1 = pu.parse_var_assignment_without_ivar_ref_from_asmline( val1) # assume classref, frameworkclass, ivar, selector and string cannot exist on the left side var_assign2 = pu.parse_var_assignment_without_ivar_ref_from_asmline( val2) val2_remaining_types = [ pu.parse_ivar, pu.parse_objc_class, pu.parse_selector, pu.parse_string, pu.parse_imp ] # stack fetch stackvar2 = pu.parse_stackvar(val2) if stackvar2 is not None: self.is_stack_fetch = True # replace arg_ with method argument for c function definition self._try_replace_argument_stack_var_c_method(stackvar2, val2) lobject = self._get_val1_value(val1, stackvar1, var_assign1) self.lobject = lobject robject = self._get_val2_value(val2, stackvar2, var_assign2, val2_remaining_types) self.robject = robject # save register in args_since_last_call to determine number of register arguments needed for next call self.add_reg_arg_since_last_call(lobject, robject)
def create_from_asm_line(asmline): ''' Create Selector from asm line like e.g. @selector(objectForKey:) ''' selector_match = AsmRegEx.compiled_vre( AsmRegEx.RE_SELECTOR).search(asmline) if selector_match is not None: selector = Selector( selector_match.group(AsmRegEx.RE_SELECTOR_GR_SELECTOR)) return selector return None
def parse_c_method_name(asmline): ''' Check if is `RE_SUB` or `RE_C_METHOD` and return the appropriate name if available. Otherwise None. Returns ------- Function if `RE_C_METHOD` matches Sub if `RE_SUB` matches ''' re_sub_match = regexp.compiled_vre(regexp.RE_SUB).search(asmline) if re_sub_match: return Sub(re_sub_match.group(regexp.RE_SUB_GR_SUBNAME)) re_c_method_match = regexp.compiled_vre(regexp.RE_C_METHOD).search(asmline) if re_c_method_match: return Function(re_c_method_match.group(regexp.RE_C_METHOD_GR_NAME)) return None
def parse_meth_impl(asmline): ''' Parse a method implementation like e.g. "methImpl_AppDelegate_applicationDidFinishLaunching_". For more details see `RE_METH_IMPL`. Returns ------- MethodImplementation ''' meth_impl_match = regexp.compiled_vre(regexp.RE_METH_IMPL).search(asmline) if meth_impl_match is not None: classname = meth_impl_match.group(regexp.RE_METH_IMPL_GR_CLASS) selectorname = meth_impl_match.group(regexp.RE_METH_IMPL_GR_SELECTOR) selector = Selector.selector_from_underscore_delimiter(selectorname) is_static = regexp.method_implementation_is_static(meth_impl_match.group(regexp.RE_METH_IMPL_GR_STATIC)) if classname is not None: objc_class = ObjcClass(classname, is_static = is_static) return MethodImplementation(objc_class, [selector], is_static) return None
def parse_own_c_method_called(asmline): re_own_c_method_call_match = regexp.compiled_vre(regexp.RE_OWN_C_METHOD_CALLED).search(asmline) if re_own_c_method_call_match: func_name = re_own_c_method_call_match.group(regexp.RE_OWN_C_METHOD_BASE_GR_NAME) func_args_str = re_own_c_method_call_match.group(regexp.RE_OWN_C_METHOD_BASE_GR_ARGS) if func_args_str is not None: func_args_list = re_own_c_method_call_match.group(regexp.RE_OWN_C_METHOD_BASE_GR_ARGS).split(', ') else: func_args_list = [] return Function(func_name, func_args_list) return None
def parse_format_string(self, asmline): ''' Create a `FormatString` from e.g. @"%s%d" ''' string_match = regexp.compiled_vre(regexp.RE_FORMAT_STRING).search(asmline) if string_match is not None: name = string_match.group(regexp.RE_FORMAT_STRING_GR_STRING) is_objc_format_string = string_match.group(regexp.RE_FORMAT_STRING_GR_OBJC_STRING) if not is_objc_format_string: return CFormatString(CString(name)) else: return FormatString(NSString(name)) return None
def _read_basic_block(self, asmline): ''' Read a Basic Block like " ; Basic Block Input Regs: rax rdx rsi rdi - Killed Regs: rax rcx rdx rsp rbp rsi rdi r8" and kill the specified registers. ''' basic_block_match = AsmRegEx.compiled_vre( AsmRegEx.RE_BASIC_BLOCK).search(asmline) if basic_block_match is not None: killed_regs = basic_block_match.group( AsmRegEx.RE_BASIC_BLOCK_KILLED_REGS) killed_regs = killed_regs.split(' ') self.memory.registers.delete_elements(killed_regs)
def compiled_re_stack(stack_pointer_name): ''' Return a compiled regular expression that matches e.g. "qword [ss:rsp+0x8]" Use `RE_STACK_GR_ADDRESS` group key to get the stack address. Parameters ---------- stack_pointer_name: string the name of stack pointer register (e.g. "rsp") ''' RE_STACK = r''' \[ss: # [ss: (?P<%s>%s) # stack pointer name (\+ # + (?P<%s>0x\w+))* # 0x8 or empty -> 0x0 \] # ] ''' % (AsmRegEx_x86_64.RE_STACK_GR_REG, stack_pointer_name, AsmRegEx_x86_64.RE_STACK_GR_ADDRESS) return AsmRegEx.compiled_vre(RE_STACK)
def create_from_asm_line(asmline): ''' Create a `MethodImplementation` from an assembler line like e.g " +[NSURLRequest(AnyHttpsCert) allowsAnyHTTPSCertificateForHost:]_100002120:" Returns a `MethodImplementation` where the class is a `CategoryClass`. ''' msg_send = None category_match = AsmRegEx.compiled_vre( AsmRegEx.RE_CATEGORY).search(asmline) if category_match is not None: category_on_class = category_match.group( AsmRegEx.RE_CATEGORY_ON_CLASS) classname = category_match.group(AsmRegEx.RE_CATEGORY_CLASS) category_is_static = category_match.group( AsmRegEx.RE_CATEGORY_STATIC_SYMBOL ) == AsmRegEx.RE_CATEGORY_SYMBOL_STATIC selectorname = category_match.group(AsmRegEx.RE_CATEGORY_SELECTOR) selector = Selector(selectorname) cclass = CategoryClass(classname, category_on_class) msg_send = MethodImplementation(cclass, [selector], is_static=category_is_static) return msg_send