import idc
import idautils


def enum_segments():
    for segstart in idautils.Segments():
        segend = idc.get_segm_end(segstart)
        segname = idc.get_segm_name(segstart)
        yield segstart, segend, segname


def find_pointers(start, end):
    for va in range(start, end-0x4):
        ptr = idc.get_wide_dword(va)
        if idc.get_segm_start(ptr) == idc.BADADDR:
            continue

        yield va, ptr


def is_head(va):
    return ida_bytes.is_head(idc.get_full_flags(va))


def get_head(va):
    if is_head(va):
        return va
    else:
        return idc.prev_head(va)


def is_code(va):
    if is_head(va):
        flags = idc.get_full_flags(va)
        return ida_bytes.is_code(flags)
    else:
        head = get_head(va)
        return is_code(head)


CACHED_STRINGS = list(idautils.Strings())
def is_in_string(va):
    for s in CACHED_STRINGS:
        if s.ea <= va < s.ea + s.length:
            return True
    return False


def is_defined(va):
    pass


def is_unknown(va):
    return ida_bytes.is_unknown(idc.get_full_flags(va))


def main():
    for segstart, segend, segname in enum_segments():
        if segname not in ('.text', '.data'):
            continue

        for src, dst in find_pointers(segstart, segend):
            if is_code(src):
                # ignore instructions like:
                #
                #     call    ds:__vbaGenerateBoundsError
                #print('code pointer: 0x%x -> 0x%x' % (src, dst))
                continue

            if is_in_string(src):
                # for example, the following contains 0x444974 (a common valid offset):
                #
                #     text:004245B0 aRequestid    db 'requestID',
                #
                # enable or disable this behavior as you wish
                print('string pointer: 0x%x -> 0x%x' % (src, dst))
                pass
                #continue

            print('pointer from 0x%x to 0x%x' % (src, dst))

            if is_unknown(dst):
                print('destination unknown, making byte: 0x%x' % (dst))
                ida_bytes.create_data(dst, FF_BYTE, 1, ida_idaapi.BADADDR)

            elif is_head(dst):
                # things are good
                pass

            else:
                # need to undefine head, and make byte
                head_va = get_head(dst)
                print('destination overlaps with head: 0x%x' % (head_va))
                ida_bytes.del_items(head_va, dst - head_va)
                ida_bytes.create_data(head_va, FF_BYTE, 1, ida_idaapi.BADADDR)
                ida_bytes.create_data(dst, FF_BYTE, 1, ida_idaapi.BADADDR)

            ida_bytes.del_items(src, 4)
            ida_bytes.create_data(src, FF_DWORD, 4, ida_idaapi.BADADDR)
            # this doesn't seem to always work :-(
            idc.op_plain_offset(src, -1, 0)


if __name__ == '__main__':
    main()