Files
CCSDS_study/test/__pycache__/Tianwen-1-parse-netzob.cpython-38.pyc

108 lines
16 KiB
Plaintext
Raw Normal View History

2026-05-05 21:54:35 +08:00
U
<00><><EFBFBD>i<EFBFBD>F<00>@s<>dZddlmZddlZddlZddlZddlmZddlm Z e
ej d<06>rZej j dd<08>e e <0A><01><0E>jjZed d
d Ze<11><12>r<>ej<13>dee<11><01>dd lTed dZddd<11>dd<13>ZdIdddd<17>dd<19>Zdd<1B>dd<1D>Zddd<1F>d d!<21>Zdddddd"<22>d#d$<24>ZdJddd%d&d'<27>d(d)<29>Zd&dd*d+<2B>d,d-<2D>Zd&d.d/<2F>d0d1<64>ZdKd.ddd3<64>d4d5<64>Z d.dd6<64>d7d8<64>Z!d&d*d/<2F>d9d:<3A>Z"d&d*d/<2F>d;d<<3C>Z#d&d.ddd=<3D>d>d?<3F>Z$ddd@<40>dAdB<64>Z%dCd<1B>dDdE<64>Z&dd<1B>dFdG<64>Z'e(dHk<02>r<>e'<27>dS)Lu
Teach unknown-frame analysis with Netzob on Tianwen-1 raw frame data.
这个脚本的目标不是“直接使用已知的 Tianwen-1 / CCSDS 解析器”,而是假设我们
只拿到一段连续的二进制帧数据,不知道具体空间帧协议,然后用 Netzob 的核心
概念做一次可运行的协议探索教学。
重点演示的 Netzob 概念:
1. RawMessage把每一帧原始字节包装成 Netzob 消息。
2. Symbol把一组相似消息放进同一个协议符号。
3. Format.splitStatic根据样本中固定/变化的字节位置自动切字段。
4. Format.clusterByKeyField选择某个字段作为 key把消息按字段值聚类。
5. Field / Raw在已有观察基础上手工建立一个“候选帧格式”模型。
注意:
- 本脚本不会 import Tianwen.ccsds也不会调用 AOSFrame.parse。
- 为了教学和运行速度,默认只抽样前 96 帧做 Netzob 推断。
- 原始数据较大,完整协议逆向通常需要多轮实验;这里侧重方法和工具用法。
<EFBFBD>)<01> annotationsN)<01>Counter)<01>Path<74> reconfigureT)<01>line_bufferingz
netzob-030Ztest<73>src)<01>*ZTianwenztianwen1_frames_20200730.u8<75>str<74>None)<02>title<6C>returncCstd<01>t|<00>td<02>dS)u*打印一个清晰的教学分节标题。zO
==============================================================================zN==============================================================================N)<01>print)r <00>r<00>test/Tianwen-1-parse-netzob.py<70>section7sr<00>0<00>bytes<65>int)<03>data<74> max_bytesr cCs*|d|<01><00>d<02>}|t|<00>|kr$dndS)uQ把 bytes 转成短十六进制字符串,避免一帧 220 字节全部刷屏。N<E38082> z ...<2E>)<02>hex<65>len)rr<00>headrrr<00> short_hex?sr<00>float)r cs,t|<00>}t|<00><01>t<02>fdd<02>|<01><03>D<00><01> S)u<>计算一组离散值的 Shannon entropy。
entropy 越低,说明这个字节位置越稳定,越像版本号、固定标识或填充。
entropy 越高,说明这个字节位置变化越丰富,越像计数器、时间戳或载荷。
c3s$|]}|<01>t<00>|<01><00>VqdS)N)<02>mathZlog2)<02>.0<EFBFBD>count<6E>Ztotalrr<00> <genexpr>Oszentropy.<locals>.<genexpr>)rr<00>sum<75>values)r#<00>countsrr r<00>entropyFsr%r)<02>pathr cCs|<00><00>std|<00><00><02><01>|<00><02>S)u读取原始二进制文件。zinput file not found: )<03>exists<74>FileNotFoundErrorZ
read_bytes)r&rrr<00>load_raw_bytesRsr))<05>raw<61> candidate_min<69> candidate_max<61> sample_frames<65>header_columnsc s<>g}t||d<00>D]<5D><>tt<02><02><01>|<03>}|dkr2q<12><00>fdd<04>t|<06>D<00>}t|<04><00>}g} g}
t|<08>D]6<><01>fdd<04>|D<00>} | <09>t| <0B><01>|
<EFBFBD>tt| <0B><01><01>qd|<05><03>t| <09>t| <09>t|
<EFBFBD>t|
<EFBFBD>|d<06><04>qt|dd<08>d <09>S)
u%在不知道帧长时,用“候选帧长打分”的方式找可能帧长。
思路很简单:
- 如果帧长猜对了,那么每一行的开头会对齐到真实帧头。
- 真实帧头通常包含版本号、ID、计数器等结构化字段。
- 这些字段的熵一般比随机载荷低。
- 所以对每个候选帧长,把数据切成多行,计算前若干列的平均熵。
- 平均熵越低,越可能是正确帧长。
这不是严格证明,只是未知协议分析中常用的启发式方法。
<20><00>cs$g|]}<01>|<01>|d<00><00><00>qS<00>r/r<00>r<00>i<><02>
frame_sizer*rr<00>
<listcomp>ssz'estimate_frame_size.<locals>.<listcomp>csg|] }|<01><00>qSrr<00>r<00>frame<6D><01>offsetrrr6zs)r5<00> avg_entropy<70>
avg_unique<EFBFBD> frame_countcSs|d|dfS)Nr;r<r<00><01>itemrrr<00><lambda><3E><00>z%estimate_frame_size.<locals>.<lambda><3E><01>key)<08>range<67>minr<00>appendr%<00>setr"<00>sorted) r*r+r,r-r.Zresultsr=<00>frames<65>columnsZ entropiesZ unique_countsr#r)r5r:r*r<00>estimate_frame_sizeZs. <06>
 <02><04> rKz
