2026数字中国创新大赛初赛wp之图片隐写一
缘起
我最喜欢的就是杂项了,很有意思,尤其是隐写,可是这道题确实有点料,浪费不少时间,不过还好,最后还是做出来了。
题目

题目附件:
分析
题目看起来很简单:
- 虽然图片很多,但是实际就5张,就算每个都试试也不是问题
- 已经明确说了是lsb,而且是最后一位,范围以及限制死了
初始
lsb隐写,必须先上神器zsteg啊:
先试试简单的:
bash
zsteg --lsb 1.png
没结果,再试试所有的:
bash
zsteg -a --lsb 1.png
也没戏:

其他几个小工具也试过,都没记过,再读一遍题目:
- 图片是lsb隐写
- 最后一位
那就只有一种可能了,就是并不是顺序存储在最后一位的,而是间隔的,但是这个范围太大了,只能先按照题目内容缩小一下尝试了:
- 假设是平均间隔,前面可以空几位
- 假设他给的答案例子,格式是一样的,就是32位的(题目答案例子)
然后,还想先找到是哪张图片,但是我存储下来的图片没有找到任何信息,包括exif,也许我另存的时候丢失了,由于docx其实是一个压缩文件,所以直接改后缀名解压:
然后找图片,目录是word->media

然后发现问题了,只有第5张图片被更新过,其他的都是老照片,所以猜猜,第5张图片。
然后就准备上代码爆破了,有好几个地方:
- 组合1: 颜色,就是可能是单色(r,g,b),或者三色都有(rgb)
- 组合2: 排列:lsb或者msb
- 从第几位开始存储
- 每隔多少位存储
第一第二个数据只有几位,每个试一下就行,只有3,4有太多种可能,必须爆破了,先上代码:
python
img = Image.open("5.png")
rgb = img.convert("RGB").tobytes()
G = rgb[1::3]
L = 32 #1
for start in range(0,8): #2
for step in range(1,(len(G)//(L*8))+1): #3
bits = [G[start + i * step] & 1 for i in range(L * 8)]
result = []
for i in range(0, len(bits), 8):
b=0
for bit in bits[i:i+8]:
b =(b<<1) | bit # msb 4
result.append(b)
decoded_string = bytes(result).decode(errors='ignore')
if decoded_string.isprintable(): # 5
print(decoded_string)
先跑一下看结果:


万事大吉!
然后简单解释一下代码:
- 前面分析的结果设置,G通道是试到第二个试出来的
- 第一层循环,假设隐藏数据开头从第几位开始,先爆一个字节试试
- 第二层循环,平均间隔:总长度除以答案长度,就表示最长间隔,所以从1到这个数,爆破
- 然后按照8个bit组成一个字节,这里有2中可能,lsb或者msb,就是高低位,这个是试出来的,2次就行
- 答案应该是可打印的,所以加了个过滤,方便人眼看,其实可以按照答案模板更多的过滤一些,不过就此也够一眼出结果了
小结
还是第一次遇到这种lsb,好在题目提示够多,对lsb的了解也还可以,总算做出来了,不过耗时太多了,可惜!
初赛其他题目: