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) => 用於揭露或隱藏訊息
![](https://www.researchgate.net/profile/Qiangfu_Zhao/publication/221910064/figure/fig3/AS:305233390522402@1449784719527/The-block-diagram-of-steganography-Though-the-steganography-is-able-to-hide-messages-in.png)
依現行能用於隱藏訊息的載體,大致可分為五種型式:
- Text
- Image
- Audio
- Video
- Network
Steganography
vs.
Cryptography
就核心概念來說,兩者的目標一致:
保護訊息不讓第三方知曉
兩者使用完全不同的機制來達到目的
- 密碼學(cryptography):改變訊息本身為密文,需要金鑰來進行解密
- 隱寫術(steganography):不改變訊息本身,但隱藏在其他訊息當中
STEGANOGRAPHY | CRYPTOGRAPHY | |
---|---|---|
定義 | 隱藏訊息在存在的通訊中 | 轉換訊息為無法理解的型式 |
密文可視性 | Never | Always |
密文資料結構 | 不會更動 | 會更動 |
金鑰 | Optional | Necessary |
LSB Steganography
![](https://i.imgur.com/cX87LqF.png)
每張圖像是由 pixels (像素) 所組成,為影像顯示的基本單位。
每個 pixels (像素),儲存的是由 RGB 三原色組成的顏色值。亦即 Red, Green, Blue 組成每張圖像的每一個 pixel (像素)。
![](https://i.imgur.com/ArSWwYo.png)
![](https://i.imgur.com/jARF2ns.png)
![](https://i.imgur.com/ArSWwYo.png)
![](https://i.imgur.com/5FoPjaE.png)
![](https://image.3001.net/images/20180225/15195435137985.png)
LSB Insertion
總共16777216 種顏色
人類能夠辨識的顏色平均大約1000萬種顏色
更改每個像素LSB的變化以人眼無法辨別
Practice
with
Python
先確認使用者要用何種功能(隱寫or解密)
- 隱寫
- 解密
- 開圖檔
- 將訊息轉換為ASCII的二進位編碼,並加上`1111111111111110`作為結尾
- 讀取圖片pixel的RGBA值(紅、綠、藍、透明度)
- 將訊息二進位編碼一個bit一個bit寫入R or G or B or A
- 將更改後的RGBA編碼重新存檔為圖片
先確認使用者要用何種功能(隱寫or解密)
- 隱寫
- 解密
- 開圖檔
- 讀取pixel的RGBA值
- 將R or G or B or A值轉換為二進制,並讀取其最後一個bit,將其寫入二進位字串
- 假設該字串最後16碼為`1111111111111110`則代表訊息結束
- 停止讀取,將該二進位字串轉換為文字字串
先確認使用者要用何種功能(隱寫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