Tianwen-1-parse-netzob.py 教学说明
这个脚本是一个 Netzob 入门教学脚本。它使用天问一号原始二进制帧数据做例子,但故意不使用 Tianwen.ccsds 里已经写好的协议解析器。
它的核心假设是:
我们只拿到一个
.u8原始二进制文件,不知道 Tianwen-1 的空间帧具体协议,想先用 Netzob 和基础统计方法观察数据结构。
脚本位置:
/home/zjz/CCSDS_study/test/Tianwen-1-parse-netzob.py
默认输入数据:
/home/zjz/CCSDS_study/Tianwen/tianwen1_frames_20200730.u8
这个脚本不做什么
它不做已知协议解析。
具体来说,它不会:
import Tianwen.ccsds- 调用
ccsds.AOSFrame.parse(...) - 直接使用已知的 AOS 帧字段定义
- 直接解释 spacecraft ID、virtual channel、APID 的真实协议含义
这样做是为了模拟未知协议逆向分析的第一步。
这个脚本主要做什么
它主要演示:
- 如何把原始 bytes 包装成 Netzob
RawMessage。 - 如何把一组消息放入 Netzob
Symbol。 - 如何用
Format.splitStatic自动观察固定字段和动态字段。 - 如何用
Format.clusterByKeyField按候选字段聚类。 - 如何用
Field和Raw手工写一个候选帧结构。 - 如何用简单统计辅助 Netzob 分析,比如熵、唯一值数量、最常见字节值。
运行方式
推荐在项目根目录运行:
/home/zjz/CCSDS_study/ccsds/bin/python /home/zjz/CCSDS_study/test/Tianwen-1-parse-netzob.py
查看帮助:
/home/zjz/CCSDS_study/ccsds/bin/python /home/zjz/CCSDS_study/test/Tianwen-1-parse-netzob.py --help
常用参数:
--frame-size 220
如果你已经知道候选帧长,可以直接指定,跳过帧长估计。
--sample-size 32
控制参与统计和 Netzob 分析的帧数。数值越大,结果越稳定,但运行更慢。
--cluster-sample-size 8
只控制 clusterByKeyField 演示使用多少帧。Netzob 聚类比较耗时,所以默认较小。
Step 1:读取未知二进制数据
脚本首先读取整个 .u8 文件:
raw = load_raw_bytes(args.input)
此时 raw 只是一个很长的 bytes,没有帧边界,也没有字段含义。
新手可以理解为:
raw = 一大串连续字节
Step 2:估计固定帧长
如果不知道每帧多长,脚本会尝试多个候选帧长,例如默认的 180 到 260。
核心思路:
- 用候选帧长把字节流切成多行。
- 看每一行开头若干字节的统计特征。
- 如果帧长猜对了,帧头字段会对齐。
- 对齐后的帧头通常比随机载荷更稳定。
- 稳定字段的平均熵更低。
因此脚本会打印类似:
Top frame-size candidates by low average header entropy:
size=220 avg_entropy=...
这里 220 通常会排在最前面。
注意:这不是数学证明,只是未知协议分析中的启发式方法。
Step 3:创建 Netzob RawMessage 和 Symbol
脚本把切好的每一帧包装成 Netzob RawMessage:
messages = [RawMessage(data=frame) for frame in frames]
然后把这些消息放进一个 Symbol:
symbol = Symbol(messages=messages, name=name)
新手可以这样理解:
RawMessage:一条原始消息。Symbol:一组格式相似的消息。
刚开始 Netzob 不知道字段边界,所以一个 Symbol 通常只有一个大字段。
Step 4:字节位置统计
在使用 Netzob 自动推断前,脚本先做基础统计。
每个字节位置会统计:
offset:字节偏移。unique:这个位置出现过多少种不同字节值。entropy:这个位置的熵。most common byte values:最常见的几个字节值。
例如:
offset unique entropy most common byte values
0 2 0.811 0x7d:72, 0x54:24
可以粗略理解为:
unique很小:可能是版本、类型、ID、固定标记。entropy很小:字段比较稳定。unique很大:可能是计数器、时间戳、载荷或校验。
脚本还会把连续位置合并成候选区间:
bytes 000-003 low-var
bytes 004-004 dynamic
bytes 005-007 low-var
这一步不是 Netzob 必需的,但很适合新手理解数据形态。
Step 5:使用 Format.splitStatic
这一段是 Netzob 的核心教学点之一。
脚本调用:
Format.splitStatic(...)
它会比较同一个 Symbol 中所有消息的同一字节位置:
- 如果所有样本都一样,可能是 static field。
- 如果样本之间不同,可能是 dynamic field。
脚本演示两种模式。
合并相邻字段
mergeAdjacentStaticFields=True
mergeAdjacentDynamicFields=True
这种模式会把连续的静态字段或动态字段合并。
如果样本中几乎每个字节位置都变化过,那么可能会合并成一个很大的 dynamic field。
不合并相邻字段
mergeAdjacentStaticFields=False
mergeAdjacentDynamicFields=False
这种模式会让每个字节都变成独立字段。
这样做的好处是:
fields[0] 对应 byte 0
fields[1] 对应 byte 1
fields[2] 对应 byte 2
后面做 clusterByKeyField 时就可以直接选择某个 byte offset。
Step 6:使用 clusterByKeyField 聚类
未知协议分析中,经常需要找一个“分类字段”。
例如真实协议里可能有:
- 版本字段
- 消息类型字段
- spacecraft ID
- virtual channel ID
- 命令类型
脚本先用统计方法找出低变化候选 offset,然后选择一个字段调用:
Format.clusterByKeyField(bytewise_symbol, bytewise_symbol.fields[key_offset])
这会把 key 字段值相同的消息放到同一组。
输出类似:
clusters created: 2
key=0x7d messages=6
key=0x54 messages=2
新手可以理解为:
按某个字节值把帧分组
这个过程不能直接证明字段含义,但可以帮助我们发现数据中是否存在不同类别。
Step 7:手工建立候选模型
脚本最后演示如何用 Netzob 手工定义一个候选结构:
candidate_header = Field(Raw(nbBytes=6), name="candidate_header_0_5")
candidate_insert_or_secondary = Field(Raw(nbBytes=8), ...)
candidate_payload = Field(Raw(nbBytes=...), ...)
candidate_tail = Field(Raw(nbBytes=4), ...)
这里的字段名都带 candidate_,意思是:
这只是猜测,不是已经确认的协议规范。
然后脚本创建:
Symbol(name="manual_candidate_tianwen_like_frame", fields=[...])
并打印结构树。
这一段的重点是学习 Netzob 如何表达协议结构,而不是确认 Tianwen-1 的真实字段。
主要函数说明
estimate_frame_size(...)
用于估计固定帧长。
它会遍历候选帧长,计算帧头前若干字节的平均熵,并按熵排序。
slice_frames(...)
把连续 bytes 切成固定长度帧。
build_symbol(...)
把 Python bytes 转成 Netzob RawMessage,再放入 Symbol。
byte_statistics(...)
统计每个字节偏移的唯一值数量、熵、最常见字节。
demonstrate_split_static(...)
演示 Netzob Format.splitStatic。
demonstrate_cluster_by_key_field(...)
演示 Netzob Format.clusterByKeyField。
demonstrate_manual_candidate_model(...)
演示如何用 Netzob Field 和 Raw 写候选协议结构。
和 Tianwen-1-frame-analysis-v2.py 的区别
Tianwen-1-frame-analysis-v2.py 是在已知 Tianwen-1 / CCSDS 解析结构的基础上分析数据。
它会使用:
Tianwen.ccsds
Tianwen.tianwen1_tm
而 Tianwen-1-parse-netzob.py 是未知协议探索教学脚本。
它关注的是:
不知道协议时,如何开始观察数据?
如何用 Netzob 包装消息?
如何用 Netzob 初步切字段?
如何用候选字段聚类?
如何写候选模型?
新手总结
这个脚本可以理解成一个“协议逆向入门练习”。
它的工作流是:
原始二进制文件
-> 估计帧长
-> 切成一帧一帧
-> 包装成 Netzob RawMessage
-> 放入 Netzob Symbol
-> 用 splitStatic 看固定/变化字段
-> 用 clusterByKeyField 按候选字段分组
-> 用 Field/Raw 写候选协议模型
如果你是新手,建议按这个顺序理解:
- 先看
raw:它只是原始字节。 - 再看
frames:它是按候选帧长切出来的帧列表。 - 再看
RawMessage:每帧被包装成 Netzob 消息。 - 再看
Symbol:一组消息被认为属于同一类格式。 - 再看
splitStatic:Netzob 开始帮你找字段变化规律。 - 再看
clusterByKeyField:Netzob 按候选字段值分组。 - 最后看
Field(Raw(...)):你开始把观察结果写成候选协议结构。
这就是这个文件最重要的教学目的。