import ida_struct import ida_kernwin import ida_segment import ida_bytes import idautils import json from collections import OrderedDict """ Exports analysis data from IDA to a bnida json file """ __author__ = 'zznop' __copyright__ = 'Copyright 2018, zznop0x90@gmail.com' __license__ = 'MIT' def get_single_comment(regular, repeatable): """ IDA has repeatable and regular comments. BN only has regular comments. This function constructs a single comment from both repeatable and regular comments """ if repeatable is None and regular is None: return None elif repeatable is not None and regular is not None: return regular + '\n' + repeatable elif repeatable is not None and regular is None: return repeatable elif regular is not None and repeatable is None: return regular def get_single_function_comment(ea): """ Get function comment :param ea: Function offset :return: comment string or None """ func = ida_funcs.get_func(ea) regular = ida_funcs.get_func_cmt(func, False) repeatable = ida_funcs.get_func_cmt(func, True) return get_single_comment(regular, repeatable) def get_single_line_comment(ea): """ Get line comment :param ea: Function offset :return: Comment string or None """ regular = ida_bytes.get_cmt(ea, False) repeatable = ida_bytes.get_cmt(ea, True) cmt = get_single_comment(regular, repeatable) return cmt def get_function_comments(): """ Get function comments from IDA database :return: Dict containing function comments """ comments = {} for ea in idautils.Functions(): comment = get_single_function_comment(ea) if comment: comments[ea] = comment return comments def get_functions(): """ Get function start addresses :return: Array containing function addresses """ func_addrs = [] for ea in idautils.Functions(): func_addrs.append(ea) return func_addrs def get_line_comments(): """ Iterate through every address in a segment and check for comments :return: Dict containing line comments """ last_comment = '' comments = {} for ea in idautils.Segments(): segm = ida_segment.getseg(ea) name = ida_segment.get_segm_name(segm) if name == 'LOAD': continue for i in range(segm.start_ea, segm.end_ea): comment = get_single_line_comment(i) if comment and comment != last_comment: comments[i] = comment last_comment = comment return comments def get_names(): """ Get symbols from IDA database :return: Dict containing symbol information """ symbols = {} for addr, name in idautils.Names(): symbols[addr] = name return symbols def get_sections(): """ Get section names and start/end addrs from IDA database :return: Dict containing section info """ sections = {} for ea in idautils.Segments(): segm = ida_segment.getseg(ea) name = ida_segment.get_segm_name(segm) if name == 'LOAD': continue curr = {} curr['start'] = segm.start_ea curr['end'] = segm.end_ea sections[name] = curr return sections def get_member_type(struct, idx): """ Retrieve the type information for the struct member :return: Type string """ member = ida_struct.get_member(struct, idx) tif = idaapi.tinfo_t() ida_struct.get_member_tinfo(tif, member) elements = str(tif).split(' ') typ = None if len(elements) == 2 and elements[0] == 'unsigned': if elements[1] == '__int8': typ = 'uint8_t' elif elements[1] == '__int16': typ = 'uint16_t' elif elements[1] == '__int32': typ = 'uint32_t' elif elements[1] == '__int64': typ = 'uint64_t' elif elements[1] != '': typ = elements[1] elif len(elements) == 1: if elements[0] == '__int8': typ = 'int8_t' elif elements[0] == '__int16': typ = 'int16_t' elif elements[0] == '__int32': typ = 'int32_t' elif elements[0] == '__int64': typ = 'int64_t' elif elements[0] != '': typ = str(tif) return typ def get_struct_members(struct, sid): """ Get members belonging to a structure by structure ID :param struct: Structure object :param sid: Structure ID :return: Dict containing structure member information """ members = {} for offset, name, size in idautils.StructMembers(sid): members[name] = {} typ = get_member_type(struct, offset) if typ: members[name]['type'] = typ else: # Type isn't set so make it a byte array members[name]['type'] = 'uint8_t [{}]'.format(size) members[name]['offset'] = offset members[name]['size'] = size return members def get_structs(): """ Get structures from IDA database :return: Dict containing structure info """ structs = OrderedDict() for idx, sid, name in idautils.Structs(): struct = ida_struct.get_struc(sid) structs[name] = {} structs[name]['size'] = ida_struct.get_struc_size(struct) structs[name]['members'] = get_struct_members(struct, sid) return structs def main(json_file): """ Construct a json file containing analysis data from an IDA database :param json_file: Output JSON file name """ json_array = {} print('[*] Exporting analysis data to {}'.format(json_file)) json_array['sections'] = get_sections() json_array['functions'] = get_functions() json_array['func_comments'] = get_function_comments() json_array['line_comments'] = get_line_comments() json_array['names'] = get_names() json_array['structs'] = get_structs() print('[+] Done exporting analysis data') with open(json_file, 'w') as f: f.write(json.dumps(json_array, indent=4)) if __name__ == '__main__': main(ida_kernwin.ask_file(1, '*.json', 'Export file name'))