STEGA-LSB

LSB隐写

什么是LSB

甚么是LSB隐写(最低有效位隐写)捏~
众所周知,图片像素的颜色都由红绿蓝三原色组成,每个像素使用24位,其中每一种原色占8位(即0x000xFF,有256种强度不同的颜色),所以三原色共能生成256的三次方种颜色,怎么想都很多吧!而人眼不足以分辨所有的256的三次方(16777216)种颜色,因此改动其二进制的最低位时,颜色的变化微乎其微,人眼难以察觉。也就是说…每个像素都可以携带3比特的信息,而不被人眼发现异常,借用这一现象,我们就能在图片中进行内容的隐藏,比如一段话,一些文件,或者另一张图片——

音频中的LSB

当然,LSB不止在图片中存在,音频中也存在LSB隐写。如WAV文件中不包括构成数字图像的像素,而是线性脉冲编码调制(LPCM)比特流,以均匀间隔编码模拟音频波形的采样。该比特流通常是未压缩的,但支持无损压缩。与图片的像素类似,对每个采样的最低限度(LSB)调整是无声的,即人耳很难察觉,可以用来逐位嵌入隐藏消息。

工具?

面对经过LSB隐写处理的图片/音频,如何提取其中信息呢?

  • 其一可以使用诸如stegsolve等工具查看目标文件(推荐~)
  • 其二可以根据上述原理撰写脚本

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from PIL import Image
import sys

def toasc(strr):
return int(strr, 2) #str1为所要提取的信息的长度(根据需要修改),str2为加密载体图片的路径,str3为提取文件的保存路径

def decode(str1,str2,str3):
b=""
im = Image.open(str2)
lenth = int(str1)*8
width,height = im.size[0],im.size[1]
count = 0
for h in range(height):
for w in range(width):
#获得(w,h)点像素的值
pixel = im.getpixel((w, h))
#此处余3,依次从R、G、B三个颜色通道获得最低位的隐藏信息
if count%3==0:
count+=1
b=b+str((mod(int(pixel[0]),2)))
if count ==lenth:
break
if count%3==1:
count+=1
b=b+str((mod(int(pixel[1]),2)))
if count ==lenth:
break
if count%3==2:
count+=1
b=b+str((mod(int(pixel[2]),2)))
if count ==lenth:
break
if count == lenth:
break

with open(str3,"w",encoding='utf-8') as f:
for i in range(0,len(b),8):
#以每8位为一组二进制,转换为十进制
stra = toasc(b[i:i+8])
#将转换后的十进制数视为ascii码,再转换为字符串写入到文件中
#print((stra))
f.write(chr(stra))
print("sussess")

def plus(string):
#Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。
return string.zfill(8)

def get_key(strr):
#获取要隐藏的文件内容
with open(strr,"rb") as f:
s = f.read()
string=""
for i in range(len(s)):
#逐个字节将要隐藏的文件内容转换为二进制,并拼接起来
#1.先用ord()函数将s的内容逐个转换为ascii码
#2.使用bin()函数将十进制的ascii码转换为二进制
#3.由于bin()函数转换二进制后,二进制字符串的前面会有"0b"来表示这个字符串是二进制形式,所以用replace()替换为空
#4.又由于ascii码转换二进制后是七位,而正常情况下每个字符由8位二进制组成,所以使用自定义函数plus将其填充为8位
string=string+""+plus(bin(s[i]).replace('0b',''))
#print(string)
return string

def mod(x,y):
return x%y
#str1为载体图片路径,str2为隐写文件,str3为加密图片保存的路径
def encode(str1,str2,str3):
im = Image.open(str1)
#获取图片的宽和高
width,height= im.size[0],im.size[1]
print("width:"+str(width))
print("height:"+str(height))
count = 0
#获取需要隐藏的信息
key = get_key(str2)
keylen = len(key)
for h in range(height):
for w in range(width):
pixel = im.getpixel((w,h))
a=pixel[0]
b=pixel[1]
c=pixel[2]
if count == keylen:
break
#下面的操作是将信息隐藏进去
#分别将每个像素点的RGB值余2,这样可以去掉最低位的值
#再从需要隐藏的信息中取出一位,转换为整型
#两值相加,就把信息隐藏起来了
a= a-mod(a,2)+int(key[count])
count+=1
if count == keylen:
im.putpixel((w,h),(a,b,c))
break
b =b-mod(b,2)+int(key[count])
count+=1
if count == keylen:
im.putpixel((w,h),(a,b,c))
break
c= c-mod(c,2)+int(key[count])
count+=1
if count == keylen:
im.putpixel((w,h),(a,b,c))
break
if count % 3 == 0:
im.putpixel((w,h),(a,b,c))
im.save(str3)

if __name__ == '__main__':
if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) < 2:
print ('Usage: python test.py <cmd> [arg...] [opts...]')
print (' cmds:')
print (' encode image + flag -> image(encoded)')
print (' decode length + image(encoded) -> flag')
sys.exit(1)
cmd = sys.argv[1]
if cmd != 'encode' and cmd != 'decode':
print('wrong input')
sys.exit(1)
str1 = sys.argv[2]
str2 = sys.argv[3]
str3 = sys.argv[4]
if cmd != 'encode' and cmd != 'decode':
print ('Wrong cmd %s' % cmd)
sys.exit(1)
elif cmd=='encode':
encode(str1,str2,str3)
elif cmd=='decode':
decode(str1,str2,str3)
#(该jio本仅可处理图像中文本的LSB隐写)

唔——基础的数字隐写术LSB的介绍暂时就到此为止啦!所以,以后想藏些有趣的东西,尽情使用这种方式吧!