mirror of
https://github.com/awgil/ffxiv_reverse.git
synced 2025-04-22 14:57:45 +00:00
209 lines
7.9 KiB
Python
209 lines
7.9 KiB
Python
# This is a wrapper for IDA API, it is quite inconvenient to use...
|
|
import idaapi
|
|
import ida_bytes
|
|
import ida_enum
|
|
import ida_funcs
|
|
import ida_lines
|
|
import ida_nalt
|
|
import ida_name
|
|
import ida_struct
|
|
import ida_typeinf
|
|
import ida_xref
|
|
import ida_ua
|
|
import datetime
|
|
from contextlib import contextmanager
|
|
|
|
# enumerate (ea, name) tuples for all global names in database
|
|
def enumerate_names():
|
|
for i in range(ida_name.get_nlist_size()):
|
|
yield (ida_name.get_nlist_ea(i), ida_name.get_nlist_name(i))
|
|
|
|
# find EA of a global name; return idaapi.BADADDR on failure
|
|
def find_global_name_ea(name):
|
|
return ida_name.get_name_ea(idaapi.BADADDR, name)
|
|
|
|
# check whether we have a data offset at given EA
|
|
def ea_has_data_offset(ea):
|
|
flags = ida_bytes.get_full_flags(ea)
|
|
return (flags & ida_bytes.FF_DATA) != 0 and ida_bytes.is_off0(flags)
|
|
|
|
# get instruction at EA; return None or insn_t instance
|
|
def decode_instruction(ea):
|
|
insn = ida_ua.insn_t()
|
|
ilen = ida_ua.decode_insn(insn, ea)
|
|
return insn if ilen > 0 else None
|
|
|
|
# get instruction's operand as immediate
|
|
def get_instruction_operand_immediate(ea, opIndex):
|
|
insn = decode_instruction(ea)
|
|
return insn.ops[opIndex].addr if insn else None
|
|
|
|
# given EA of a call instruction, return EAs of instructions setting arguments in proper order
|
|
def get_call_argument_assignment_eas(call_ea):
|
|
return idaapi.get_arg_addrs(call_ea)
|
|
|
|
# enumerate xrefs to address
|
|
def enumerate_xrefs_to(ea):
|
|
addr = ida_xref.get_first_cref_to(ea)
|
|
while addr != idaapi.BADADDR:
|
|
yield addr
|
|
addr = ida_xref.get_next_cref_to(ea, addr)
|
|
|
|
# create typeinfo from cdecl; return None on failure
|
|
# cdecl should not have a trailing semicolon, it is added automatically
|
|
def parse_cdecl(cdecl):
|
|
tif = ida_typeinf.tinfo_t()
|
|
return tif if ida_typeinf.parse_decl(tif, None, cdecl + ';', 0) == '' else None
|
|
|
|
# speed up mass creation of enums or structs
|
|
@contextmanager
|
|
def mass_type_updater(utpFlag):
|
|
print(f'{datetime.datetime.now()} Starting mass type updates: {utpFlag}')
|
|
ida_typeinf.begin_type_updating(utpFlag)
|
|
try:
|
|
yield None
|
|
finally:
|
|
print(f'{datetime.datetime.now()} Comitting mass type updates: {utpFlag}')
|
|
ida_typeinf.end_type_updating(utpFlag)
|
|
print(f'{datetime.datetime.now()} Done with mass type updates: {utpFlag}')
|
|
|
|
# create enum type; returns idaapi.BADADDR on failure
|
|
def add_enum(name, bitfield, signed, width):
|
|
# flags seem to be undocumented, here's what I found:
|
|
# signed is 0x20000
|
|
# hexa is FF_0NUMH | FF_1NUMH (0x1100000), decimal is FF_0NUMD | FF_1NUMD (0x2200000), octal is FF_0NUMO | FF_1NUMO (0x7700000), binary is FF_0NUMB | FF_1NUMB (0x6600000), character is FF_0CHAR | FF_1CHAR (0x3300000)
|
|
flags = 0x1100000
|
|
if signed:
|
|
flags |= 0x20000
|
|
eid = ida_enum.add_enum(idaapi.BADADDR, name, flags)
|
|
if eid != idaapi.BADADDR:
|
|
ida_enum.set_enum_bf(eid, bitfield)
|
|
ida_enum.set_enum_width(eid, width)
|
|
return eid
|
|
|
|
# get structure by name
|
|
def get_struct_by_name(name):
|
|
return ida_struct.get_struc(ida_struct.get_struc_id(name))
|
|
|
|
# create struct type; returns struc_t or None on failure
|
|
def add_struct(name, isUnion = False):
|
|
sid = ida_struct.add_struc(idaapi.BADADDR, name, isUnion)
|
|
return ida_struct.get_struc(sid) if sid != idaapi.BADADDR else None
|
|
|
|
# create structure member of primitive types
|
|
def add_struct_member_primitive(s, offset, name, flag, size):
|
|
return ida_struct.add_struc_member(s, name, offset, ida_bytes.FF_DATA | flag, None, size) == 0
|
|
|
|
def add_struct_member_byte(s, offset, name, arraySize = 1):
|
|
return add_struct_member_primitive(s, offset, name, ida_bytes.FF_BYTE, arraySize)
|
|
|
|
def add_struct_member_word(s, offset, name, arraySize = 1):
|
|
return add_struct_member_primitive(s, offset, name, ida_bytes.FF_WORD, arraySize * 2)
|
|
|
|
def add_struct_member_dword(s, offset, name, arraySize = 1):
|
|
return add_struct_member_primitive(s, offset, name, ida_bytes.FF_DWORD, arraySize * 4)
|
|
|
|
def add_struct_member_qword(s, offset, name, arraySize = 1):
|
|
return add_struct_member_primitive(s, offset, name, ida_bytes.FF_QWORD, arraySize * 8)
|
|
|
|
# create structure member of pointer type
|
|
def add_struct_member_ptr(s, offset, name, arraySize = 1):
|
|
opinfo = ida_nalt.opinfo_t()
|
|
opinfo.ri = ida_nalt.refinfo_t()
|
|
opinfo.ri.base = opinfo.ri.target = idaapi.BADADDR
|
|
opinfo.ri.flags = ida_nalt.REF_OFF64
|
|
return ida_struct.add_struc_member(s, name, offset, ida_bytes.FF_DATA | ida_bytes.FF_QWORD | ida_bytes.FF_0OFF | ida_bytes.FF_1OFF, opinfo, arraySize * 8) == 0
|
|
|
|
# create structure member of enumeration type
|
|
def add_struct_member_enum(s, offset, name, type, arraySize = 1):
|
|
opinfo = ida_nalt.opinfo_t()
|
|
opinfo.tid = ida_enum.get_enum(type)
|
|
if opinfo.tid == idaapi.BADADDR:
|
|
return False
|
|
w = ida_enum.get_enum_width(opinfo.tid)
|
|
if w == 1:
|
|
flag = ida_bytes.FF_BYTE
|
|
elif w == 2:
|
|
flag = ida_bytes.FF_WORD
|
|
elif w == 8:
|
|
flag = ida_bytes.FF_QWORD
|
|
else:
|
|
flag = ida_bytes.FF_DWORD # default
|
|
return ida_struct.add_struc_member(s, name, offset, ida_bytes.FF_DATA | flag | ida_bytes.FF_0ENUM | ida_bytes.FF_1ENUM, opinfo, w * arraySize) == 0
|
|
|
|
# create structure member that is an instance of a different structure; returns success
|
|
def add_struct_member_substruct(s, offset, name, type, arraySize = 1):
|
|
opinfo = ida_nalt.opinfo_t()
|
|
opinfo.tid = ida_struct.get_struc_id(type)
|
|
size = ida_struct.get_struc_size(opinfo.tid) if opinfo.tid != idaapi.BADADDR else 0
|
|
if size == 0: # note: adding 0-sized struct field leads to problems
|
|
return False
|
|
return ida_struct.add_struc_member(s, name, offset, ida_bytes.FF_DATA | ida_bytes.FF_STRUCT, opinfo, size * arraySize) == 0
|
|
|
|
# create base class field for a structure
|
|
def add_struct_baseclass(s, type):
|
|
offset = ida_struct.get_struc_size(s)
|
|
success = add_struct_member_substruct(s, offset, f'baseclass_{hex(offset)[2:]}', type)
|
|
if success:
|
|
ida_struct.get_member(s, offset).props |= ida_struct.MF_BASECLASS
|
|
# TODO: we might need to call save_struc here...
|
|
return success
|
|
|
|
# create structure member of custom type
|
|
def add_struct_member_typed(s, offset, name, type):
|
|
return add_struct_member_byte(s, offset, name) and set_struct_member_by_offset_type(s, offset, type)
|
|
|
|
def get_struct_member_tinfo(m):
|
|
tif = ida_typeinf.tinfo_t()
|
|
return tif if ida_struct.get_member_tinfo(tif, m) else None
|
|
|
|
# set structure member type info
|
|
def set_struct_member_tinfo(s, m, tif):
|
|
# note: SET_MEMTI_* flags
|
|
return ida_struct.set_member_tinfo(s, m, 0, tif, 0) > 0
|
|
|
|
# set structure member type from string
|
|
def set_struct_member_type(s, m, type):
|
|
tif = parse_cdecl(type)
|
|
return set_struct_member_tinfo(s, m, tif) if tif else False
|
|
|
|
# set structure member type
|
|
def set_struct_member_by_offset_type(s, offset, type):
|
|
m = ida_struct.get_member(s, offset)
|
|
return set_struct_member_type(s, m, type) if m else False
|
|
|
|
# add comment; if one already exists, append as new line (unless existing comment already contains what we're trying to add)
|
|
def add_comment_enum(id, comment, repeatable = False):
|
|
existing = ida_enum.get_enum_cmt(id, repeatable)
|
|
if not existing or comment not in existing:
|
|
ida_enum.set_enum_cmt(id, f'{existing}\n{comment}' if existing else comment, repeatable)
|
|
|
|
def add_comment_func(func, comment, repeatable = False):
|
|
existing = ida_funcs.get_func_cmt(func, repeatable)
|
|
if not existing or comment not in existing:
|
|
ida_funcs.set_func_cmt(func, f'{existing}\n{comment}' if existing else comment, repeatable)
|
|
|
|
def add_comment_inline(ea, comment, repeatable = False):
|
|
existing = ida_bytes.get_cmt(ea, repeatable)
|
|
if not existing or comment not in existing:
|
|
ida_bytes.set_cmt(ea, f'{existing}\n{comment}' if existing else comment, repeatable)
|
|
|
|
def add_comment_outline(ea, comment, posterior = False):
|
|
line = ida_lines.E_NEXT if posterior else ida_lines.E_PREV
|
|
while True:
|
|
existing = ida_lines.get_extra_cmt(ea, line)
|
|
if not existing:
|
|
ida_lines.add_extra_cmt(ea, not posterior, comment)
|
|
return
|
|
elif comment in existing:
|
|
return
|
|
else:
|
|
line += 1
|
|
|
|
# func or outline
|
|
def add_comment_ea_auto(ea, comment):
|
|
func = ida_funcs.get_func(ea)
|
|
if func:
|
|
add_comment_func(func, comment)
|
|
else:
|
|
add_comment_outline(ea, comment)
|