Beispiel #1
0
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
Beispiel #2
0
 def __init__(self, arch: Architecture):
     self.cVmtSelfPtr = Type.pointer(arch, Type.void())
     self.cVmtIntfTable = Type.pointer(arch, Type.void())
     self.cVmtAutoTable = Type.pointer(arch, Type.void())
     self.cVmtInitTable = Type.pointer(arch, Type.void())
     self.cVmtTypeInfo = Type.pointer(arch, Type.void())
     self.cVmtFieldTable = Type.pointer(arch, Type.void())
     self.cVmtMethodTable = Type.pointer(arch, Type.void())
     self.cVmtDynamicTable = Type.pointer(arch, Type.void())
     self.cVmtClassName = Type.pointer(arch, Type.void())
     self.cVmtInstanceSize = Type.int(arch.address_size)
     self.cVmtParent = Type.pointer(arch, Type.void())
Beispiel #3
0
    def visit_MLIL_CONST(self, expr):
        view = expr.function.source_function.view
        class_symbol = view.get_symbol_at(expr.constant)

        if class_symbol is None:
            return

        log_debug(class_symbol.name)

        class_match = re.match(
            r'_OBJC_(META)?CLASS_\$_(?P<classname>[_A-Za-z0-9=/]+)(@GOT)?',
            class_symbol.name
        )

        class_name = class_match.group('classname')

        class_type = view.types.get(class_name)

        if class_type is None:
            view.define_user_type(class_name, Type.structure_type(Structure()))
            class_type = view.types.get(class_name)

        return Type.pointer(
            view.arch,
            Type.named_type_from_type(class_name, class_type)
        )
Beispiel #4
0
    def __create_class_name_type(bv: BinaryView, vmt: DelphiVMT, out_struct: types.Structure) -> bool:
        vmt_offsets = vmt.vmt_offsets

        if not vmt.seek_to_vmt_offset(vmt_offsets.cVmtClassName):
            return False

        class_name_ptr = vmt.read_ptr()

        if class_name_ptr is None:
            return False

        if not vmt.seek_to_code(class_name_ptr):
            return False

        class_name_len = vmt.read8()

        if class_name_len is None:
            return False

        class_name_struct = types.Structure()
        class_name_struct.append(Type.int(1, False), 'length')
        class_name_struct.append(Type.array(Type.char(), class_name_len), 'value')
        struct_type = Type.structure_type(class_name_struct)

        bv.define_user_data_var(class_name_ptr, struct_type)
        out_struct.append(Type.pointer(bv.arch, struct_type), 'cVmtClassName')

        # Create Symbole for class name
        class_name_sym = Symbol(SymbolType.DataSymbol, class_name_ptr, f'{vmt.class_name}Name')
        bv.define_user_symbol(class_name_sym)

        return True
Beispiel #5
0
def _define_classes(view: BinaryView, class_t: Type):
    __objc_data = view.sections.get('__objc_data')

    if __objc_data is None:
        raise KeyError('This binary has no __objc_data section')

    for addr in range(__objc_data.start, __objc_data.end, class_t.width):
        current_class = Class.from_address(addr, view)

        log_debug(f"Created {current_class}")

    __objc_classrefs = view.sections.get('__objc_classrefs')

    if __objc_classrefs is None:
        raise KeyError('This binary has no __objc_classrefs section')

    for addr in range(__objc_classrefs.start, __objc_classrefs.end,
                      view.address_size):
        view.define_user_data_var(addr, Type.pointer(view.arch, class_t))

        class_addr = int.from_bytes(
            view.read(addr, view.address_size),
            "little" if view.endianness is Endianness.LittleEndian else "big")

        class_ = view.session_data['ClassList'].get(
            class_addr) if class_addr else None

        if class_ is not None:
            log_debug(f"{addr:x} points to {class_!r}")
            view.define_user_symbol(
                Symbol(SymbolType.DataSymbol, addr,
                       f"_OBJC_CLASS_$_{class_.vtable.name}@GOT"))
