def _parse_function_type(type_string: str, self_name: str, view: BinaryView, is_class=False) -> Type: log_debug(f'_parse_function_type {type_string}') ret_type_str = type_string[0] # Handle structures defined in the function types if ret_type_str == '{': ret_type, type_string = _parse_structure(type_string[1:], view) else: ret_type = _lookup_type(ret_type_str, view) type_string = type_string[1:] stack_size = ''.join(takewhile(str.isdigit, type_string)) type_string = type_string[len(stack_size):] stack_size = int(stack_size) if stack_size else None args = [] while type_string: if type_string[0] == '{': arg_type, type_string = _parse_structure(type_string[1:], view) args.append(Type.pointer(view.arch, arg_type)) else: arg_type = ''.join( takewhile(lambda i: not str.isdigit(i), type_string)) type_string = type_string[len(arg_type):] args.append(_lookup_type(arg_type, view)) arg_stack_offset = ''.join(takewhile(str.isdigit, type_string)) type_string = type_string[len(arg_stack_offset):] # we know that the first parameter is the 'self' parameter if it's not # an objc_msgSend_stret or objc_msgSendSuper_stret. Otherwise it's the # second one. if ret_type.type_class == TypeClass.NamedTypeReferenceClass: log_debug(f'return value is {ret_type}') ret_type = Type.pointer(view.arch, ret_type) args.insert(0, FunctionParameter(ret_type, 'ret_value')) if len(args) < 2: args.append(None) args[1] = FunctionParameter( Type.pointer( view.arch, (Type.named_type_from_type(self_name, view.types[self_name]) if not is_class else Type.named_type_from_type( 'class_t', view.get_type_by_name('class_t')))), 'self') else: args[0] = FunctionParameter( Type.pointer( view.arch, (Type.named_type_from_type(self_name, view.types[self_name]) if not is_class else Type.named_type_from_type( 'class_t', view.get_type_by_name('class_t')))), 'self') function_type = Type.function(ret_type, args) return function_type
def _add_vmt_methods(bv: BinaryView, vmt: DelphiVMT, out_struct: types.Structure) -> bool: offset_ptr_size = vmt.offset_ptr_size if not vmt.seek_to_vmt_offset(vmt.vmt_offsets.cVmtParent + offset_ptr_size): return False for _ in range(len(vmt.virtual_methods)): value = vmt.read_ptr() if value == 0: continue if value not in vmt.virtual_methods: prev_offset = vmt.br_offset - offset_ptr_size raise RuntimeError( f'Invalid method address detected at 0x{prev_offset:08x} ({vmt.class_name})') # Create function if not exists if bv.get_function_at(value) is None: bv.create_user_function(value) function = bv.get_function_at(value) # Set method name if not already set function_name = function.name method_name = vmt.virtual_methods[value] if function_name.startswith('sub_'): bv.define_user_symbol(Symbol( SymbolType.FunctionSymbol, value, method_name )) # Add field to structure field_type = Type.pointer( bv.arch, Type.function( function.return_type, [(Type.void() if x.type is None else x.type) for x in function.parameter_vars], function.calling_convention ) ) field_name = method_name.split('.')[-1] out_struct.append(field_type, field_name) return True
def fix_tailcall(self, func, function_parameter_list, return_type, func_cc): # PE's sometimes use a tailcall that jumps to the IAT\GOT entry of the function. # This function will label the tailcall as the same type as the function its jumping into (since its not # changing any registers\variables). xref_list = self.bv.get_code_refs(func.start) if len(xref_list) == 1: caller_func = xref_list[0].function if caller_func.mlil[0].operation == enums.MediumLevelILOperation.MLIL_TAILCALL: # If the first instruction is a tailcall, then change the functions' type to the called function type try: function_type = Type.function(return_type, function_parameter_list, func_cc) caller_func.set_user_type(function_type) caller_func.name = '__j_' + func.name return True except Exception as e: print("Failed to fix the tailcall to function: ", func) print(str(e)) return False return True
def fix_printfs(view: BinaryView): printf = view.get_symbols_by_name('_printf') if not printf: printf = view.get_symbols_by_name('printf') if not printf: return for sym in printf: function = view.get_function_at(sym.address) if not function: continue xrefs = view.get_code_refs(function.start) for xref in xrefs: caller: Function = xref.function call_mlil = caller.get_low_level_il_at(xref.address).mlil print(call_mlil) if call_mlil is None: continue fmt_operand = call_mlil.params[0] if fmt_operand.operation == MediumLevelILOperation.MLIL_VAR: log.log_warn( f"Potential format string bug: {fmt_operand.address:x}") continue elif fmt_operand.operation in ( MediumLevelILOperation.MLIL_CONST_PTR, MediumLevelILOperation.MLIL_CONST): fmt_address = fmt_operand.constant fmt = view.get_ascii_string_at(fmt_address, 2) if fmt is None: continue fmt_value = fmt.value else: continue specifiers = fmt_value.split('%') param_types = [] for specifier in specifiers[1:]: if not specifier: continue if specifier.startswith('d'): param_types.append(Type.int(4, sign=True)) elif specifier.startswith('s'): param_types.append(Type.pointer(view.arch, Type.char())) elif specifier.startswith('p'): param_types.append(Type.pointer(view.arch, Type.void())) else: log.log_warn( f'Unknown format specifier: {specifier}; skipping') param_types.append(Type.pointer(view.arch, Type.void())) param_idx = 1 params = [ FunctionParameter(Type.pointer(view.arch, Type.char()), 'fmt') ] for param in param_types: params.append(FunctionParameter(param, f'arg{param_idx}')) param_idx += 1 caller.set_call_type_adjustment(xref.address, Type.function(Type.int(4), params))
def FUNCTION_DECL_handler(self, current_node_hash): with self.driver.session() as session: result = session.run("MATCH (func:FUNCTION_DECL {Hash: {current_node_hash}})-[:FunctionArgument]->" "(func_param:PARM_DECL) " "RETURN func_param.Hash as param_hash, " " func_param.TypeDefinition as type_definition," " func_param.TypeName as type_name ", current_node_hash=current_node_hash) # Parse function arguments function_parameter_list = list() if result.peek(): for record in result: # If parameter type is already defined move on to the next parameter if not self.type_definition_cache.get(record['param_hash']): if self.insert_type_definition_into_binaryView('PARM_DECL', record['param_hash']): self.type_definition_cache[record['param_hash']] = True else: print("Failed to insert definition for function parameter ", record['type_name']) return False try: # Define the Function parameter binaryNinja object function_parameter_list.append( types.FunctionParameter(self.bv.get_type_by_name(record['type_name']), record['type_name'])) except Exception as e: print("FUNCTION_DECL_handler: Failed to process function parameter" + str(e)) return False # Parse return value and function name result = session.run("MATCH (func:FUNCTION_DECL {Hash: {current_node_hash}})-[:ReturnType]->" "(return_type) " "RETURN return_type.Hash as return_hash, " " labels(return_type)[0] as return_label, " " return_type.TypeDefinition as type_definition, " " return_type.TypeName as type_name ", current_node_hash=current_node_hash) return_type = types.Type.void() if result.peek(): for record in result: # If return type is already defined move on, otherwise define it if not self.type_definition_cache.get(record['return_hash']): if self.insert_type_definition_into_binaryView(record['return_label'], record['return_hash']): self.type_definition_cache[record['return_hash']] = True else: print("Failed to insert definition for function return value ", record['type_name']) return False try: # Define the Function return value binaryNinja type object if record['type_definition'] == 'PointerTo': # Return type is a pointer var_type, name = self.bv.parse_type_string(record['type_name'][:-1]) return_type = Type.pointer(self.bv.arch, var_type) else: var_type, name = self.bv.parse_type_string( record['type_definition'] + " " + record['type_name']) self.bv.define_user_type(name, var_type) return_type = Type.named_type_from_type(name, self.bv.get_type_by_name(name)) except Exception as e: print("FUNCTION_DECL_handler: Failed to process return value, " + str(e)) return False else: # A function might not have any return value or arguments pass # Define the function itself result = session.run("MATCH (func:FUNCTION_DECL {Hash: {current_node_hash}}) " "RETURN func.TypeName as func_name", current_node_hash=current_node_hash) if result.peek(): func_name = result.peek()['func_name'] try: for func in self.bv.functions: if func.name == func_name: func_cc = func.calling_convention function_type = Type.function(return_type, function_parameter_list, func_cc) func.set_user_type(function_type) if not self.fix_tailcall(func, function_parameter_list, return_type, func_cc): return False self.type_definition_cache[current_node_hash] = True except Exception as e: print("Failed to process function :", func_name) print(str(e)) return False print("Successfully defined function: ", func_name) return True
) LPCSTR_type = Type.pointer(Architecture["x86"], Type.char()) LPCSTR = Type.named_type_from_type('LPCSTR', LPCSTR_type) DWORD = Type.int(4, sign=False, altname="DWORD") advapi32_x86.add_named_type("HCRYPTPROV", HCRYPTPROV_type) CryptAcquireContextA = Type.function( BOOL, [ FunctionParameter( Type.pointer(Architecture["x86"], HCRYPTPROV), "phProv" ), FunctionParameter(LPCSTR, "szContainer"), FunctionParameter(LPCSTR, "szProvider"), FunctionParameter(DWORD, "dwProvType"), FunctionParameter(DWORD, "dwFlags"), ], calling_convention=Platform['windows-x86'].stdcall_calling_convention ) CryptReleaseContext = Type.function( BOOL, [ FunctionParameter( HCRYPTPROV, 'hProv' ), FunctionParameter( DWORD, 'dwFlags'