int | Nonez list[bytes])r*r5<00>limitr cs6t<00><01><01>}|dk rt||<02>}<03><00>fdd<03>t|<03>D<00>S)u*把连续字节流切成固定长度帧。Ncs$g|]}<01>|<01>|d<00><00><00>qSr1rr2r4rrr6<00>sz slice_frames.<locals>.<listcomp>)rrErD)r*r5rLZ total_framesrr4r<00> slice_frames<65>s 
rM<00>Symbol)rI<00>namer cCs.dd<02>|D<00>}t||d<03>}|j<01>tt<04><01>|S)uD把 bytes 帧列表包装成 Netzob RawMessage再放入 Symbol。cSsg|]}t|d<00><01>qS))r)Z
RawMessager7rrrr6<00>sz build_symbol.<locals>.<listcomp>)<02>messagesrO)rNZencodingFunctions<6E>addZTypeEncodingFunctionZ
HexaString)rIrOrP<00>symbolrrr<00> build_symbol<6F>s rSz
list[dict])rIr c s^t|d<00>}g}t|<01>D]@<40><00>fdd<03>|D<00>}t|<03>}|<02><03>t|<04>t|<03>|<04>d<04>d<05><04>q|S)u?按字节偏移统计唯一值数量、熵和最常见取值。rcsg|] }|<01><00>qSrrr7r9rrr6<00>sz#byte_statistics.<locals>.<listcomp><3E>)r:<00>uniquer%<00>top)rrDrrFr%<00> most_common)rIr5<00>statsr#r$rr9r<00>byte_statistics<63>s  <08><04>rY<00> )rX<00> first_columnsr c Csntd<01>td<02>|d|<01>D]L}d<04>dd<06>|dD<00><01>}t|dd <09>d
|d d <09>d
|d d <0A>d|<03><00><07>qdS)u*打印前若干字节位置的统计表。z1offset unique entropy most common byte valuesz1------ ------ ------- -----------------------N<>, css$|]\}}d|d<01>d|<02><00>VqdS<00>Z0xZ02x<32>:Nr<00>r<00>valuerrrrr!<00>sz#print_byte_stats.<locals>.<genexpr>rVr:z>6<> rUr%z>7.3fz )r <00>join)rXr[r?rVrrr<00>print_byte_stats<74>s*<2A>rc)rXr c
Cs.g}|D]>}|ddkr$|<01>d<03>q|ddkr<|<01>d<05>q|<01>d<06>qg}d}|d}t|dd<08>dd <09>D],\}}||krl|<03>||d|f<03>|}|}ql|<03>|t|<01>d|f<03>td
<EFBFBD>|dd <0B>D]<\}}}||d} td |d <0A>d|d <0A>d| d <0A>d|<07><00><08>q<>t|<03>d k<04>r*tdt|<03>d <00>d<12><03>dS)uJ根据 unique 数量粗略标出固定区、低变化区和高变化区。rUr/Zstaticr0zlow-varZdynamicrN)<01>startz.candidate byte regions from simple statistics:<3A>(z bytes <20>03d<33>-z width=raz ... z more regions omitted)rF<00> enumeraterr )
rX<00>labelsr?ZregionsrdZcurrent<6E>indexZlabel<65>end<6E>widthrrr<00>print_static_dynamic_regions<6E>s,      (rmcCs<>t|d<01>}td<02>tdt|j<03><01><00><02>tj|tjddd<05>td<06>tdt|j<03><01><00><02>td<08>t|d <09>}tj|tjd
d
d<05>td <0B>td t|j<03><01><00><02>td <0A>t|jdd<0F><00>D]\}}td|d<11>d|<04><00><04>q<>t|j<03>dkr<>tdt|j<03>d<00>d<14><03>|S)u2用 Netzob splitStatic 展示自动字段切分。<E58886>unknown_tianwen_framesz.Before splitStatic, Netzob sees one raw field:z number of fields: T<>ZunitSizeZmergeAdjacentStaticFieldsZmergeAdjacentDynamicFieldszE
After splitStatic(unitSize=8, merge adjacent static/dynamic fields):z number of inferred fields: zz teaching note: if most byte positions vary at least once, adjacent dynamic bytes can merge into one large dynamic field.<2E>unknown_tianwen_frames_bytewiseFz=
After splitStatic(unitSize=8, do not merge adjacent fields):z( number of inferred byte-level fields: z first inferred field labels:N<>z
field[<5B>02d<32>] z ... z more fields) rSr r<00>fields<64>Format<61> splitStatic<69>UnitSize<7A>SIZE_8rh)rIrR<00>bytewise_symbolrjZfieldrrr<00>demonstrate_split_static<69>s:
<02><02>
<02>rzcCs"t|d<01>}tj|tjddd<03>|S)uQ把每个字节都切成独立 Field方便选择某个 offset 做聚类 key。rpFro)rSrurvrwrx)rIrRrrr<00>build_bytewise_symbols
<02>r{)rIrX<00>cluster_sample_sizer c Cs<>dd<02>|dd<04>D<00>}t|dd<06>d<07>}|s6td<08>dStd <09>|dd
<EFBFBD>D]N}d <0B>d d <0A>|dD<00><01>}td|dd<11>d|d<00>d|dd<16>d|<05>d<18> <09>qJ|dd}|d|<02>}tdt|<07><01>d<1B><03>t|<07>}td|<06>d<1D><03>tdt|j<05><01><00><02>t<06>||j|<00>} tdt| <09><01><00><02>t| <09> <09><00>dd <20>D]T\}
} t
|
t t f<02><02>rJ|
<EFBFBD> <0A>nt|
<EFBFBD>} td!| d"<22>d#t| j<0F>d$<24>d%t| j<05><01><00><06><00>q*dS)&uC演示如何用某个候选字段作为 key 进行 Netzob 聚类。cSs,g|]$}d|dkr dkrnq|<01>qS)r/rU<00> r)rr?rrrr6s
 <0C>z4demonstrate_cluster_by_key_field.<locals>.<listcomp>NrZcSs|d|dfS)NrUr%rr>rrrr@rAz2demonstrate_cluster_by_key_field.<locals>.<lambda>rBz<No low-variation key candidates found in the first 32 bytes.z3candidate key byte offsets from the first 32 bytes:r0r\css$|]\}}d|d<01>d|<02><00>VqdSr]rr_rrrr!'sz3demonstrate_cluster_by_key_field.<locals>.<genexpr>rVz offset r:rrz : unique=rUz