Beispiel #6
0
def create_vtable(view, vtable_name, vtable_address, max_funcs = 64):
    pointer_t = BinjaStruct.Pointer(view)

    funcs = list()

    for i in range(max_funcs):
        func_pointer_address = vtable_address + (i * view.address_size)
        func_address, _ = pointer_t.read(view, func_pointer_address)

        if func_address is None:
            break

        if not view.is_offset_executable(func_address):
            break

        func = view.get_function_at(func_pointer_address)

        if func is None:
            if i and view.get_code_refs(func_pointer_address, view.address_size):
                break

        funcs.append(func_address)

    if funcs:
        view.define_user_symbol(Symbol(SymbolType.DataSymbol, vtable_address, vtable_name))
        view.define_user_data_var(vtable_address, Type.array(Type.pointer(view.arch, Type.void(), const = True), len(funcs)))

    return funcs
    def from_address(cls, address: int, view: BinaryView):
        if address == 0:
            return None

        from_bytes = get_from_bytes(view)

        protocol_list_t_type = view.get_type_by_name('protocol_list_t')

        protocol_list_t = Type.named_type_from_type('protocol_list_t',
                                                    protocol_list_t_type)

        protocol_t = view.get_type_by_name('protocol_t')

        members = get_structure_members(address, protocol_list_t_type, view)

        view.define_user_data_var(address, protocol_list_t)

        protocols = {}
        start = address + protocol_list_t_type.width
        end = start + members['count'] * view.address_size
        step = view.address_size
        for protocol_ptr in range(start, end, step):
            if not view.get_data_var_at(protocol_ptr):
                view.define_user_data_var(protocol_ptr,
                                          Type.pointer(view.arch, protocol_t))
            protocol = Protocol.from_address(
                from_bytes(view.read(protocol_ptr, view.address_size)), view)

            protocols[protocol.name] = protocol

        return cls(address, **members, protocols=protocols)
