優衣庫T恤上印了一段會「自己執行」的bash script,工程師靠肉眼OCR把它挖出來
Tris Sherliker's Blog · 2026-07-09
2026年7月,工程師Tris Sherliker的太太買了一件Uniqlo與Akamai合作推出的「Peace for All」紀念T恤。T恤正面印著一顆愛心夾在大括號中間,背面則是一整片密密麻麻的英數字圖案。Sherliker一眼就認出那不是隨機圖樣,而是一段被base64編碼過的bash腳本。
發現經過
那段文字的開頭是熟悉的shebang#!/bin/bash,後面接著一長串base64字元,再串接base64 --decode | eval的執行模式。這種「解碼後直接丟給shell執行」的寫法,正是Sherliker口中「基本上就是病毒傳播的標準手法」——把不透明的內容交給eval,執行前完全看不出實際行為。問題是,這段文字只印在衣服上,沒有數位檔案可以複製貼上,唯一的輸入管道是相機和肉眼。
技術細節:三重OCR比對
要把印在布料上、可能因為拍照角度和布紋扭曲的字元,精準還原成可執行的base64字串,任何一個字元錯誤都會讓解碼直接失敗——base64沒有內建錯誤更正機制。Sherliker因此並用三種辨識管道互相校對:
- Android的「畫圈搜尋」OCR功能,他形容效果「通常很不錯」
- Tesseract,並反覆調整辨識參數
- Claude AI,作為第三方比對來源
他把三份結果互相diff,再請Claude產生差異位置表,方便人工逐一核對修正。最後靠著字串收尾處剛好對齊的引號與括號作為完整性檢查,確認整段轉錄無誤。
解碼結果:一段終端機動畫
還原後的腳本其實無害,是一段會在終端機裡跑出正弦波動畫的裝飾程式。它讓字串♥PEACE♥FOR♥ALL♥逐字沿著水平正弦曲線移動,並用256色終端色碼在青色(色碼12)與橘色(色碼208)之間做漸層。核心運算如下:
freq=0.2
angle=$(echo "($t) * $freq" | bc -l)
sine_value=$(echo "s($angle)" | bc -l)
x=$(echo "($cols / 2) + ($cols / 4) * $sine_value" | bc -l)腳本還用tput civis隱藏游標,並以trap "tput cnorm; exit" SIGINT確保使用者按下Ctrl+C離開時游標會恢復正常——算是少見地在一段惡搞彩蛋裡,還顧到了終端機的收尾禮貌。
字體側寫的插曲
Sherliker原本從「0字型偏斜的斜線、1沒有襯線、曲線帶圓角」等特徵推斷背後印刷字體是Consolas,後來Hacker News網友ralphinus指正,實際上是Roboto Mono。Akamai事後發布的新聞稿也解釋了設計脈絡:以「米色機殼」與Linux象徵早期網際網路基礎建設,呼應公司成立超過25年的里程碑。文章發布後,包括Wen Chuan Lee在內也有其他人各自獨立解出同一段程式,Hacker News上也出現了對應討論串。
一顆「touchstart」地雷,埋了七年才炸到左撇子使用者
Terence Eden's Blog (shkspr.mobi) · 2026-07-08
部落客Terence Eden收到一位忠實讀者的抱怨:每次滑動頁面,留言回覆框都會莫名其妙彈出來打斷閱讀。Eden起初完全複製不出這個問題,直到讀者補了一句關鍵資訊——他是左撇子,習慣用左手拇指滑手機。這句話成了破案的鑰匙,因為Eden自己一直是用右手拇指滑動頁面。
發現經過
頁面上的「回覆」連結被放在留言區塊的左側。右撇子讀者滑動時,拇指軌跡通常落在螢幕右半邊,不會碰到左側的連結;左撇子讀者的拇指軌跡則正好會滑過去,不小心觸發連結背後綁定的事件。這個bug因此只對左手滑動的使用者顯現,右撇子(包含開發者本人)完全感覺不到異狀,這也是它潛伏七年才被回報的原因。
根本原因
循著程式碼往回追,問題出在WordPress核心一段2017年加入的touchstart事件監聽器,對應提交14dd35e62e1325ca1c6caef0a8623ce3f02d7da9。當年瀏覽器要區分「單純點擊」與「雙擊縮放」,中間會插入約300毫秒的判斷延遲,工程師於是替連結加上touchstart監聽器,試圖讓觸控回應更即時。
- 2017年:為解決300ms觸控延遲,替連結加上
touchstart監聽器 - 問題:多數瀏覽器其實早在2013年就已經修掉了這個300ms延遲
- 結果:監聽器留在程式碼裡整整多餘了好幾年,卻沒人移除
- 觸發條件:左側連結 + 左手拇指滑動軌跡 = 意外觸發回覆框
修法與收尾
對應的WordPress Trac票券是#46713,早在多年前就已經被回報,卻遲遲沒人接手排查。Eden最終確認問題後,提交了GitHub PR #12168,修法內容非常簡單——直接刪掉那段過時的touchstart監聽器程式碼就解決了問題。整起事件的教訓在於,一段「為了修復舊瀏覽器行為」而加入的相容性程式碼,如果沒有隨著瀏覽器演進被清理,就可能在特定使用習慣的使用者身上,變成一顆潛伏多年的地雷。
原始來源:shkspr.mobi、WordPress Trac #46713、WordPress GitHub PR #12168
下毒、紅色警戒區與影子記憶:拆解Linux核心裡的記憶體抓鬼機制KASAN
Bootlin Blog · 2026-07-06
Bootlin工程師Alexis Lothoré在部落格發文,深入拆解Linux核心裡的KASAN(Kernel Address Sanitizer)如何在執行期抓出越界存取與use-after-free這兩大類記憶體錯誤。文章開頭就點出Linux核心至今仍以C語言為主,雖然這是撰寫底層程式的既定語言,卻也帶來製造錯誤的高度自由度,結果就是各種型態的未定義行為、緩衝區溢位與segmentation fault。KASAN正是為了在這些錯誤釀成災難前,提早在開發階段抓出來而生。
核心機制:影子記憶體
KASAN的作法是結合編譯器插樁與核心維護的「影子記憶體」(shadow memory),用一塊獨立的記憶體區域即時記錄每一段實際記憶體的可存取狀態。基本編碼單位是每8個位元組(一個「granule」)對應1個影子位元組,換算下來,generic KASAN模式預設會拿系統記憶體的八分之一來做這件事。位址換算公式是:
shadow_byte = (target_addr >> KASAN_SHADOW_SCALE_SHIFT) + KASAN_SHADOW_OFFSET其中KASAN_SHADOW_SCALE_SHIFT在generic KASAN模式下固定為3,在x86_64架構上,影子記憶體區段對應到位址0xffffec0000000000。
紅色警戒區與下毒
影子位元組的值本身就是一份小型狀態表:0x00代表整個granule可正常存取,0x01到0x07代表granule裡只有部分位元組合法(例如0x03代表前3個位元組可用),負值則代表整段記憶體已被「下毒」(poisoned),不允許存取。專門的標記值如KASAN_SLAB_FREE用來標示剛被釋放的記憶體,KASAN_SLAB_REDZONE則標示配置區塊前後刻意留出的「紅色警戒區」緩衝地帶,一旦程式碼寫超界踩進這塊緩衝區,就會立刻被攔截。
- 配置階段:
kasan_slab_alloc呼叫kasan_unpoison把影子位元組清成0x00 kasan_kmalloc會透過poison_kmalloc_redzone把配置後多出來的空間標記為不可存取- 釋放階段:
kfree觸發kasan_poison_slab_object,把整塊記憶體標成KASAN_SLAB_FREE - 「檢疫區」(quarantine)機制會刻意延後SLAB配置器真正回收該記憶體的時間,降低立即被重新配置、掩蓋use-after-free的機率
編譯期插樁與兩種模式
這一切的前提是編譯器插樁,核心透過scripts/Makefile.kasan加上-fsanitize=kernel-address旗標,讓編譯器在每次記憶體讀寫前自動插入檢查呼叫,例如讀取對應__asan_load1到__asan_loadN,寫入對應__asan_store1到__asan_storeN,這些函式統一由DEFINE_ASAN_LOAD_STORE巨集展開產生。插樁又分成outline與inline兩種模式:outline模式每次存取都呼叫一次外部函式,程式碼體積小但執行期開銷較高;inline模式則直接把位移、比對邏輯內嵌展開,快速路徑就是比對影子位元組是否為0x00,失敗才跳轉到__asan_report_load8_noabort之類的慢速路徑,換來的是更大的核心映像檔案。
組態選項與變體
對應的核心組態選項包括CONFIG_KASAN總開關、CONFIG_KASAN_GENERIC(標準的每8位元組一位元組追蹤)、CONFIG_KASAN_OUTLINE與CONFIG_KASAN_INLINE對應前述兩種插樁模式,還有CONFIG_KASAN_VMALLOC用來追蹤vmalloc配置的記憶體、CONFIG_KASAN_STACK替堆疊變數加上紅色警戒區,以及內建自我測試模組的CONFIG_KASAN_KUNIT_TEST。除了generic KASAN之外,文章也提到軟體tag-based模式能降低記憶體開銷,而硬體tag-based模式則是利用ARM64專屬的記憶體標籤延伸(MTE)指令集,把追蹤負擔幾乎完全轉嫁給硬體。文中也示範用virtme-ng搭配CONFIG_KASAN=y、CONFIG_KUNIT=y、CONFIG_KASAN_KUNIT_TEST=m載入kasan_test模組,直接在虛擬機裡重現各種記憶體違規情境。
原始來源:Bootlin Blog