藏在五個頻道裡的字 | Isodata Seven | GaianSpace CTF 2026
🌐 English version: English
系列第三篇
LoRa 解調器在上一篇〈True Color Is a Lie〉打好骨架後,這篇換了新參數(隱式標頭),順便把隱寫的手法從 兩份相減 升級成 多頻道分群
系列其他篇:Channel Hop、Subsidy Claim、Two Voices、No Name on the Door、Hopscotch
題目在講什麼

拖拉機從黎明起就不再聽桅杆,改聽另一個陌生發射訊號源。Johnripper 終端機在經銷商的私有設定下收圖,而經銷商不公開設定,Babb 只有影印的服務手冊第 14、16 頁,中間的第 15 頁遺失了
共用 SDR 被搬到拖拉機棚,錄到了那個頻道,陌生人傳給拖拉機的是一整張圖塊(所有頻道),看起來像真的,只有北邊農田有一塊 Babb 解釋不了
課程筆記頁邊用雙底線寫著:「count what actually grows here(數數這裡實際種了什麼)」
目標:找出陌生人植入圖塊裡的東西。給的檔案有約 213 MB 的 .sigmf-data、手冊第 14/16 頁、以及遙測課程筆記
Flag 格式 GAIANSPACE{...}。
這次是隱式標頭
無線電參數(手冊第 14 頁)是 433.4 MHz、SF7、BW500k、CR4/5、網路字 0x2C、前導碼 10 符號、沒有射頻標頭(固定長度框架)
缺失的第 15 頁有框架長度表,要從資料自己推
沿用上一篇的解調器骨架,差別是這次是隱式標頭:前 8 個符號是低速率區塊(CR4/8),之後每 5 個符號一個區塊;長度由封包在空中的符號數推出
全部 779 個框架都通過手冊第 16 頁的 CRC-16 檢查後,還原出 5 張 128×128 的灰階圖:

每個頻道單看,那塊區域都像隨機雜訊,跟上一題不一樣
這可能正是題目說沒有任何波段洩漏的意思
先搞懂這張「圖」是什麼
衛星拍的不是一般相機的 RGB 照片,而是把不同波長的光分開記錄,每種波長存成一個頻道(波段)。
這題用到三類:
- 可見光:肉眼看得到的紅綠藍。健康植物的葉綠素會吸收紅光,所以植物在可見光裡偏暗、裸土偏亮
- 近紅外線(NIR):波長比紅光再長一點,肉眼看不到。健康植物的葉肉會強烈反射近紅外線,所以「近紅外線高」幾乎等於「這裡是活的植物」
- 短波紅外線(SWIR,Short-Wave InfraRed):波長更長,對水分特別敏感。含水多(溼土、灌溉後、含水的葉子)會吸收 SWIR 而顯得暗,乾燥地表則 SWIR 偏亮
所以每個像素其實帶著一組指紋:可見光亮不亮、近紅外線高不高、SWIR 高不高。如果把這些指紋拿去分群,就能把指紋相像的像素歸成同一類
ISODATA / k-means:數數這裡種了什麼
課程筆記提示用 ISODATA 分成 7 類(ISODATA 是遙測常用的非監督式分群,概念上就是 k-means)
對雜訊區的 5 維像素做 k-means,誤差在 k=4 時大幅下降(再往上加類別,誤差只是緩慢下降),表示那塊區域其實只有 4 個真正的類別。而這 4 類不是隨機的,它們剛好是兩個各自只有兩種值的開關組合出來的:
| 類別 | 可見光 | 近紅外線 | 短波紅外線 |
|---|---|---|---|
| 植被型 + SWIR 低 | 暗 | 高 | 低 |
| 植被型 + SWIR 高 | 暗 | 高 | 高 |
| 土壤型 + SWIR 高 | 亮 | 低 | 高 |
| 土壤型 + SWIR 低 | 亮 | 低 | 低 |
把這張表讀成兩個開關就清楚了:
- 開關 A:這塊地「看起來像植物」還是「看起來像土壤」(由可見光暗/亮、近紅外線高/低決定)
- 開關 B:SWIR 是「高」還是「低」
A × B = 2 × 2 = 4 種,剛好對上 k=4。
關鍵來了。正常情況下這兩個開關會自然搭配(真正的植物,A、B 會呈現該有的組合),但這邊陌生人做的手腳是,只在「要畫字」的那些像素上,把開關 B 偷偷反過來,這樣的結果是:
- 單看近紅外線那個頻道:字的像素和背景都是「植物」(A 相同),完全看不出差別 → 沒有洩漏。
- 單看 SWIR 那個頻道:字的像素只是 SWIR 剛好相反,但 SWIR 本來就滿地高高低低,看起來就是雜訊 → 也看不出來。
- 只有同時比對「A 和 B 一不一致」,字才會浮現。用數位的話說,就是把兩個開關做 XOR:一致 = 0、不一致 = 1,而文字正是畫在 XOR = 1 的像素上
這就是為什麼非得用「分群」而不是「看單一頻道」:分群會一次把所有頻道的指紋一起考慮,自然就能把「A 說是植物、B 卻反了」這種自相矛盾的像素獨立歸成小類別,那個小類別就是字。在整張圖上用 k=7 分群,那兩個「矛盾」的小類別就各自顯示一部分文字:

把那兩個類別合併畫出來,三行字清楚浮現:

讀出最後一行
字型是「粗體」版,每個字元是 5×6 的基本字形加上向右偏移 1 像素的副本
我用前兩行已知的字母(GAIANSPACE{、7TH-CLASS-)當對照,逐格確認最後一行。這裡我對 W/V 有點猶豫,最後選 W 正確
Flag
1 | GAIANSPACE{7TH-CLASS-XHTXWX} |
心得
- 這題的「植入」不是畫在某個頻道,而是把文字拆成兩份雜訊(XOR)分散到不同波段。 任何單一頻道都是雜訊,只有同時看所有頻道(分群)才現形——呼應筆記那句「the computer only knows which pixels look alike in all channels at once」。
- k-means 的誤差曲線會告訴你「真正有幾類」。 k=4 時誤差大幅下降、之後只是緩降,這就是筆記「count what actually grows here」的實作。
- 缺的那頁手冊不用補。 框架長度、頻道、列號都能從資料的欄位分布直接推出來。
- 小字型要先搞懂繪製規則再回推字形。 這題是「向右偏移 1 像素的粗體」,弄清楚規則後逐格比對,比直接看放大圖可靠。