Beispiel #8
0
    def update_memory_view(self):
        if self.adapter == None:
            raise Exception('missing adapter')
        if self.memory_view == None:
            raise Exception('missing memory_view')

        for symbol in self.old_symbols:
            # Symbols are immutable so just destroy the old one
            self.memory_view.undefine_auto_symbol(symbol)

        for dv in self.old_dvs:
            self.memory_view.undefine_data_var(dv)

        self.old_symbols = []
        self.old_dvs = set()
        new_dvs = set()

        for (reg, addr) in self.registers:
            bits = self.registers.bits(reg)
            symbol_name = '$' + reg
            self.memory_view.define_auto_symbol(
                Symbol(SymbolType.ExternalSymbol,
                       addr,
                       symbol_name,
                       namespace=symbol_name))
            self.old_symbols.append(
                self.memory_view.get_symbol_by_raw_name(symbol_name,
                                                        namespace=symbol_name))
            self.memory_view.define_data_var(addr,
                                             Type.int(bits // 8, sign=False))
            self.old_dvs.add(addr)

        # Special struct for stack frame
        if self.remote_arch.name == 'x86_64':
            width = self.registers['rbp'] - self.registers[
                'rsp'] + self.remote_arch.address_size
            if width > 0:
                if width > 0x1000:
                    width = 0x1000
                struct = Structure()
                struct.type = StructureType.StructStructureType
                struct.width = width
                for i in range(0, width, self.remote_arch.address_size):
                    var_name = "var_{:x}".format(width - i)
                    struct.insert(i, Type.pointer(self.remote_arch,
                                                  Type.void()), var_name)
                self.memory_view.define_data_var(self.registers['rsp'],
                                                 Type.structure_type(struct))
                self.memory_view.define_auto_symbol(
                    Symbol(SymbolType.ExternalSymbol,
                           self.registers['rsp'],
                           "$stack_frame",
                           raw_name="$stack_frame"))

                self.old_symbols.append(
                    self.memory_view.get_symbol_by_raw_name("$stack_frame"))
                self.old_dvs.add(self.registers['rsp'])
        else:
            pass
Beispiel #9
0
	def update_memory_view(self):
		if self.adapter == None:
			raise Exception('missing adapter')
		if self.memory_view == None:
			raise Exception('missing memory_view')

		addr_regs = {}
		reg_addrs = {}

		for reg in self.adapter.reg_list():
			addr = self.adapter.reg_read(reg)
			reg_symbol_name = '$' + reg

			if addr not in addr_regs.keys():
				addr_regs[addr] = [reg_symbol_name]
			else:
				addr_regs[addr].append(reg_symbol_name)
			reg_addrs[reg] = addr

		for symbol in self.old_symbols:
			# Symbols are immutable so just destroy the old one
			self.memory_view.undefine_auto_symbol(symbol)

		for dv in self.old_dvs:
			self.memory_view.undefine_data_var(dv)

		self.old_symbols = []
		self.old_dvs = set()
		new_dvs = set()

		for (reg, addr) in reg_addrs.items():
			symbol_name = '$' + reg
			self.memory_view.define_auto_symbol(Symbol(SymbolType.ExternalSymbol, addr, symbol_name, namespace=symbol_name))
			self.old_symbols.append(self.memory_view.get_symbol_by_raw_name(symbol_name, namespace=symbol_name))
			new_dvs.add(addr)

		for new_dv in new_dvs:
			self.memory_view.define_data_var(new_dv, Type.int(8))
			self.old_dvs.add(new_dv)

		# Special struct for stack frame
		if self.bv.arch.name == 'x86_64':
			width = reg_addrs['rbp'] - reg_addrs['rsp'] + self.bv.arch.address_size
			if width > 0:
				if width > 0x1000:
					width = 0x1000
				struct = Structure()
				struct.type = StructureType.StructStructureType
				struct.width = width
				for i in range(0, width, self.bv.arch.address_size):
					var_name = "var_{:x}".format(width - i)
					struct.insert(i, Type.pointer(self.bv.arch, Type.void()), var_name)
				self.memory_view.define_data_var(reg_addrs['rsp'], Type.structure_type(struct))
				self.memory_view.define_auto_symbol(Symbol(SymbolType.ExternalSymbol, reg_addrs['rsp'], "$stack_frame", raw_name="$stack_frame"))

				self.old_symbols.append(self.memory_view.get_symbol_by_raw_name("$stack_frame"))
				self.old_dvs.add(reg_addrs['rsp'])
		else:
			raise NotImplementedError('only x86_64 so far')
Beispiel #10
0
    def from_address(cls, address: int, class_name: str, view: BinaryView):
        if address == 0:
            return None

        from_bytes = get_from_bytes(view)

        ivar_t_type = view.types['ivar_t']
        ivar_t = Type.named_type_from_type(
            'ivar_t', ivar_t_type
        )

        members = get_structure_members(address, ivar_t_type, view)
        member_dict = {m.name: m for m in ivar_t_type.structure.members}

        # x64 uses uint64_t for offset, but everything else
        # uses uint32_t
        ivar_offset_type = (
            member_dict['offset'].type.target
            if view.arch != Architecture['x86_64']
            else Type.int(8, False)
        )
        ivar_offset_type.const = True

        if view.get_data_var_at(address) is None:
            view.define_user_data_var(address, ivar_t)

        if members['name'] != 0:
            name_string = view.get_ascii_string_at(members['name'], 1)
            if name_string is not None:
                members['name'] = name_string.value
        else:
            members['name'] = ''

        if members['type']:
            type_string = view.get_ascii_string_at(members['type'], 1).value
            members['type'] = _lookup_type(type_string, view)

        if not members['type']:
            members['type'] = Type.pointer(view.arch, Type.void())

        if members['offset']:
            view.define_user_data_var(members['offset'], ivar_offset_type)
            view.define_user_symbol(
                Symbol(
                    SymbolType.DataSymbol,
                    members['offset'],
                    f'{members["name"]}_offset',
                    namespace=class_name
                )
            )
            members['offset'] = from_bytes(
                view.read(members['offset'],
                          member_dict['offset'].type.target.width)
            )
        else:
            members['offset'] = None

        return cls(address, **members)
Beispiel #11
0
def parse_get_class(view: BinaryView, objc_getClass: int):
    log_debug(f"parse_get_class(view, {objc_getClass:x})")
    xrefs = view.get_code_refs(objc_getClass)

    for xref in xrefs:
        mlil = xref.function.mlil
        get_class_call = xref.function.get_low_level_il_at(xref.address).mlil

        if not get_class_call.params:
            continue

        class_param = get_class_call.params[0]
        log_debug(f"class_param is {class_param.operation!r}")

        if class_param.operation in (
                MediumLevelILOperation.MLIL_CONST,
                MediumLevelILOperation.MLIL_CONST_PTR):
            class_name_ptr = view.get_ascii_string_at(class_param.constant, 1)
            if class_name_ptr is None:
                continue

            class_name = class_name_ptr.value

            cls_ = Type.named_type_from_type(
                class_name,
                view.types.get(class_name)
            )

            if cls_ is None:
                continue

            cls_ptr = Type.pointer(view.arch, cls_)

            output = get_class_call.output[0] if get_class_call.output else None

            if output is None:
                continue

            log_debug(f"Updating {output!r} to {cls_ptr}")
            xref.function.create_user_var(output, cls_ptr, output.name)
            log_debug(f"Now {output!r}")

            # Update any variable that is directly set to this variable (but not if
            # the variable just happened to be used in the expression)
            for use in mlil.get_ssa_var_uses(get_class_call.ssa_form.output.dest[0]):
                log_debug(f"Checking {use!r}")
                if use.operation != MediumLevelILOperation.MLIL_SET_VAR:
                    continue

                if use.src.operation != MediumLevelILOperation.MLIL_VAR:
                    continue

                log_debug(f"Updating {use.dest!r} to {cls_ptr}")
                xref.function.create_user_var(use.dest, cls_ptr, use.dest.name)
                log_debug(f"Now {use.dest!r}")
Beispiel #12
0
def _lookup_type(type_string: str, view: BinaryView):
    if type_string in basic_types:
        return basic_types[type_string]
    elif type_string == '*':
        return Type.pointer(view.arch, Type.char())
    elif type_string.startswith('@'):
        if type_string[2:-1] in view.types:
            return Type.pointer(
                view.arch,
                Type.named_type_from_type(
                    type_string[2:-1],
                    view.types[type_string[2:-1]]
                )
            )
        elif type_string != '@?' and type_string != '@':
            if type_string[2:-1]:
                new_type = Type.named_type_from_type(
                    type_string[2:-1], Type.structure_type(Structure()))
                view.define_user_type(type_string[2:-1], new_type)
            else:
                new_type = Type.void()
            return Type.pointer(view.arch, new_type)
        else:
            return Type.pointer(view.arch, Type.void())
    elif type_string.startswith('#'):
        return Type.pointer(view.arch, Type.void())
    elif type_string == ':':
        return view.types['SEL']
    else:
        return Type.pointer(view.arch, Type.void())
Beispiel #13
0
def define_cfstrings_plugin(view: BinaryView):
    log_debug("define_cfstrings_plugin")

    from_bytes = _get_from_bytes(view)

    cfstring_type = view.get_type_by_name('CFString')
    if cfstring_type is None:
        cfstring_type = view.platform.parse_types_from_source(
            _cfstring_definition).types['CFString']

        view.define_user_type('CFString', cfstring_type)

        wchar_type = view.platform.parse_types_from_source(
            _wchar_definition).types['wchar']

    cfstring = Type.named_type_from_type('CFString', cfstring_type)

    __cfstring = view.get_section_by_name('__cfstring')

    if __cfstring is None:
        return

    buffer = cfstring_type.structure['buffer']
    length = cfstring_type.structure['length']

    for addr in range(__cfstring.start, __cfstring.end, cfstring_type.width):
        view.define_user_data_var(addr, cfstring)

        for xref in view.get_data_refs(addr):
            view.define_user_data_var(xref, Type.pointer(view.arch, cfstring))

        string_pointer = from_bytes(
            view.read(addr + buffer.offset, buffer.type.width))

        string_length = from_bytes(
            view.read(addr + length.offset, length.type.width), ) + 1

        string_section = view.get_sections_at(string_pointer)

        if not string_section:
            return

        if string_section[0].name == '__ustring':
            char_type = wchar_type
        else:
            char_type = Type.char()

        view.define_user_data_var(string_pointer,
                                  Type.array(char_type, string_length))
Beispiel #14
0
    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
Beispiel #15
0
def _define_protocols(view: BinaryView):
    __objc_protorefs = view.get_section_by_name('__objc_protorefs')

    if __objc_protorefs is None:
        return

    protocol_t = Type.named_type_from_type('protocol_t',
                                           view.get_type_by_name('protocol_t'))

    for address in range(__objc_protorefs.start, __objc_protorefs.end,
                         view.address_size):
        view.define_user_data_var(address, Type.pointer(view.arch, protocol_t))

        protocol_ptr = int.from_bytes(
            view.read(address, view.address_size),
            "little" if view.endianness is Endianness.LittleEndian else "big")

        new_protocol = Protocol.from_address(protocol_ptr, view)
Beispiel #16
0
def _define_categories(view: BinaryView):
    __objc_catlist = view.sections.get('__objc_catlist')

    if __objc_catlist is None:
        return

    category_t = Type.named_type_from_type('category_t',
                                           view.types['category_t'])

    for address in range(__objc_catlist.start, __objc_catlist.end,
                         view.address_size):
        view.define_user_data_var(address, Type.pointer(view.arch, category_t))

        category_ptr = int.from_bytes(
            view.read(address, view.address_size),
            "little" if view.endianness is Endianness.LittleEndian else "big")

        new_category = Category.from_address(category_ptr, view)
Beispiel #17
0
    def define_type(self):
        structure = Structure()
        structure.type = StructureType.ClassStructureType
        structure.width = self.vtable.instanceSize

        structure.insert(0, Type.pointer(self._view.arch, Type.void()), 'isa')

        classes = [self]
        current_superclass = self.superclass
        while current_superclass:
            classes.append(current_superclass)
            current_superclass = current_superclass.superclass

        while classes:
            current_class = classes.pop()
            if current_class.vtable.ivars is None:
                continue

            ivar_list = current_class.vtable.ivars
            for name, ivar in ivar_list.ivars.items():
                structure.insert(ivar.offset, ivar.type, name)

        self._view.define_user_type(self.vtable.name,
                                    Type.structure_type(structure))
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))
Beispiel #19
0
    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
    Type,
    TypeLibrary,
    Structure
)

advapi32_x86 = TypeLibrary.new(Architecture["x86"], "advapi32.dll")

advapi32_x86.add_platform(Platform["windows-x86"])

BOOL = Type.int(4, altname="BOOL")
HCRYPTPROV_type = Type.structure_type(Structure())
HCRYPTPROV = Type.named_type_from_type(
    "HCRYPTPROV", HCRYPTPROV_type
)

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"),