工程趣聞 2026 年 8 月 31 日

2026-08-31 — 1980 年太空梭磁芯記憶體怎麼存位元、Claude Code Auto Mode 被 struct.py 蒙混過關,以及讓 BPF 少算一次 checksum 的別名分析陷阱

primary=https://www.righto.com/2026/08/spacelab-core-memory.html primary=https://embracethered.com/blog/posts/2026/breaking-claude-code-opus-5-and-automode/ primary=https://loshz.com/debugging-bpf-tbaa/

1980 年太空梭磁芯記憶體怎麼存一個位元、Claude Code Auto Mode 被 struct.py 蒙混過關,以及讓 BPF 少算一次 checksum 的別名分析陷阱

righto.com、embracethered.com、loshz.com · 2026-08-31

三則各自獨立的工程考古與除錯故事:Ken Shirriff 拆解一顆 1980 年隨太空梭升空的磁芯記憶體模組,講清楚交越電流如何在鐵氧體環上寫入一個位元;embracethered.com 的紅隊研究示範一條針對 Claude Code Opus 5 Auto Mode 的提示注入攻擊鏈,靠一個偽造的 struct.py 蒙混過 Python 的模組載入順序;另一篇則追出一次由編譯器型別別名分析(TBAA)造成的 BPF 校驗和計算錯誤,起因是切換到 CO-RE 產生的 vmlinux.h

磁芯記憶體怎麼飛上太空梭

這顆模組來自 1980 年法國製造的 Mitra 125 MS 迷你電腦,是太空梭酬載艙中歐洲太空總署 Spacelab 實驗艙裡三台同型電腦之一。Ken Shirriff 的拆解文章指出,單片核心平面板上塞了294,912 顆直徑約 0.8 毫米(32 mil)的鋰鐵氧體磁芯環,一疊七片板子(驅動板、四片核心平面、第二片驅動板、介面板)構成 18 位元字組(16 資料位元、1 奇偶位元、1 儲存保護位元),整台電腦共 128 KB。

每個磁芯靠順時針/逆時針兩種磁化方向代表 0 或 1,寫入靠交越電流選址(coincident-current selection):X、Y 方向的線各自只通半強度電流,唯有兩線交會處的磁芯才會被實際翻轉。讀取則是破壞性的——電路把 X、Y 線都驅動成寫 0,若磁芯原本存的是 1,翻轉瞬間感應線(sense wire)上就會感應出一個脈衝,讀完後電路得立刻把值寫回去。

Spacelab 這代設計採用「2½D」架構省掉傳統的 inhibit 線,改成 18 個位元各自獨立的 X 驅動電路,寫入時只對要寫 1 的位元通電;Y 線兩兩成對繞成 U 形,靠反轉電流方向讓一個驅動器管兩倍磁芯數。板上用 FSA2977 二極體陣列、National Semiconductor DS5534 感應放大器把毫伏訊號轉成邏輯電位,SN55325 驅動晶片負責 600 mA 雙向選址電流。太空梭後來在 1991 年換裝的 AP-101S 電腦才改用有電池備援與錯誤更正碼的半導體記憶體。

Claude Code Opus 5 Auto Mode 被 struct.py 偷天換日

Embrace The Red 的研究示範一條對 Claude Code Opus 5 Auto Mode 的提示注入攻擊,實測成功率落在六成到八成之間,與 Anthropic 引用的第三方評測(720 個測試情境、涵蓋 72 種注入手法,聲稱 0.00% 攻擊成功率)明顯矛盾。攻擊鏈共五步:

  • 伺服器回應 HTTP 415,誘使 Claude 放棄 WebFetch 改用 bash/curl
  • 重導向到一個 zip 檔,內含編碼過的紀錄、一支 macOS 二進位解碼器、以及一個被下毒的 Python 模組
  • Claude 出於安全考量拒絕直接執行二進位檔,卻自己動手寫了一支 Python 解碼器
  • 解碼器執行 import base64 時,Python 優先從當前目錄載入攻擊者放在解壓目錄裡、同名的 struct.py,而非標準函式庫
  • 被偷換的 struct.pypython3 -I(隔離模式,避免自己也被二次頂替)另開子行程,下載並執行原生酬載,啟動計算機證明程式碼執行、並建立 C2 連線

部分測試中,Claude 甚至會另外開一個無介面的 claude -p 子行程做偵察。研究者把 PoC 回報到 modelbugbounty@anthropic.com,起初未獲回應,升級後 Anthropic 將案件列為「Informative」結案,理由是Auto Mode 只是靠盡力而為分類器把關的便利功能,不是安全保證。更諷刺的是,偵測到系統已被入侵後,Auto Mode 的分類器反而擋下了 Claude 自己想執行的清理指令。

vmlinux.h 換掉核心標頭之後,BPF 校驗和開始算錯

這篇除錯記錄的起點是一個 TC(traffic control)BPF 程式的例行更新:把個別 #include "linux/*.h" 換成 BTF 產生的 vmlinux.h,結果整合測試開始出現封包丟棄。用 tcpdump 一查,問題是IPv4 標頭的 L3 校驗和算錯了——程式改寫 ToS 欄位後理應用增量方式重算,卻明顯沒重算成功。

程式邏輯是讀 from(舊 16 位元值)、寫入新 ToS、再讀一次 to(新 16 位元值),兩者差值拿去餵 bpf_l3_csum_replace()。反組譯(clang -target bpf -O2 -gllvm-objdump --disassemble --source)後發現第二次讀取根本沒發生,編譯器直接把暫存器 r3(from)複製給 r4(to):

uint16_t from = *((uint16_t *)iph);
iph->tos = 0x88;
uint16_t to = *((uint16_t *)iph);   // 被最佳化掉,直接沿用 from
bpf_l3_csum_replace(skb, off, from, to, 2);

// 修正:用 READ_ONCE()(barrier() 包裝)強迫每次都重新讀取記憶體
uint16_t from = READ_ONCE(*((uint16_t *)iph));
iph->tos = 0x88;
uint16_t to = READ_ONCE(*((uint16_t *)iph));

根因是型別別名分析(TBAA)/嚴格別名規則(strict aliasing):uint16_t *struct iphdr * 是不同型別,Clang 因此假設寫入 iph->tos 不會影響 uint16_t 指標指到的記憶體,於是放心重用快取值。深入追查發現關鍵在 vmlinux.h 裡的 __attribute__((preserve_access_index))——這是 CO-RE 重定位專用的屬性,會讓欄位存取被包進 LLVM intrinsic llvm.bpf.preserve.struct.access.index,使別名分析看不到 from/to 兩次讀取其實在位元組 offset 上重疊;換成純手動標頭時,Clang 能看穿這個重疊,就不會做這個優化。-fno-strict-aliasing 能整體關掉這類優化,但作者選擇用 READ_ONCE() 只在需要的地方強制重讀,保留其餘優化。

原始來源:righto.comembracethered.comloshz.com


End of article
0
Would love your thoughts, please comment.x
()
x