2026/3/29 23:18:36
网站建设
项目流程
网站title的写法,广西建设厅证书查询,瓯北网站制作报价,wordpress 侧栏 位置文章目录一、Ghidra Python脚本基础1. 运行环境与核心规则2. 核心模块与常用类/函数二、5个实战脚本案例案例1#xff1a;批量反编译所有函数到文件案例2#xff1a;提取所有字符串并导出到CSV案例3#xff1a;批量重命名相似函数#xff08;基于特征#xff09;案例4批量反编译所有函数到文件案例2提取所有字符串并导出到CSV案例3批量重命名相似函数基于特征案例4扫描程序中的硬编码IP地址案例5统计函数调用关系并生成DOT图三、Ghidra Python脚本开发技巧四、总结在汽车嵌入式开发中 常常使用Ghidra工具来对二进制目标代码进行分析。Ghidra是NSA开源的逆向工程利器其内置的Python脚本引擎基于Jython兼容Python 2.7极大扩展了逆向分析的自动化能力。无论是批量反编译、函数特征提取还是自定义漏洞检测Python脚本都能将重复的手工操作转化为高效的自动化流程。本文将从Ghidra Python脚本的基础语法、核心API入手结合5个实战脚本案例全面讲解Ghidra Python脚本的开发技巧。一、Ghidra Python脚本基础1. 运行环境与核心规则解释器Ghidra内置JythonPython 2.7 Java交互可直接调用Ghidra的Java API也兼容大部分Python 2.7语法脚本入口无显式main函数脚本从上至下执行可通过currentProgram、currentAddress等全局变量获取当前分析上下文核心依赖无需额外安装库直接导入Ghidra内置模块如ghidra.program.model.listing、ghidra.app.decompiler即可。2. 核心模块与常用类/函数Ghidra Python脚本的核心是调用其Java封装的API以下是逆向分析中最常用的模块和类模块/类功能描述ghidra.program.model.listing程序列表操作获取函数、指令、数据、符号等核心模块ghidra.app.decompiler反编译相关将二进制函数转为C代码ghidra.program.model.address地址操作构建、比较、偏移地址ghidra.program.model.mem内存操作读取/写入程序内存数据ghidra.util.task.ConsoleTaskMonitor任务监视器用于反编译、分析等耗时操作的进度跟踪currentProgram全局变量当前打开的程序对象listing currentProgram.getListing()获取程序列表对象用于遍历函数/指令func.getEntryPoint()获取函数入口地址decompiler.decompileFunction()反编译指定函数memory.getBytes(addr, length)从指定地址读取指定长度的字节数据二、5个实战脚本案例案例1批量反编译所有函数到文件功能遍历程序中所有函数将反编译后的C代码批量导出到指定文件方便离线分析。# -*- coding: utf-8 -*-# 批量反编译函数脚本importghidra.app.decompilerasdecompilerimportghidra.program.model.listingaslistingimportcodecs# Python 2处理UTF-8编码# 导出路径自定义EXPORT_PATHD:\\ghidra_decompile_all_functions.c# 获取当前程序和列表对象programcurrentProgram listingprogram.getListing()# 初始化反编译器decompiler_servicedecompiler.DecompInterface()decompiler_service.openProgram(program)# 任务监视器避免阻塞monitorghidra.util.task.ConsoleTaskMonitor()# 打开文件并写入withcodecs.open(EXPORT_PATH,w,utf-8)asf:f.write(// Auto decompiled by Ghidra Python Script\n)f.write(#include stdint.h\n\n)# 遍历所有函数递归获取forfuncinlisting.getFunctions(True):func_namefunc.getName()func_addrfunc.getEntryPoint()# 跳过系统自动生成的无效函数iffunc_name.startswith(_)andfunc_name.find(sub_)-1:continue# 写入函数头信息f.write(// %s 0x%s \n%(func_name,func_addr))# 反编译函数decompile_resultdecompiler_service.decompileFunction(func,0,monitor)ifdecompile_result.decompileCompleted():c_codedecompile_result.getDecompiledFunction().getC()f.write(c_code\n\n)else:f.write(// Decompile failed: %s\n\n%func_name)printDecompile failed: %s%func_name# 释放资源decompiler_service.dispose()printAll functions decompiled to: %s%EXPORT_PATH案例2提取所有字符串并导出到CSV功能扫描程序内存中的所有字符串ASCII/Unicode提取字符串内容、地址、长度导出为CSV文件辅助快速定位关键字符串如硬编码密码、URL。# -*- coding: utf-8 -*-# 提取字符串并导出CSV脚本importghidra.program.model.listingaslistingimportghidra.program.model.memasmemimportcodecs# 导出路径CSV_PATHD:\\ghidra_strings.csvprogramcurrentProgram listingprogram.getListing()memoryprogram.getMemory()# 字符串类型ASCII/UnicodeSTRING_TYPES[listing.DataUtilities.STRING_TYPE,listing.DataUtilities.UNICODE_STRING_TYPE]# 打开CSV文件并写入表头withcodecs.open(CSV_PATH,w,utf-8)asf:f.write(Address,Type,Length,Content\n)# 遍历程序中所有数据fordatainlisting.getDefinedData(True):# 判断是否为字符串类型ifdata.getDataType().isString()anddata.getValue()isnotNone:addrdata.getAddress()str_typeASCIIifdata.getDataType().getName()stringelseUnicodestr_lendata.getLength()str_contentdata.getValue().encode(utf-8,ignore)# 处理特殊字符# 写入CSV行f.write(0x%s,%s,%d,%s\n%(addr,str_type,str_len,str_content))printStrings exported to: %s%CSV_PATH案例3批量重命名相似函数基于特征功能识别以sub_开头的自动命名函数若函数包含指定指令特征如mov eax, 0x1则批量重命名为func_xxx提升逆向可读性。# -*- coding: utf-8 -*-# 批量重命名函数脚本importghidra.program.model.listingaslistingimportghidra.program.model.addressasaddress programcurrentProgram listingprogram.getListing()# 目标指令特征字节码mov eax, 0x1 → 0xB8 0x01 0x00 0x00 0x00TARGET_OPCODE[0xB8,0x01,0x00,0x00,0x00]RENAME_PREFIXfunc_syscall_counter0# 遍历所有函数forfuncinlisting.getFunctions(True):func_namefunc.getName()# 仅处理自动命名的sub_函数iffunc_name.startswith(sub_):func_addrfunc.getEntryPoint()func_instructionslisting.getInstructions(func.getBody(),True)# 检查函数指令是否包含目标特征has_target_opcodeFalseforinstinfunc_instructions:# 读取指令字节码opcode_bytes[]foriinrange(len(TARGET_OPCODE)):try:byteprogram.getMemory().getByte(inst.getAddress().add(i))opcode_bytes.append(byte0xFF)except:break# 匹配特征ifopcode_bytesTARGET_OPCODE:has_target_opcodeTruebreak# 重命名函数ifhas_target_opcode:new_name%s%d%(RENAME_PREFIX,counter)func.setName(new_name,ghidra.program.model.symbol.SourceType.USER_DEFINED)printRenamed: %s → %s (0x%s)%(func_name,new_name,func_addr)counter1printRenamed %d functions%counter案例4扫描程序中的硬编码IP地址功能扫描程序内存中的4字节数据解析为IPv4地址如0x0100007F→127.0.0.1提取所有硬编码IP并导出辅助漏洞分析。# -*- coding: utf-8 -*-# 扫描硬编码IP脚本importghidra.program.model.memasmemimportcodecs# 导出路径IP_PATHD:\\ghidra_hardcoded_ips.txtprogramcurrentProgram memoryprogram.getMemory()# 遍历程序所有内存块IP_LIST[]defbytes_to_ip(byte1,byte2,byte3,byte4):4字节转IPv4地址小端序return%d.%d.%d.%d%(byte40xFF,byte30xFF,byte20xFF,byte10xFF)# 遍历内存仅扫描可执行/数据段forblockinmemory.getBlocks():ifblock.isReadable()andnotblock.isExternal():start_addrblock.getStart()end_addrblock.getEnd()current_addrstart_addr# 逐4字节扫描whilecurrent_addr.compareTo(end_addr)0:try:# 读取4字节b1memory.getByte(current_addr)b2memory.getByte(current_addr.add(1))b3memory.getByte(current_addr.add(2))b4memory.getByte(current_addr.add(3))# 转换为IPipbytes_to_ip(b1,b2,b3,b4)# 过滤无效IP如0.0.0.0、255.255.255.255ifipnotin[0.0.0.0,255.255.255.255]:IP_LIST.append((current_addr,ip))# 偏移4字节current_addrcurrent_addr.add(4)except:current_addrcurrent_addr.add(1)# 去重并写入文件unique_ipslist(set(IP_LIST))withcodecs.open(IP_PATH,w,utf-8)asf:f.write(Address,IP Address\n)foraddr,ipinunique_ips:f.write(0x%s,%s\n%(addr,ip))printFound %d hardcoded IPs, exported to: %s%(len(unique_ips),IP_PATH)案例5统计函数调用关系并生成DOT图功能分析指定函数的调用关系被谁调用/调用了谁生成DOT格式文件可通过Graphviz转为可视化流程图。# -*- coding: utf-8 -*-# 生成函数调用关系DOT脚本importghidra.program.model.listingaslistingimportghidra.program.model.symbolassymbolimportcodecs# 目标函数名可修改TARGET_FUNC_NAMEmainDOT_PATHD:\\ghidra_callgraph.dotprogramcurrentProgram listingprogram.getListing()symbol_tableprogram.getSymbolTable()# 查找目标函数target_funcNoneforfuncinlisting.getFunctions(True):iffunc.getName()TARGET_FUNC_NAME:target_funcfuncbreakifnottarget_func:printFunction %s not found!%TARGET_FUNC_NAME exit(0)# 获取调用关系caller_list[]# 调用当前函数的函数callee_list[]# 当前函数调用的函数# 1. 获取被调用者calleeforrefinprogram.getReferenceManager().getReferencesFrom(target_func.getEntryPoint()):ifref.getReferenceType().isCall():callee_addrref.getToAddress()callee_funclisting.getFunctionContaining(callee_addr)ifcallee_func:callee_list.append(callee_func.getName())# 2. 获取调用者callerforrefinprogram.getReferenceManager().getReferencesTo(target_func.getEntryPoint()):ifref.getReferenceType().isCall():caller_addrref.getFromAddress()caller_funclisting.getFunctionContaining(caller_addr)ifcaller_func:caller_list.append(caller_func.getName())# 生成DOT文件withcodecs.open(DOT_PATH,w,utf-8)asf:f.write(digraph CallGraph {\n)f.write( node [shapebox];\n)# 绘制调用者→目标函数forcallerincaller_list:f.write( %s - %s;\n%(caller,TARGET_FUNC_NAME))# 绘制目标函数→被调用者forcalleeincallee_list:f.write( %s - %s;\n%(TARGET_FUNC_NAME,callee))f.write(}\n)printCall graph generated to: %s%DOT_PATHprintCallers of %s: %s%(TARGET_FUNC_NAME,caller_list)printCallees of %s: %s%(TARGET_FUNC_NAME,callee_list)三、Ghidra Python脚本开发技巧类型转换Jython中Java对象与Python类型可直接转换如java.lang.String→ Python字符串但需注意编码推荐utf-8异常处理内存读取、函数查找等操作需加try-except避免因无效地址导致脚本崩溃性能优化遍历大量数据时如全内存扫描优先使用getBlocks()按内存块遍历而非逐字节扫描调试技巧通过print输出中间结果或使用Ghidra的Script Console实时执行代码片段调试兼容性避免使用Python 3专属语法如f-string全部改用%格式化或str.format()。四、总结Ghidra Python脚本是逆向工程自动化的核心工具通过调用其丰富的Java API可实现从批量反编译、特征提取到自定义分析的全流程自动化。本文介绍的5个实战脚本覆盖了逆向分析中最常见的场景开发者可基于这些案例扩展功能如漏洞扫描、恶意代码特征匹配等。掌握Ghidra Python脚本开发能大幅提升逆向分析效率将重复的手工操作转化为可复用的自动化流程。