应用安全 --- IDA Pro 函数头批量导出
使用ida内置的识别方法导出
""" IDA Pro Hex-Rays Function Header Exporter 功能:批量导出所有函数的Hex-Rays反编译函数头 """ import idaapi import idc import idautils import ida_funcs import ida_hexrays import ida_name import ida_typeinf from datetime import datetimeclass HexRaysHeaderExporter(idaapi.plugin_t):flags = idaapi.PLUGIN_UNLcomment = "导出Hex-Rays函数头"help = "批量导出所有函数的反编译函数头"wanted_name = "Hex-Rays Header Exporter"wanted_hotkey = "Ctrl-Shift-H"def init(self):# 检查Hex-Rays是否可用if not ida_hexrays.init_hexrays_plugin():idaapi.msg("[!] Hex-Rays decompiler 未安装或不可用\n")return idaapi.PLUGIN_SKIPidaapi.msg("Hex-Rays Header Exporter 已加载\n")idaapi.msg("按 Ctrl+Shift+H 导出函数头\n")return idaapi.PLUGIN_OKdef run(self, arg):exporter = HeaderExporterCore()exporter.export_all_headers()def term(self):passclass HeaderExporterCore:def __init__(self):self.headers = []self.failed = []def check_hexrays(self):"""检查Hex-Rays是否可用"""if not ida_hexrays.init_hexrays_plugin():idaapi.warning("Hex-Rays decompiler 不可用!\n请确保已安装对应架构的反编译器。")return Falsereturn Truedef get_function_header(self, func_ea):"""获取函数的反编译头"""try:# 尝试反编译cfunc = ida_hexrays.decompile(func_ea)if not cfunc:return None# 方法1: 从伪代码中提取第一行(最准确)sv = cfunc.get_pseudocode()if sv and len(sv) > 0:# 第一行通常是函数声明first_line = ida_lines.tag_remove(sv[0].line)# 清理多余空格和换行first_line = ' '.join(first_line.split())return first_line# 方法2: 从类型信息构造func_type = cfunc.typeif func_type:# 获取函数名func_name = ida_name.get_name(func_ea)# 生成函数声明declaration = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, func_type, func_name, '')return declarationreturn Noneexcept Exception as e:return Nonedef get_function_header_simple(self, func_ea):"""备用方法:使用类型信息生成函数头"""try:func_name = ida_name.get_name(func_ea)# 获取函数类型tif = ida_typeinf.tinfo_t()if not ida_typeinf.guess_tinfo(tif, func_ea):return None# 打印类型result = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, tif, func_name, '')if result:return resultreturn Noneexcept:return Nonedef export_all_headers(self):"""导出所有函数头"""if not self.check_hexrays():return# 询问导出选项choice = idaapi.ask_yn(1, "选择导出模式:\n\n""YES - 仅导出成功反编译的函数\n""NO - 同时导出失败信息\n""CANCEL - 取消")if choice == -1:returninclude_failed = (choice == 0)# 选择保存路径filepath = idaapi.ask_file(1, "*.txt", "保存函数头")if not filepath:returnidaapi.msg("\n[*] 开始导出函数头...\n")idaapi.show_wait_box("正在导出函数头...")self.headers = []self.failed = []# 获取所有函数funcs = list(idautils.Functions())total = len(funcs)for idx, func_ea in enumerate(funcs):if idx % 50 == 0:idaapi.replace_wait_box(f"进度: {idx}/{total}")idaapi.msg(f"[*] 进度: {idx}/{total}\n")func_name = ida_name.get_name(func_ea)# 尝试获取函数头header = self.get_function_header(func_ea)if header:self.headers.append({'address': func_ea,'name': func_name,'header': header})else:# 尝试备用方法header = self.get_function_header_simple(func_ea)if header:self.headers.append({'address': func_ea,'name': func_name,'header': header})else:self.failed.append({'address': func_ea,'name': func_name})idaapi.hide_wait_box()# 写入文件self.write_to_file(filepath, include_failed)# 显示结果msg = f"导出完成!\n\n"msg += f"成功: {len(self.headers)} 个函数\n"msg += f"失败: {len(self.failed)} 个函数\n"msg += f"文件: {filepath}"idaapi.info(msg)idaapi.msg(f"\n[+] {msg}\n")def write_to_file(self, filepath, include_failed):"""写入文件"""with open(filepath, 'w', encoding='utf-8') as f:# 写入文件头f.write("=" * 80 + "\n")f.write("IDA Pro Hex-Rays 函数头导出\n")f.write(f"导出时间: {datetime.now()}\n")f.write(f"二进制文件: {idaapi.get_input_file_path()}\n")f.write(f"成功导出: {len(self.headers)} 个函数\n")f.write(f"失败: {len(self.failed)} 个函数\n")f.write("=" * 80 + "\n\n")# 写入成功的函数头f.write("=" * 80 + "\n")f.write("函数声明列表\n")f.write("=" * 80 + "\n\n")for item in self.headers:# 格式: 地址 | 函数头f.write(f"// Address: 0x{item['address']:X}\n")f.write(f"{item['header']}\n\n")# 如果需要,写入失败的函数if include_failed and self.failed:f.write("\n" + "=" * 80 + "\n")f.write("反编译失败的函数\n")f.write("=" * 80 + "\n\n")for item in self.failed:f.write(f"0x{item['address']:X} - {item['name']}\n")def PLUGIN_ENTRY():return HexRaysHeaderExporter()# 独立运行 if __name__ == "__main__":exporter = HeaderExporterCore()exporter.export_all_headers()
自己实现的函数参数头导出
""" IDA Pro Function Parameter Analyzer (兼容版本) 修复了 get_func_flags 的兼容性问题 """ import idaapi import idc import idautils import ida_funcs import ida_typeinf import ida_name import json import csv from datetime import datetimeclass FunctionParamAnalyzer(idaapi.plugin_t):flags = idaapi.PLUGIN_UNLcomment = "批量分析函数参数并导出"help = "分析所有函数的参数个数和类型"wanted_name = "Function Parameter Analyzer"wanted_hotkey = "Ctrl-Shift-P"def init(self):idaapi.msg("Function Parameter Analyzer 已加载\n")idaapi.msg("按 Ctrl+Shift+P 启动分析\n")return idaapi.PLUGIN_OKdef run(self, arg):analyzer = ParamAnalyzerCore()analyzer.show_menu()def term(self):passclass ParamAnalyzerCore:def __init__(self):self.functions_data = []def show_menu(self):"""显示操作菜单"""choice = idaapi.ask_yn(1, "函数参数分析器\n\n""YES - 导出为JSON\n""NO - 导出为CSV\n""CANCEL - 导出为TXT")if choice == -1:export_type = "txt"elif choice == 0:export_type = "csv"else:export_type = "json"self.analyze_all_functions()self.export_results(export_type)def analyze_all_functions(self):"""分析所有函数"""idaapi.msg("\n[*] 开始分析函数参数...\n")self.functions_data = []func_count = 0for func_ea in idautils.Functions():func_count += 1current = 0for func_ea in idautils.Functions():current += 1if current % 100 == 0:idaapi.msg(f"[*] 进度: {current}/{func_count}\n")func_info = self.analyze_function(func_ea)if func_info:self.functions_data.append(func_info)idaapi.msg(f"[+] 分析完成!共分析 {len(self.functions_data)} 个函数\n")def get_function_flags(self, func_ea, func_obj):"""获取函数标志(兼容多版本)"""try:# 方法1: 通过func对象if func_obj and hasattr(func_obj, 'flags'):return func_obj.flagsexcept:passtry:# 方法2: 使用idcflags = idc.get_func_attr(func_ea, idc.FUNCATTR_FLAGS)if flags is not None and flags != idc.BADADDR:return flagsexcept:passtry:# 方法3: 使用ida_funcs (老版本)if hasattr(ida_funcs, 'get_func_flags'):return ida_funcs.get_func_flags(func_ea)except:passreturn 0def is_library_function(self, func_ea, func_obj):"""判断是否为库函数(兼容多版本)"""try:flags = self.get_function_flags(func_ea, func_obj)if flags:return (flags & ida_funcs.FUNC_LIB) != 0except:pass# 备用方法:通过函数名判断try:func_name = ida_name.get_name(func_ea)# 常见库函数前缀lib_prefixes = ['_', 'j_', '__', 'std::', 'strcpy', 'strcmp', 'malloc', 'free', 'printf', 'scanf']for prefix in lib_prefixes:if func_name.startswith(prefix):return Trueexcept:passreturn Falsedef analyze_function(self, func_ea):"""分析单个函数"""try:func_name = ida_name.get_name(func_ea)if not func_name:func_name = f"sub_{func_ea:X}"# 获取函数对象func = ida_funcs.get_func(func_ea)if not func:return None# 获取函数类型信息tif = ida_typeinf.tinfo_t()has_tif = ida_typeinf.guess_tinfo(tif, func_ea)func_data = {'address': f"0x{func_ea:X}",'name': func_name,'param_count': 0,'params': [],'return_type': 'unknown','calling_convention': 'unknown','is_library': self.is_library_function(func_ea, func)}# 如果有类型信息if has_tif and tif.is_func():func_details = ida_typeinf.func_type_data_t()tif.get_func_details(func_details)# 参数数量func_data['param_count'] = func_details.size()# 调用约定cc = func_details.cccc_map = {ida_typeinf.CM_CC_CDECL: '__cdecl',ida_typeinf.CM_CC_STDCALL: '__stdcall',ida_typeinf.CM_CC_FASTCALL: '__fastcall',ida_typeinf.CM_CC_THISCALL: '__thiscall',}func_data['calling_convention'] = cc_map.get(cc, 'unknown')# 返回类型ret_type = func_details.rettypefunc_data['return_type'] = str(ret_type)# 参数详情for i in range(func_details.size()):param = func_details[i]param_info = {'index': i,'name': param.name if param.name else f"arg_{i}",'type': str(param.type),'size': param.type.get_size()}func_data['params'].append(param_info)else:# 尝试从栈帧分析参数func_data['param_count'] = self.estimate_param_count(func_ea)return func_dataexcept Exception as e:# 静默处理错误,避免大量输出return Nonedef estimate_param_count(self, func_ea):"""估算参数数量(当没有类型信息时)"""try:frame = ida_funcs.get_frame(func_ea)if not frame:return 0param_count = 0for member in idautils.StructMembers(frame.id):offset, name, size = memberif offset > 0 and offset < 0x100:param_count += 1return param_countexcept:return 0def export_results(self, export_type):"""导出结果"""if not self.functions_data:idaapi.warning("没有可导出的数据!")return# 选择保存路径filepath = idaapi.ask_file(1, f"*.{export_type}", f"保存为 {export_type.upper()}")if not filepath:idaapi.msg("[!] 取消导出\n")returntry:if export_type == "json":self.export_json(filepath)elif export_type == "csv":self.export_csv(filepath)elif export_type == "txt":self.export_txt(filepath)idaapi.msg(f"[+] 成功导出到: {filepath}\n")idaapi.info(f"分析结果已导出到:\n{filepath}\n\n共 {len(self.functions_data)} 个函数")except Exception as e:idaapi.warning(f"导出失败: {str(e)}")def export_json(self, filepath):"""导出为JSON格式"""output = {'analysis_time': datetime.now().isoformat(),'binary': idaapi.get_input_file_path(),'total_functions': len(self.functions_data),'functions': self.functions_data}with open(filepath, 'w', encoding='utf-8') as f:json.dump(output, f, indent=2, ensure_ascii=False)def export_csv(self, filepath):"""导出为CSV格式"""with open(filepath, 'w', newline='', encoding='utf-8') as f:writer = csv.writer(f)writer.writerow(['Address', 'Function Name', 'Param Count', 'Return Type', 'Calling Convention', 'Parameters', 'Is Library'])for func in self.functions_data:params_str = '; '.join([f"{p['name']}:{p['type']}" for p in func['params']]) if func['params'] else 'N/A'writer.writerow([func['address'],func['name'],func['param_count'],func['return_type'],func['calling_convention'],params_str,func['is_library']])def export_txt(self, filepath):"""导出为TXT格式"""with open(filepath, 'w', encoding='utf-8') as f:f.write("=" * 80 + "\n")f.write("函数参数分析报告\n")f.write(f"分析时间: {datetime.now()}\n")f.write(f"二进制文件: {idaapi.get_input_file_path()}\n")f.write(f"函数总数: {len(self.functions_data)}\n")f.write("=" * 80 + "\n\n")for func in self.functions_data:f.write(f"函数: {func['name']}\n")f.write(f" 地址: {func['address']}\n")f.write(f" 参数数量: {func['param_count']}\n")f.write(f" 返回类型: {func['return_type']}\n")f.write(f" 调用约定: {func['calling_convention']}\n")f.write(f" 库函数: {'是' if func['is_library'] else '否'}\n")if func['params']:f.write(" 参数列表:\n")for p in func['params']:f.write(f" [{p['index']}] {p['name']}: {p['type']} (size: {p['size']})\n")else:f.write(" 参数列表: 无类型信息\n")f.write("\n" + "-" * 80 + "\n\n")def PLUGIN_ENTRY():return FunctionParamAnalyzer()# 独立运行 if __name__ == "__main__":analyzer = ParamAnalyzerCore()analyzer.show_menu()
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/937492.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!