# LSB 圖片隱寫:在圖片裡偷渡敏感內容的方法 被加了浮水印的圖片,在網路上可以說是隨處可見,這還包括了繪師在圖畫作品一角的落款。 這些浮水印顯而易見,有時又因為破壞了畫面本身而有點惱人,但你知道其實有一種技術,可以偷偷地在圖片裡藏入浮水印,卻讓人渾然不覺嗎? 恰巧最近盲水印的議題在「知乎」等等網站特別流行,今天就來講講圖片的隱寫術吧。 ## ► 圖片的像素組成 在電腦的世界裡,除了向量圖形以外,我們常見的每一張 JPG、PNG、BMP 圖片都是由數十到數千萬個像素格組成的。 以光的三原色 RGB 色彩模型為例,每一種顏色都是由不同比例的紅、綠、藍組成,數值越高則越亮,而每一個像素就記載了這三個數值的資訊: ![](https://i.imgur.com/uDZF8Im.png) 複習之前寫的《跟著豬腳 C 起來:用電腦來操控資料吧》,我們可以知道,用「位元組」作為資料儲存的基本單位是一件對電腦而言比較方便的做法。一個位元組最高可以表示到十進位制的 255(也就是二進位制的 11111111),如果要表示更大的數字,就需要使用到第九個位元了,就像我們不可能只用三位數去表示一個大於 999 的數一樣。 把 R、G、B 數值各自用一個位元組儲存,像這樣分別用 256 個等級就足以讓電腦表示出 16,777,216 種不同的顏色了。因為再細分下去對於分辨不出差異的人眼來說沒有什麼意義,因此一般我們在電腦裡會習慣把 R、G、B 三種數值各自用 0~255 的範圍來表示最弱和最強,最多用三個位元組就可以儲存一個像素的資訊了。 ## ► 利用無損儲存的特性,埋藏資訊到圖片裡 我們知道 PNG 圖片的畫質通常比較好,這是因為 PNG 格式和 JPG 格式的特性不同——PNG 單純地把圖片像素內容壓縮,而 JPG 利用傅立葉轉換等數學方法,犧牲畫質來換取更高的輕便性。 假如我們用 PNG 格式來儲存圖片,那麼這些像素點一就是一、二就是二,不會有任何誤差。利用這點,再加上剛才提到的「人眼察覺不到細微的顏色變化」,我們就可以利用這些實際存在卻無法被察覺的小差異,在 PNG 圖片埋藏一些資訊。 比如,我們把剛才圖片的其中幾個像素格的 G 通道(綠色)動手腳,偷偷從 (127, 127, 127) 改成 (127, 126, 127): ![](https://i.imgur.com/9US11vU.png) 顏色被替換之後,我們即使用肉眼也看不出中間的兩個像素比較暗,因為他們的 G 值只有相差 1 而已。即使我們連同 R、B 值都做了同樣的微小更動,正常人也看不出差異。 假如我們把這些像素的 RGB 數值全都用二進位來表示的話,你就可以更明顯地注意到,每一個像素的 R、G、B 值在二進位模式下的第一位,都可以用來存放一個位元的數據(紅字部分): ![](https://i.imgur.com/Jimx8uF.png) 這個位置就被稱為「最低有效位」(LSB,Least Significant Bit),而偷偷印上去的內容也被稱為「盲水印」。 也就是說,對於一張 PNG 圖片,我們可以把每一個像素的 R、G、B 值的最低有效位全都替換成自己想要的內容,這張圖片也會看起來跟原來沒有什麼兩樣。假如你善用每一個像素的最低有效位,一張 800×600 的 PNG 圖片,就相當於提供了約 180 KB 的空間,可以塞得下整整 18 萬個英文字母! ## ► LSB 隱寫術的實作 隱寫術的實作其實不難,而且每個人都可以自行發明不同的方法,甚至可以結合加密的方式,使得這些藏進去的資訊即使被發現了也無法被直接解讀內容。 以最基本的隱寫術來說,我們循著這樣的流程: 1. 準備好水印的圖片,可以是 QR code、條碼、有意義的圖形,但只能有純黑和純白兩種顏色 2. 寫好一個程式,同時開啟原圖和水印圖片 3. 水印內容黑色的地方代表 0、白色的地方代表 1 4. 令程式把原圖和水印逐個像素對照,假如遇到黑色的像素,就把原圖 R 值的 LSB 設為 0,反之若遇到白色則設為 1 把上面的流程用 Golang 寫成程式([原始碼連結](https://hackmd.io/@upk1997/go-lsb-steganography-code)),實際測試之後,我們會得到這樣的結果: ![](https://i.imgur.com/KxLBOh0.png) 雖然我們看不出左右兩張圖的差異,但實際上右邊那張已經被上了隱寫的水印,用比較誇張的方式來示意,就會是這樣: ![](https://i.imgur.com/3FTindP.png) 以這個案例來說,當我們想要從動過手腳的圖片取出盲水印,只要檢查每一個像素 R 值的 LSB 是 1 或 0,就可以把藏在裡頭的水印還原出來了: ![](https://i.imgur.com/Sy7mhu4.png) ## ► LSB 隱寫術的應用 想像一個情境:假如你現在手上有一張重要的 PNG 圖片檔,打算分發給五個不同的人:A、B、C、D、E。 這張圖片不能外傳給任何人看到,但你也不曉得這五個人當中誰會把圖片外流出去。假如你在圖片上面直接寫上「A 專用不可外流」、「B 專用不可外流」……等等字樣,那麼收到圖片而有意外流的人就會輕易地發現這段文字,並且直接把這段浮水印塗抹掉。 於是,你在不事先通知他們的情況之下,把這張圖片製成了五個不同版本,這五個版本分別在 R 通道上面印上了五種不同的盲水印,你也把這五個版本分別發給五個人,並且把他們收到的版本記錄下來。 兩個禮拜後,你發現這張圖片檔被流出去了。 你把同樣的圖片下載一份回來,透過盲水印的萃取,你發現這份文件是當初傳給 B 先生的版本。 現在你知道你該找誰算帳了。 ## ► LSB 隱寫術的攻擊 對於 PNG 圖片上的 LSB 隱寫術,我們仍然可以透過一些做法在不必澈底分析的情況下把圖片的盲水印破壞掉。最簡單的方式就是把原先的圖片轉成 JPG 圖片,或者是利用修圖軟體裁切、變色、調整亮度、調整對比、添加噪點、使用模糊濾鏡,有必要的時候甚至還能用 GENIUS 截圖法——用相機直接對著電腦螢幕翻拍——當然,這樣也就等於惡意流出的人不得不降低圖片的品質來自保,假如這樣的盲水印是應用在付費的繪圖作品,那至少防止高畫質原圖被流出的目的也達成了。 想在圖片裡偷偷藏入資訊,不光只有 LSB 隱寫術。市面上其實也有針對 JPG 格式設計的盲水印演算法,甚至可以一定程度地抵抗上述修圖軟體對盲水印的破壞,只是這樣的技術就涉及到更多複雜的數學知識了,也許哪天摸熟了再特別寫成一系列主題來說說吧。