LSB Steganography

Using Python

Lecturer: 夜猫、土豆、丰嘉

OUTLINE

  • What is steganography?
  • Steganography vs. Cryptography
  • LSB Steganography
  • Practice with Python

What is steganography?

隱寫術 (steganography)

steganos 代表 hidden or covered (隱藏) 的意思。

graph 代表 to write (書寫) 的意思。

一般完整的隱寫系統包含幾個要件:

  • 偽裝文本 (Cover Text) => 用於隱藏訊息的載體
  • 隱密文本 (Stego Text) => 隱藏的訊息
  • 金鑰 (Key) => 用於揭露或隱藏訊息

依現行能用於隱藏訊息的載體,大致可分為五種型式:

  •     Text
  •     Image
  •     Audio
  •     Video
  •     Network

Steganography

vs.

Cryptography

就核心概念來說,兩者的目標一致:

保護訊息不讓第三方知曉

兩者使用完全不同的機制來達到目的

  • 密碼學(cryptography):改變訊息本身為密文,需要金鑰來進行解密
  • 隱寫術(steganography):不改變訊息本身,但隱藏在其他訊息當中
STEGANOGRAPHY CRYPTOGRAPHY
定義 隱藏訊息在存在的通訊中 轉換訊息為無法理解的型式
密文可視性 Never Always
密文資料結構 不會更動 會更動
金鑰 Optional Necessary

LSB Steganography

每張圖像是由 pixels (像素) 所組成,為影像顯示的基本單位。

每個 pixels (像素),儲存的是由 RGB 三原色組成的顏色值。亦即 Red, Green, Blue 組成每張圖像的每一個 pixel (像素)。

LSB Insertion

總共16777216 種顏色
    人類能夠辨識的顏色平均大約1000萬種顏色

更改每個像素LSB的變化以人眼無法辨別

Practice

with

Python


先確認使用者要用何種功能(隱寫or解密)

  • 隱寫
  • 解密
  1. 開圖檔
  2. 將訊息轉換為ASCII的二進位編碼,並加上`1111111111111110`作為結尾
  3. 讀取圖片pixel的RGBA值(紅、綠、藍、透明度)
  4. 將訊息二進位編碼一個bit一個bit寫入R or G or B or A
  5. 將更改後的RGBA編碼重新存檔為圖片


先確認使用者要用何種功能(隱寫or解密)

  • 隱寫
  • 解密
  1. 開圖檔
  2. 讀取pixel的RGBA值
  3. 將R or G or B or A值轉換為二進制,並讀取其最後一個bit,將其寫入二進位字串
  4. 假設該字串最後16碼為`1111111111111110`則代表訊息結束
  5. 停止讀取,將該二進位字串轉換為文字字串


先確認使用者要用何種功能(隱寫or解密)

  • 隱寫
  • 解密
def str2bin(message):
        message_bytes = bytes(message, 'ascii')
        return "".join(["{:08b}".format(x) for x in message_bytes])
def bin2str(binary):
        binary_split = []
        count = 0
        temp = ""
        for i in range(len(binary)):
            count += 1
            temp += binary[i]
            if count == 8:
                binary_split.append(temp)
                count = 0
                temp = ""
        return "".join([chr(int(b, 2)) for b in binary_split])
def encode(num, digit):
    '''
    1. change num to binary.
    2. add digit to last digit of num binary.
    3. change num binary back to decimal, and return it.
    '''
    bin_num = bin(num)
    bin_num = bin_num[:-1] + digit
    return int(bin_num, 2)
def decode(num):
    return bin(num)[-1]
def hide(filename, message):
        img = Image.open(filename)
        binary = str2bin(message) + "1111111111111110"
        if img.mode == 'RGBA':
            datas = img.getdata()
            newData = []
            count = 0
            message_end = False
            for data in datas:
                if not message_end:
                    newpix = []
                    for num in data :
                        if count < len(binary):
                            newpix.append(encode(num, binary[count]))
                            count += 1
                        else:
                            newpix.append(num)
                            message_end = True
                    newData.append(tuple(newpix))
                else:
                    break
            img.putdata(newData)
            img.save(filename, "PNG")
            return "Completed!"
        else:
            return "Incorrect Image Mode, couldn't hide :("
def hide(filename, message):
        img = Image.open(filename)
        binary = str2bin(message) + "1111111111111110"
        if img.mode == 'RGBA':
            datas = img.getdata()
            newData = []
            count = 0
            message_end = False
            for data in datas:
                if not message_end:
                    newpix = []
                    for num in data :
                        if count < len(binary):
                            newpix.append(encode(num, binary[count]))
                            count += 1
                        else:
                            newpix.append(num)
                            message_end = True
                    newData.append(tuple(newpix))
                else:
                    break
            img.putdata(newData)
            img.save(filename, "PNG")
            return "Completed!"
        else:
            return "Incorrect Image Mode, couldn't hide :("
def retr(filename):
        img = Image.open(filename)
        binary = ""

        if img.mode == 'RGBA':
            datas = img.getdata()
            for data in datas:
                for num in data:
                    binary += decode(num)
                    if binary[-16:] == "1111111111111110":
                        print("Seccuss!")
                        return bin2str(binary[:-16])
            return bin2str(binary)
        return "Incorrect Image Mode, couldn't retrieve :("
def main():
    parser = optparse.OptionParser('python lsbsteg.py ' + '-e/-d <target file>')
    parser.add_option('-e', dest = 'hide', type='string', help='target pic path to hide text')
    parser.add_option('-d', dest = 'retr', type='string', help='target pic path to retrieve text')
    (options, args) = parser.parse_args()
    if options.hide != None:
        text = input("Enter a message to hide: ")
        print(hide(options.hide, text))
    elif options.retr != None:
        print(retr(options.retr))
    else:
        print(parser.usage)
        quit()
if __name__ == '__main__':
    main()     

ENCORE

SHORTER VERSION

from PIL import Image
from stegano import lsb
import optparse
parser = optparse.OptionParser('usage %prog ' + '-e/-d <target file>')
parser.add_option('-e', dest = 'hide', type='string', help='target pic path to hide text')
parser.add_option('-d', dest = 'retr', type='string', help='target pic path to retrieve text')
(options, args) = parser.parse_args()
if options.hide != None:
    text = input("Enter a message to hide: ")
    image = Image.open(options.hide)
    image_stego = lsb.hide(image, text)
    image_stego.save(options.hide)
    print("Success")
elif options.retr != None:
    message = lsb.reveal(options.retr)
    print("Complete")
    print(message)
else:
    print(parser.usage)
    quit()
parser = optparse.OptionParser('usage %prog ' + '-e/-d <target file>')
parser.add_option('-e', dest = 'hide', type='string', help='target pic path to hide text')
parser.add_option('-d', dest = 'retr', type='string', help='target pic path to retrieve text')
(options, args) = parser.parse_args()
if options.hide != None:
	text = input("Enter a message to hide: ")
	print(hide(options.hide, text))
elif options.retr != None:
	print(retr(options.retr))
else:
	print(parser.usage)
	quit()
parser = optparse.OptionParser('usage %prog ' + '-e/-d <target file>')
parser.add_option('-e', dest = 'hide', type='string', help='target pic path to hide text')
parser.add_option('-d', dest = 'retr', type='string', help='target pic path to retrieve text')
(options, args) = parser.parse_args()
if options.hide != None:
    text = input("Enter a message to hide: ")

elif options.retr != None:

else:
	print(parser.usage)
	quit()

LSB Steganography Using Python

By Иo1lz

LSB Steganography Using Python

  • 483