GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

应用安全 --- IDA Pro脚本 之 批量导出反编译和反汇编

import idc
import idaapi
import idautils
import os
import re

def sanitize_filename(name, max_length=100):
    """清理文件名,移除非法字符并限制长度"""
    # 移除或替换非法字符
    name = re.sub(r'[<>:"/\\|?*]', '_', name)
    # 移除多余的下划线
    name = re.sub(r'_+', '_', name)
    # 限制长度
    if len(name) > max_length:
        name = name[:max_length]
    # 确保文件名不为空
    if not name or name.isspace():
        name = "unknown_function"
    return name.strip()

def export_complete_function_info(output_dir="exported_functions"):
    """
    导出所有函数的反汇编和反编译代码(修复文件名问题)
    """
    print(f"开始导出函数信息到目录: {output_dir}")
    
    # 创建输出目录(确保路径存在)
    try:
        if not os.path.exists(output_dir):
            os.makedirs(output_dir, exist_ok=True)
        print(f"目录已创建: {os.path.abspath(output_dir)}")
    except Exception as e:
        print(f"创建目录失败: {e}")
        # 使用当前目录作为备选
        output_dir = "."
        print(f"使用当前目录: {os.path.abspath(output_dir)}")
    
    # 获取函数列表
    functions = list(idautils.Functions())
    total_functions = len(functions)
    print(f"找到 {total_functions} 个函数")
    
    exported_count = 0
    hexrays_available = False
    
    # 检查Hex-Rays反编译器是否可用
    try:
        import ida_hexrays
        hexrays_available = True
        print("Hex-Rays反编译器可用")
    except:
        print("Hex-Rays反编译器不可用,仅导出反汇编代码")
    
    for i, func_ea in enumerate(functions):
        func_name = idc.get_func_name(func_ea)
        
        # 创建安全的文件名
        safe_name = sanitize_filename(func_name)
        filename = f"{safe_name}_{func_ea:08X}.txt"
        filepath = os.path.join(output_dir, filename)
        
        # 进一步确保文件路径不会太长
        if len(filepath) > 200:
            # 如果路径太长,使用更短的文件名
            filename = f"func_{func_ea:08X}.txt"
            filepath = os.path.join(output_dir, filename)
        
        try:
            # 确保目录存在(再次检查)
            os.makedirs(os.path.dirname(filepath), exist_ok=True)
            
            with open(filepath, 'w', encoding='utf-8', errors='ignore') as f:
                # 写入函数基本信息
                f.write(f"函数名称: {func_name}\n")
                f.write(f"起始地址: 0x{func_ea:08X}\n")
                
                # 获取函数对象
                func = idaapi.get_func(func_ea)
                if func:
                    f.write(f"结束地址: 0x{func.end_ea:08X}\n")
                    f.write(f"函数大小: {func.end_ea - func.start_ea} 字节\n")
                
                f.write("\n" + "="*70 + "\n")
                f.write("反汇编代码 (包含地址信息):\n")
                f.write("="*70 + "\n\n")
                
                # 导出反汇编代码
                if func:
                    ea = func.start_ea
                    while ea < func.end_ea and ea != idc.BADADDR:
                        if idc.is_code(idc.get_full_flags(ea)):
                            # 获取地址和反汇编指令
                            address = f"0x{ea:08X}"
                            disasm = idc.GetDisasm(ea)
                            
                            # 获取注释
                            comment = idc.get_cmt(ea, 0)
                            if comment:
                                f.write(f"{address}: {disasm:<50} // {comment}\n")
                            else:
                                f.write(f"{address}: {disasm}\n")
                        
                        # 移动到下一条指令
                        next_ea = idc.next_head(ea, func.end_ea)
                        if next_ea == idc.BADADDR or next_ea <= ea:
                            break
                        ea = next_ea
                
                # 导出反编译代码(如果可用)
                if hexrays_available:
                    f.write("\n" + "="*70 + "\n")
                    f.write("反编译代码 (Hex-Rays):\n")
                    f.write("="*70 + "\n\n")
                    
                    try:
                        cfunc = ida_hexrays.decompile(func_ea)
                        if cfunc:
                            f.write(str(cfunc))
                        else:
                            f.write("// 反编译失败\n")
                    except Exception as e:
                        f.write(f"// 反编译错误: {str(e)}\n")
                else:
                    f.write("\n// Hex-Rays反编译器不可用\n")
            
            exported_count += 1
            if i % 100 == 0:  # 每100个函数打印一次进度
                print(f"进度: {i+1}/{total_functions}")
                
        except Exception as e:
            print(f"导出函数 {func_name} 时出错: {str(e)}")
            # 尝试使用更简单的文件名
            try:
                simple_filename = f"func_{func_ea:08X}.txt"
                simple_filepath = os.path.join(output_dir, simple_filename)
                with open(simple_filepath, 'w', encoding='utf-8', errors='ignore') as f:
                    f.write(f"函数名称: {func_name}\n地址: 0x{func_ea:08X}\n")
                    f.write(f"原始文件名过长,使用简化文件名保存\n")
                print(f"使用简化文件名成功保存: {simple_filename}")
                exported_count += 1
            except Exception as e2:
                print(f"简化文件名也失败: {e2}")
    
    print(f"导出完成! 成功导出 {exported_count}/{total_functions} 个函数")
    print(f"文件保存在: {os.path.abspath(output_dir)}")
    
    # 在IDA输出窗口显示完成信息
    idc.msg(f"\n函数导出完成: {exported_count}/{total_functions} 个函数已导出到 {output_dir}\n")

# 简化版本:只使用地址作为文件名
def export_simple_version(output_dir="simple_export"):
    """使用简化文件名导出,避免路径过长问题"""
    print("使用简化版本导出...")
    
    if not os.path.exists(output_dir):
        os.makedirs(output_dir, exist_ok=True)
    
    functions = list(idautils.Functions())
    exported_count = 0
    
    for func_ea in functions:
        func_name = idc.get_func_name(func_ea)
        filename = f"func_{func_ea:08X}.txt"
        filepath = os.path.join(output_dir, filename)
        
        try:
            with open(filepath, 'w', encoding='utf-8', errors='ignore') as f:
                f.write(f"Function: {func_name}\n")
                f.write(f"Address: 0x{func_ea:08X}\n\n")
                
                func = idaapi.get_func(func_ea)
                if func:
                    f.write("Disassembly:\n")
                    f.write("-" * 50 + "\n")
                    
                    ea = func.start_ea
                    while ea < func.end_ea:
                        if idc.is_code(idc.get_full_flags(ea)):
                            disasm = idc.GetDisasm(ea)
                            f.write(f"0x{ea:08X}: {disasm}\n")
                        
                        next_ea = idc.next_head(ea, func.end_ea)
                        if next_ea == idc.BADADDR:
                            break
                        ea = next_ea
            
            exported_count += 1
        except Exception as e:
            print(f"导出失败 {func_name}: {e}")
    
    print(f"简化版本导出完成: {exported_count} 个函数")

# 立即执行导出(使用修复版本)
if __name__ == "__main__":
    # 先尝试完整版本
    try:
        export_complete_function_info("exported_functions")
    except Exception as e:
        print(f"完整版本失败: {e}")
        print("尝试简化版本...")
        export_simple_version("simple_export")

 

posted on 2025-09-23 06:49  GKLBB  阅读(139)  评论(0)    收藏  举报