, entropy=r%<00>.3fz, top=[<5B>]rz using zU frames for this cluster demo (kept small because Netzob clustering can be expensive)z.
Netzob clusterByKeyField demo on byte offset r^z bytewise fields available: z clusters created: r}z key=0xz<4z messages=z>4z fields=)rHr rbrr{rtruZclusterByKeyField<6C>list<73>items<6D>
isinstancer<00> bytearrayrr rP) rIrXr|Z
candidatesr?rVZ
key_offsetZcluster_framesryZclustersrCZcluster_symbolZkey_hexrrr<00> demonstrate_cluster_by_key_fields8
<EFBFBD>,<2C>  <0E> $<24>r<EFBFBD>)r5r cCs<>td<01>ttdd<03>dd<05>}ttdd<03>dd<05>}ttt|dd <09>d<03>d
d<05>}ttd d<03>d d<05>}td ||||gd<0E>}td<0F>t|<05><06><00>dS)u 用 Netzob Field/Raw 手工搭建一个候选格式模型。
这一步不是声称字段含义已经确定,而是演示逆向分析常见工作流:
先用统计和 splitStatic 找到疑似字段边界,再用 Netzob 明确描述一个候选模型。
z5Step 7 - Manual candidate model with Netzob Field/Raw<61>)ZnbBytesZcandidate_header_0_5)rOr0Z"candidate_insert_or_secondary_6_13<31>r<00>candidate_payloadrTZcandidate_tail_4_bytesZ#manual_candidate_tianwen_like_frame)rOrtzBThis is a teaching model, not a confirmed Tianwen-1 specification:N)rZFieldZRaw<61>maxrNr Z str_structure)r5Zcandidate_headerZcandidate_insert_or_secondaryr<79>Zcandidate_tailrRrrr<00>"demonstrate_manual_candidate_modelFs*<02><02><02><02>
r<>zargparse.NamespacecCs<>tjdd<02>}|jdttdt<04><00>d<05>|jdtddd<05>|jd td
d d<05>|jd td dd<05>|jdtddd<05>|jdtddd<05>|jdtddd<05>|<00><06>S)u命令行参数。zHUse Netzob to teach unknown binary frame analysis on Tianwen-1 raw data.)Z descriptionz--inputz raw binary input file, default: )<03>type<70>default<6C>helpz --frame-sizeNzUknown or chosen frame size. If omitted, the script estimates it from candidate sizes.z--candidate-min<69><6E>z>minimum frame-size candidate used when estimating frame lengthz--candidate-maxiz>maximum frame-size candidate used when estimating frame lengthz --sample-size<7A>`z)number of frames used for Netzob analysisz--show-samplesrTz1number of raw sample frames to print as short hexz--cluster-sample-sizer0z@number of frames used only for the Netzob clusterByKeyField demo)<07>argparse<73>ArgumentParser<65> add_argumentr<00> DEFAULT_INPUTr<00>
parse_args)<01>parserrrrr<>is\<02><08><02> <02><02><02><02><02>r<>c Cs,t<00>}td<01>t|j<03>}td|j<03><00><02>tdt|<01>d<04><04><02>td<05>|jdkr<>t||j|j |j
dd<08>}td <09>|dd
<EFBFBD>D]:}td |d d <0A>d|dd<10>d|dd<13>d|d<00><00><08>qz|dd }td|<04><00><02>n|j}td|<04><00><02>t|<01>|}t |||j
d<19>}td|d<04><04><02>tdt|<06>d<04><04><02>td<1C>t |d|j <0A><00>D]$\}}td|d<1E>dt|<08><01><00><04><00>q@td <20>t|d!<21>} td"<22>td#t| j<10><01><00><02>td$| j<11><00><02>td%t| j<12><01><00><02>td&<26>td'<27>t|<06>}
t|
d(d)<29>t<04>t|
<EFBFBD>td*<2A>t|<06>td+<2B>t||
|j<18>t|<04>td,<2C>td-<2D>dS).zRun the teaching analysis.z!Step 1 - Read unknown binary dataz input file: z total bytes: <20>,z.Step 2 - Estimate or choose a fixed frame sizeN<65>)r+r,r-r.z8Top frame-size candidates by low average header entropy:<3A>
z size=r5z>3z avg_entropy=r;r~z avg_unique=r<z.2fz frames_tested=r=rz6
Chosen frame size for the rest of this teaching run: z Using user-provided frame size: )rLz(complete frames in file with this size: zframes sampled for Netzob: z"
First sample frames as short hex:z frame[rfrsz5Step 3 - Wrap samples as Netzob RawMessage and SymbolrnzNetzob objects created:z RawMessage count: z Symbol name: z Initial field count: zqTeaching point: at the beginning Netzob only knows each frame is raw bytes; it does not know the protocol fields.z;Step 4 - Byte-position statistics before protocol knowledgere)r[z2Step 5 - Netzob Format.splitStatic field inferencez8Step 6 - Netzob clusterByKeyField on candidate key byteszDone - What to try nexta<74>1. Increase --sample-size to see whether inferred fields remain stable.
2. Try --frame-size with another candidate and compare splitStatic results.
3. Choose another low-variation offset as cluster key and inspect clusters.
4. After a candidate field map is stable, then compare it with known CCSDS/Tianwen parsing.
5. Treat this as protocol-discovery scaffolding, not as a final specification.)r<>rr)<00>inputr rr5rKr+r,Z sample_sizerMrhZ show_samplesrrSrPrOrtrYrcrmrzr<>r|r<>) <0B>argsr*Zrankedr?r5Zall_frame_countrIrjr8Zteaching_symbolrXrrr<00>main<69>sj

<02>0<>  
<02> <02>r<EFBFBD><00>__main__)r)N)rZ))<29>__doc__Z
__future__rr<>r<00>sys<79> collectionsr<00>pathlibr<00>hasattr<74>stdoutr<00>__file__Zresolve<76>parentZ PROJECT_ROOTZLOCAL_NETZOB_SRCr'r&<00>insertr Z
netzob.allr<6C>rrr%r)rKrMrSrYrcrmrzr{r<>r<>r<>r<><00>__name__rrrr<00><module>s@      0   * 3#8M