從PNG Dropper到Alpha通道隱寫術實踐
0x1 前言
前些天從安全客上看到一篇翻譯文章是關於利用PNG畫素隱藏PE程式碼,對實現細節很感興趣就抽空稍微研究了下相關內容,於是就有了本次分享。
0x2 PNG Dropper樣本分析
先來看下翻譯文中的樣本,其中PE資源裡包含一個PNG目錄,該目錄裡儲存了8張PNG格式的圖片:
將8張圖片全部提取出來,隨便選擇一張開啟檢視,發現可以正常開啟,但顯示的內容都是一些雜亂的隨機畫素點:
將其拖進010edit分析下格式,能夠正常解析圖片資料,資料格式貌似沒什麼異常:
再來跟蹤一下該樣本記憶體解密PE的過程。整體流程是依次定位資源中的8張PNG圖片,提取其中的圖片資料到緩衝區裡拼湊成完整的PE,最後再記憶體LoadPE執行。
然後最關心的部分應該就是提取圖片資料的過程了。這部分的程式碼看上去比較多,其實主要也就3小部分。第1部分是將PNG圖片流讀進一個Bitmap物件來方便操作圖片:
第2部分是獲取該PNG的各項基本資料包括寬度、高度、Stride(行跨度?)和影象格式,這些資料主要是用作最後一步呼叫的結構體引數(Rect和BitmapData),這部分最關鍵,因為這些引數直接影響最後獲取到的圖片資料。
最後1步其實很簡單,呼叫“GdipBitmapLockBits”函式就能直接獲取到圖片的資料了。當然這裡得到的圖片資料不是上文010edit裡看到的那個IDAT資料,而是對該資料根據第2步指定的影象格式引數來進行解碼後的“原始畫素”資料。
根據此流程也可以自己嘗試用Gdi+寫程式碼來提取圖片資料,然後發現從上面樣本中圖片獲取到的編碼格式是“PixelFormat32bppARGB”,然而實際內部儲存的畫素資料卻用的是“PixelFormat16bppARGB1555”編碼格式,這可能是作者的一種反檢測小技巧吧。
0x3 Alpha通道隱寫實踐
本來分析到上面也就結束了,結果看到安全客上有評論談及隱寫術,想起了大學時期使用Matlab做LSB影象資訊隱藏的實驗,當時只是簡單應付課程對其懵懵懂懂實在有點遺憾,於是趁此機會重新複習下順便研究看看所謂高階的隱寫術——利用RGBA中的A(Alpha)通道來隱藏資料。
首先複習LSB(最低有效位),這個是指影象隱藏採用的一種演算法吧,查了一下這個演算法看網上有用jpg圖片來做,也有用bmp圖片來做的,還有jpg處理完變png的,有點暈,其實應該是針對一般的RGB影象都能做。怎麼理解呢,我們知道影象是由一個個的畫素點構成,比如一張100×100(寬度和高度都是100)的圖片,總共就有10000個畫素點,而其中每個畫素點都用一個RGB值來表示其顏色。對於不同的圖片格式,其畫素資料的編碼格式可能不一樣,RGB值的儲存格式也就可能不同,即使相同格式比如PNG,其畫素格式也有很多種,具體可參考“RGB格式詳解”。本文主要以其中的“RGB32”或“ARGB32”為例,一個RGB值用3位元組來分別表示R(紅)、G(綠)、B(藍)3個顏色通道的數值,這樣每個顏色通道的數值範圍都是0-255,LSB影象隱藏演算法的原理就是利用其中每個畫素顏色通道的最低位來儲存需要隱藏的資料,這樣對於整張影象的“視覺影響”非常低,圖片處理前後就可能肉眼看上去“沒什麼變化”。
正好前段時間在看雪公眾號上看到一篇用PHP來實現LSB的分享,直接貼上來先看看實現過程吧,最核心部分的就是將需要隱藏的資料的每個二進位制位,依次存進每個選取畫素點中藍色(B)通道的最低位。
隨便找一張圖片測試一下效果吧:
完全看不出差異,只是隱藏的資訊“hello”字串實際上已經儲存到右邊圖片的畫素矩陣中,資料位依次被隱藏到所選45度角線上的畫素點。當然這只是個簡單的例子,我們自己寫工具時可以從原點開始遍歷所有畫素點,也可以將其他2個通道最低位都用上以提高資訊儲存的容量。但總的來說,個人覺得LSB能夠儲存的容量還是相對比較小,比如我想把系統的計算器程式calc.exe(大約900kb)存進一張圖片中則需要一張大約100萬畫素的圖片。如下所示,經過自己實現的LSB工具(使用每個畫素的最低位依次儲存)處理後,將計算器程式放到圖片中,圖片的size前後膨脹了3MB!
資訊提取的話只要反向操作畫素資料就行,具體可以參見本文後面分享的Demo。這裡就不繼續探究LSB了,感興趣的網上搜一下參考資料一大堆。接下來直接進入PNG的Alpha通道隱寫這個話題,具體地本文是以PNG的“PixelFormat32bppARGB”這種畫素格式的影象隱寫為例進行分享。
其實我並不瞭解真正的Alpha通道隱寫是什麼樣的,也沒有去找相關實現的資料,此番分享完全是自己摸索一下基於“PixelFormat32bppARGB”格式的PNG圖片如何進行資訊隱藏。這種格式非常容易理解,就是在3位元組RGB基礎上再加一個A通道,也就是Alpha或者透明通道,於是正好組成4位元組一組的畫素資料(從高到低為ARGB)。然後對這個A通道的理解很關鍵,這個位元組的數值表示的是該畫素點的透明度:數值為0時,該畫素點完全透明,相當於RGB完全隱藏不用;數值為255時該畫素點只由RGB顏色決定,相當於沒有透明度。如下圖所示,左圖的背景全透明,A通道數值為0,;右圖背景設定(不)透明度為50%,則剛好為1位元組數值範圍的一半即0x80。
接著就很自然而然地想利用這種特性來藏資料了。既然透明度為0畫素點完全透明,那該畫素點的RGB這3個通道的位元組資料我們不就可以隨便用來存資料了麼,而且其他畫素點仍可以儲存正常的影象資料啊!比如上圖的透明背景圖,透明畫素資料的RGB值為0xffffff是因為該圖的背景圖層為白色(或空),那麼反正它也不顯示(透明度100%),乾脆就徵用一個位元組來隱藏資料吧(如B通道位元組,也可以RGB全徵用)。那麼隱藏資料前可以先計算一下圖片的可用容量,比如設定A通道為0且R、G兩通道為0xff則判斷為可用畫素點:
當可用透明畫素點數量大於要隱藏的資料位元組數就可以進行儲存了,直接依次存進去每個可用畫素點的B通道位元組唄,存完後加個結束標誌就行:
直接看一下隱藏效果吧,還是以儲存計算器程式calc.exe(大約900kb)為例:
還行吧,肉眼仍看不出圖片顯示有什麼不同,圖片size看上去也還可以,膨脹不到1MB(差不多計算器程式的size),並且還有不小的提升空間(比如圖片處理掉更多畫素,或者加入其他2個通道儲存)。然後使用神器“Stegsolve.jar”來看看圖片隱藏的計算器吧:
當然之所以能這樣一開始直接就看到資料是因為選取圖片畫素的原點區域正好被掏空(透明區域)可以用來儲存資料,其實根據載體圖片的不同完全可能將位元組資料“隨機化”。最後關於如何獲取具有透明通道格式的可儲存圖片也很簡單,以Photoshop為例,隨便拉張圖片選取無關背景區域直接刪除畫素(摳圖),也可以再加一層透明圖層背景,然後儲存為PNG格式即可。
0x4 工具實現
既然研究都差不多了,不實現工具化總歸有點不完整,示例程式碼就不多做介紹了,寫的很粗糙原本是不打算獻醜的,本著學習分享的理念還是直接扔到Github上僅供參考吧。以下分別是LSB和PNG Alpha通道隱寫工具,均包含加解密兩種功能,以及處理過程相關的提示,可以滿足一般的資訊隱藏需求。
Demo地址: ofollow,noindex" target="_blank">https://github.com/weiyiling/img_store
包含上述直接編譯好的工具可以測試,目前僅面向PNG的ARGB32格式圖片支援良好~
文章封面圖使用此方式將工具隱寫其中,讀者可以用此圖實踐操作。
0x5 總結
通過這次的探索,對影象有了進一步的理解,影象畫素的組織格式多種多樣,本質上是顏色資訊儲存格式的差異,而資訊隱藏的原理卻是異曲同工,都是在於對目標載體的資料格式做分析,尋求可供儲存的“無關”資料空間,只要不影響原始載體的功能“感覺”,應該都是可以操作的。
0x6 參考連結
- 《利用PNG畫素隱藏PE程式碼:分析PNG Dropper新樣本》: https://www.anquanke.com/post/id/166451
- 《影象隱寫之使用PHP隱藏影象中的文字》: https://zhuanlan.kanxue.com/article-4472.htm
- 《RGB格式詳解(三)—RGB畫素格式》: http://blog.51cto.com/7335580/2065296