自动化逆向(一):IDAPython脚本编写、批量分析
说实话,干了这么多年逆向,我最怕的就是重复劳动。一个样本分析完了,又来一批类似的——手动点来点去,眼睛都快瞎了。后来我学乖了,开始用 IDAPython 写脚本。嗯,这玩意儿是真的香。
今天我们就聊聊怎么用 IDAPython 把那些机械化的操作自动化掉。说白了,就是让机器替我们干活,我们只负责动脑子。
为什么需要自动化逆向?
你想想看,一个恶意软件家族可能有几十个变种。每个变种都要手动分析一遍?那不得累死。我在项目中遇到过这样的情况:一个勒索软件家族,每天冒出十几个新样本。手动分析?不存在的。
自动化的核心价值就三点:
- 效率提升——脚本跑一遍,顶你手动点半小时
- 一致性——机器不会累,不会漏掉关键点
- 可复现——换个样本,同样的脚本再来一遍
核心思路:把分析经验固化成代码,让脚本替你执行那些「一看就知道该怎么做」的操作。
IDAPython 基础:你得先会这些
IDAPython 其实就是 IDA Pro 的 Python 接口。它把 IDA 内部的各种功能都暴露出来了。我个人习惯把常用操作记在脑子里,写脚本时信手拈来。
获取当前地址和选中内容
import idc
import idautils
import idaapi
# 获取当前光标所在地址
ea = idc.get_screen_ea()
print(f"当前地址: {hex(ea)}")
# 获取选中的地址范围
sel_start, sel_end = idc.read_selection_start(), idc.read_selection_end()
if sel_start != idc.BADADDR and sel_end != idc.BADADDR:
print(f"选中范围: {hex(sel_start)} - {hex(sel_end)}")
遍历函数和指令
# 遍历所有函数
for func_ea in idautils.Functions():
func_name = idc.get_func_name(func_ea)
print(f"函数: {func_name} @ {hex(func_ea)}")
# 遍历函数内的每条指令
for head in idautils.Heads(func_ea, idc.get_func_attr(func_ea, idc.FUNCATTR_END)):
mnem = idc.print_insn_mnem(head)
if mnem == "call":
# 这里可以记录所有 call 指令
pass
小技巧:用 idautils.Heads() 比 idautils.FuncItems() 更灵活,因为它可以处理非连续地址范围。
实战:批量提取字符串引用
我记得有一次分析一个木马样本,它用了大量加密字符串。手动提取?那得搞到猴年马月。于是我写了个脚本,一键提取所有字符串引用。
def extract_string_refs():
"""提取所有被引用的字符串及其调用位置"""
results = []
for seg_ea in idautils.Segments():
seg_name = idc.get_segm_name(seg_ea)
if seg_name in [".rodata", ".rdata", ".data"]:
for head in idautils.Heads(seg_ea, idc.get_segm_end(seg_ea)):
if idc.get_str_type(head):
# 找到字符串,查找交叉引用
for ref in idautils.XrefsTo(head):
caller_func = idc.get_func_name(ref.frm)
results.append({
"string": idc.get_strlit_contents(head),
"address": head,
"caller": caller_func,
"caller_addr": ref.frm
})
return results
# 执行并输出
strings = extract_string_refs()
for s in strings[:10]: # 只显示前10个
print(f"[{hex(s['address'])}] {s['string']} -> 被 {s['caller']} 调用")
注意:有些字符串是动态解密的,静态提取不到。这时候需要结合动态分析或者模拟执行。我曾经踩过这个坑,以为提取完了,结果漏掉了一半。
批量分析:一次跑完所有样本
自动化逆向的终极形态,就是批量分析。你扔进去一堆样本,脚本一个个打开、分析、生成报告。我建议用以下流程:
- 准备样本列表——把要分析的样本路径写到一个文本文件里
- 编写主控脚本——循环打开每个样本,执行分析函数
- 生成报告——把结果保存成 JSON 或 CSV,方便后续处理
import subprocess
import json
import os
def batch_analyze(sample_list_path, output_dir):
"""批量分析样本"""
with open(sample_list_path, "r") as f:
samples = [line.strip() for line in f if line.strip()]
for sample in samples:
print(f"正在分析: {sample}")
# 调用 IDA 命令行模式执行脚本
cmd = [
"idat64", # 或者 idat32
"-A", # 自动模式,不弹对话框
f"-Sanalyze_single.py", # 分析单个样本的脚本
sample
]
subprocess.run(cmd, capture_output=True)
# 读取分析结果
result_file = os.path.join(output_dir,
os.path.basename(sample) + ".json")
if os.path.exists(result_file):
with open(result_file, "r") as rf:
data = json.load(rf)
print(f" 发现 {len(data.get('functions', []))} 个函数")
print("批量分析完成!")
关键点:IDA 命令行模式用 -A 参数可以跳过所有交互对话框。配合 -S 参数指定脚本,就能实现全自动分析。
知识体系:自动化逆向的核心逻辑
下面这张图是我自己总结的自动化逆向流程。说白了,就是三步走:获取信息、分析处理、输出结果。
脚本模板:一个完整的分析脚本
下面是我常用的脚本模板。你直接拿去改改就能用。
import idc
import idautils
import idaapi
import json
import sys
def analyze_single():
"""分析单个样本的主函数"""
result = {
"file": idc.get_input_file_path(),
"functions": [],
"strings": [],
"imports": []
}
# 1. 收集函数信息
for func_ea in idautils.Functions():
func_name = idc.get_func_name(func_ea)
func_size = idc.get_func_attr(func_ea, idc.FUNCATTR_END) - func_ea
result["functions"].append({
"name": func_name,
"address": func_ea,
"size": func_size
})
# 2. 收集字符串
for seg_ea in idautils.Segments():
seg_name = idc.get_segm_name(seg_ea)
if seg_name in [".rodata", ".rdata", ".data"]:
for head in idautils.Heads(seg_ea, idc.get_segm_end(seg_ea)):
str_type = idc.get_str_type(head)
if str_type:
result["strings"].append({
"address": head,
"value": idc.get_strlit_contents(head)
})
# 3. 收集导入表
import_idx = idaapi.get_import_module_qty()
for i in range(import_idx):
name = idaapi.get_import_module_name(i)
result["imports"].append(name)
# 输出结果
output_path = idc.get_input_file_path() + ".json"
with open(output_path, "w") as f:
json.dump(result, f, indent=2)
print(f"分析完成,结果保存到: {output_path}")
if __name__ == "__main__":
analyze_single()
idc.qexit(0) # 退出 IDA
使用方式:把这个脚本保存为 analyze_single.py,然后执行:idat64 -A -Sanalyze_single.py sample.exe
常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 脚本运行到一半卡住 | IDA 弹出了交互对话框 | 确保使用了 -A 参数,并在脚本开头设置 idaapi.set_script_timeout(30) |
| 获取字符串为空 | 字符串被加密或混淆 | 先运行解密脚本,或者使用动态分析辅助 |
| 批量分析时内存溢出 | 样本太大或脚本有内存泄漏 | 每个样本分析完后调用 idc.qexit(0) 彻底退出 IDA 进程 |
| 结果文件被覆盖 | 多个样本输出到同一路径 | 用样本的 MD5 或文件名作为输出文件名的一部分 |
嗯,自动化逆向这东西,说白了就是「把经验写成代码」。刚开始可能觉得写脚本比手动分析还慢,但一旦积累起来,后面就爽了。我现在的习惯是:任何重复超过三次的操作,一定写脚本。
记住,自动化不是目的,解放你的时间去做更有价值的事情才是。那些需要动脑子的事情——比如理解攻击者的意图、设计对抗方案——才是我们真正该花时间的地方。
一句话总结:IDAPython 脚本 = 你的逆向经验 × 代码执行力。写一次,用一辈子。
公众号:蓝海资料掘金营,微信deep3321