Repository: mandiant/capa
File: capa/features/extractors/ida/insn.py (Python)

Code before:
        for name in capa.features.extractors.helpers.generate_symbols(api[0], api[1]):
            yield API(name), ih.address
        # a call instruction should only call one function, stop if a call to an import is extracted
        return

    # check call to extern functions
    api = check_for_api_call(insn, get_externs(fh.ctx))
    if api:
        # tuple (<module>, <function>, <ordinal>)
        yield API(api[1]), ih.address
        # a call instruction should only call one function, stop if a call to an extern is extracted
        return

    # extract dynamically resolved APIs stored in renamed globals (renamed for example using `renimp.idc`)
    if insn.Op1.type == ida_ua.o_mem:
        op_addr = insn.Op1.addr
        op_name = idaapi.get_name(op_addr)
        # when renaming a global using an API name, IDA assigns it the function type
        # ensure we do not extract something wrong by checking that the address has a name and a type
        # we could check that the type is a function definition, but that complicates the code
        if (not op_name.startswith("off_")) and idc.get_type(op_addr):
            # Remove suffix used in repeated names, for example _0 in VirtualFree_0
            match = re.match(r"(.+)_\d+", op_name)
            if match:
                op_name = match.group(1)
            # We global name does not include the DLL name, so we can't extract it
            for name in capa.features.extractors.helpers.generate_symbols("", op_name):
                yield API(name), ih.address

    # extract IDA/FLIRT recognized API functions
    targets = tuple(idautils.CodeRefsFrom(insn.ea, False))
    if not targets:
        return

    target = targets[0]
    target_func = idaapi.get_func(target)
    if not target_func or target_func.start_ea != target:
        # not a function (start)
        return

    if target_func.flags & idaapi.FUNC_LIB:
        name = idaapi.get_name(target_func.start_ea)
        yield API(name), ih.address
        if name.startswith("_"):
            # some linkers may prefix linked routines with a `_` to avoid name collisions.
            # extract features for both the mangled and un-mangled representations.
            # e.g. `_fwrite` -> `fwrite`
            # see: https://stackoverflow.com/a/2628384/87207
            yield API(name[1:]), ih.address



Review comment (refactor):
```suggestion
            # global name does not include the DLL name, so we can't extract it
```

Code after:
        for name in capa.features.extractors.helpers.generate_symbols(api[0], api[1]):
            yield API(name), ih.address
        # a call instruction should only call one function, stop if a call to an import is extracted
        return

    # check call to extern functions
    api = check_for_api_call(insn, get_externs(fh.ctx))
    if api:
        # tuple (<module>, <function>, <ordinal>)
        yield API(api[1]), ih.address
        # a call instruction should only call one function, stop if a call to an extern is extracted
        return

    # extract dynamically resolved APIs stored in renamed globals (renamed for example using `renimp.idc`)
    # examples: `CreateProcessA`, `HttpSendRequestA`
    if insn.Op1.type == ida_ua.o_mem:
        op_addr = insn.Op1.addr
        op_name = idaapi.get_name(op_addr)
        # when renaming a global using an API name, IDA assigns it the function type
        # ensure we do not extract something wrong by checking that the address has a name and a type
        # we could check that the type is a function definition, but that complicates the code
        if (not op_name.startswith("off_")) and idc.get_type(op_addr):
            # Remove suffix used in repeated names, for example _0 in VirtualFree_0
            match = re.match(r"(.+)_\d+", op_name)
            if match:
                op_name = match.group(1)
            # the global name does not include the DLL name, so we can't extract it
            for name in capa.features.extractors.helpers.generate_symbols("", op_name):
                yield API(name), ih.address

    # extract IDA/FLIRT recognized API functions
    targets = tuple(idautils.CodeRefsFrom(insn.ea, False))
    if not targets:
        return

    target = targets[0]
    target_func = idaapi.get_func(target)
    if not target_func or target_func.start_ea != target:
        # not a function (start)
        return

    if target_func.flags & idaapi.FUNC_LIB:
        name = idaapi.get_name(target_func.start_ea)
        yield API(name), ih.address
        if name.startswith("_"):
            # some linkers may prefix linked routines with a `_` to avoid name collisions.
            # extract features for both the mangled and un-mangled representations.
            # e.g. `_fwrite` -> `fwrite`
            # see: https://stackoverflow.com/a/2628384/87207
            yield API(name[1:]), ih.address