Misc知识点整理——隐写篇
文字相关
零宽字符(Unicode)隐写

零宽字符隐写是利用 Unicode 字符集中不可见(宽度为 0)的控制字符,将秘密信息编码后插入到普通文本中。

一般效果就是肉眼看到的文本没有任何异常(字数、排版均无变化),但在内存或文件字节流中包含了隐藏数据。

*事实上在题目中,一些莫名其妙的口口(空格),大概率就是零宽字符

这个时候也可以在预览那边看到异常

在虚拟机用vim打开,普通零宽字符是直接可见的

常见 Unicode 零宽字符

  • U+200B (Zero Width Space)
  • U+200C (Zero Width Non-Joiner)
  • U+200D (Zero Width Joiner)
  • U+200E / U+200F (方向标记 LRM / RLM)
  • U+FEFF (BOM / 零宽不换行)

解题时优先使用CyberChef或经典零宽解密网站上试,如果能直接出,说明是标准算法。

如果不能,说明可能是特定网站/Python/Node.js 库出的题,算私密/非标算法,这个时候就得找出题人是否提供原工具。如果没有,那先分析它用了哪几种零宽字符,再自己写 Python 脚本去适配它的映射规则与分组逻辑。

异体字选择器(Variation Selectors,简称 VS)隐写

这种隐写是零宽隐写的进阶形态。它的核心优势在于字符种类刚好有 16 个(U+FE00U+FE0F),可以完美映射十六进制的 0F。这使得它无需像二进制隐写那样把 1 个字节拆分成 8 个 bit,而是 1 个 VS 字符就能直接表示 4 个 bit(半个字节),隐藏效率和密度比普通零宽高出一倍。

*这种时候连vim打开文本也看不见有关零宽隐写的特殊标记,隐蔽性极高

不过用工具(如010)查看底层十六进制字节,能看到线索。

*这个里面大量的ef b8 8x 格式的 3 字节序列就是典型的VS隐写

这种时候就只能拿到含有 VS 隐写的文本后,用脚本自动提取 U+FE00 ~ U+FE0F 范围内的字符,通过偏移量转换回 Hex 字符串来解密了。

Stegsnow(SNOW 算法)

stegsnow是 CTF Misc 隐写中历史非常悠久且极其经典的一种文本行末隐写技术。

它完全不依赖任何 Unicode 零宽字符,而是利用了纯文本文件在每一行末尾放置的 空格 Space (0x20) 和 制表符 Tab (0x09) 来隐藏信息。由于大多数文本编辑器和命令行工具在渲染时都会自动忽略行末的空白字符,因此隐蔽性极高。

*不过隐写内容特别多的时候,文末经常会有大段空白块,一个ctrl+A就会非常显眼了

snow隐写一般使用特定工具,常见解密方式有:

  • 无密码直接解密(最常见
stegsnow -C file.txt
  • 带密码的解密
stegsnow -C -p "your_password" file.txt

图片相关
附加数据类型

附加数据类型,又叫附加流隐写,它的原理非常简单:任何图片文件格式都有固定的文件头和文件尾。图片查看器在渲染图片时,只要读取到了文件尾标志(如 JPEG 的 FF D9 或 PNG 的 49 45 4E 44 AE 42 60 82),就会停止解析并展示图片。

出题者正是利用了这一点,直接将加密文本、压缩包,甚至另一个图片/程序拼接在文件尾标志之后。对于图片查看器来说,后半部分数据是不可见的;但使用十六进制编辑器或提取工具时,后半部分数据往往能被直观看见。

*一些常见图片格式

图片格式常见后缀文件头(Hex 签名)文件尾(Hex 签名)特点与常见用途
JPG/JPEG.jpg/.jpegFF D8 FFFF D9广泛用于摄影和网页,采用有损压缩。
PNG.png89 50 4E 47 0D 0A 1A 0A49 45 4E 44 AE 42 60 82无损压缩,支持透明通道。末尾包含 IEND 块。
GIF.gif47 49 46 38 37 61 (GIF87a)
47 49 46 38 39 61 (GIF89a)
00 3B支持动图和透明度,最多 256 色。
BMP.bmp42 4D (ASCII: BM)无固定尾部微软未压缩的位图格式,文件体体积通常很大。

补:为什么有些格式“没有固定文件尾”?

在做隐写分析和文件提取时,需要注意两类不同的文件结构设计:

  • 显式结束标记型(如 PNG、JPEG、GIF): 这些格式在设计时加入了明确的“数据结束符”(如 JPEG 的 FF D9 或 PNG 的 IEND 块)。图片解析器读到这个特定字节序列后就会停止。因此,在它们后面追加数据最容易实现隐写。
  • 长度/偏移量声明型(如 BMP、WebP、TIFF): 这类格式通常没有固定的文件尾标志,而是把文件总大小或数据块偏移量写在文件头中。

因此可以在十六进制编辑器(如010 Editor)中,查看图片的标准文件头/尾中,有没有多出非00数据,从而判断该图片是否存在附加数据型隐写。

常见文件格式见(这里存放一个皇帝的新链接)处整理

发现附加数据后,一般提取方法有:

  1. 快捷提取方法

*适合附加内容为 ZIP/RAR 等常见压缩包的情况

  • 改扩展名直接解压: 将图片后缀(如 .png.jpg)直接重命名为 .zip.rar,然后右键使用 WinRAR 或 7-Zip 打开。解压软件会忽略前面的图片头,直接读取底部的压缩目录表。
  • 右键强制打开: 不必改后缀,直接打开 7-Zip 等压缩软件,将图片拖拽到软件界面中,若含有压缩包数据即可直接看到内部文件。

  1. 十六进制编辑器手动提取

*适合大多数的附加文件提取

  • Binwalk

最强大且最常用的二进制文件分析提取工具,Linux/Kali 自带。

基础命令如下:

# 1. 检查图片尾部隐藏了哪些数据及对应的偏移量
binwalk target.png

# 2. 自动提取并分离所有隐藏的文件(这里默认输出到 _target.png.extracted 目录)
binwalk -e target.png

# 3. 将提取出的数据放到指定的文件夹 my_result 中
binwalk -e target.png -C my_result
  • Foremost

基于文件头尾特征的数据恢复工具,适合提取被损坏或连续嵌套的数据。

基础命令如下:

# 1. 提取并分离指定文件
foremost -i target.png

# 2. 自动提取并分离到指定文件夹中
foremost -i target.png -o my_result
  • dd命令

Linux 系统中最古老且功能极其强大的底层数据复制与按字节切割工具,极为精准。常用于数据恢复、磁盘镜像、CTF 隐写和固件分析。

dd 的语法结构比较特殊,它不使用常见的 -f 或 –file 参数,而是采用 key=value 的形式:

dd if=输入文件 of=输出文件 bs=块大小 skip=跳过的块数 count=复制的块数

# 例如提取从某个 Offset 开始直到文件末尾的所有数据:
dd if=stego.png of=hidden.zip bs=1 skip=10000

# 只截取文件中特定区间的字节数据(如 10000 到 15000 字节):
dd if=stego.png of=hidden.zip bs=1 skip=10000 count=5000

尺寸修改

又称宽高隐写,常见于 PNG 格式,是一种利用图像文件头部的尺寸声明来裁剪画面展示的技巧。

它的核心原理是:修改图片文件中记录宽高的二进制数值,使图片查看器渲染时只展示某一部分,而将隐藏了 Flag 或关键信息的剩余部分裁剪在画面之外。

这就不得不提起png图片的结构了。PNG 图片的前 8 字节为文件魔数,随后跟随的第一个数据块固定为 IHDR(Image Header,文件头数据块)。

*IHDR 的固定结构共 13 字节:图像宽度 (4 字节),图像高度 (4 字节),其它控制字节 (5字节)

PNG 的每个数据块末尾都有 4 字节的 CRC32(循环冗余校验码),用于校验该数据块是否被非法篡改。IHDR 数据块的 CRC 是由“数据块类型码(IHDR)+ 宽高 + 图像属性”共同计算得出的。

当隐写者只修改了高度而没有重新计算并修改 CRC 时,会导致 “声明数据与 CRC 校验码不匹配”,010 Editor端通常报错在开屏:

或者最底下有此类报错:

Linux 下显示的异常通常为: 使用 pngcheck 检测或打不开图片提示 IHDR CRC error。

修复宽高常用方法有:

  • 手动修改Hex(010 Editor / WinHex)

手搓修改通常用于高度错误的图片。

原理:PNG 图片的数据在文件内部(IDAT 块中)是从上到下、按行压缩存储的。 修改 IHDR 中的高度,本质上只是改变了图片查看器的“裁剪窗口”。

如果把高度从 200 改成 500,而图片里实际上存了 500 行的像素数据,系统就会把下面被隐藏的 300 行重新渲染出来。

如果图片里本来就只有 200 行的数据,就算强制把高度改成 9999(如 00 00 27 0F),图片查看器也不会凭空变出新的画面。大部分查看器会直接报错(提示 IDAT 数据不完整 / Premature end of file),或者在下方显示一片纯黑/透明的空白。

在 010 Editor或其他十六进制编辑器中打开图片,找到 IHDR 标记,跳过其后的 4 字节(宽度),定位到高度字段的4字节。然后直接拉大高度, 将高度直接改成与宽度一致,或改为较大数值,保存后重新打开,看看下方是否出现了原本被隐藏的内容。

  • 爆破真实宽高(Python CRC32 爆破)

宽度错误时,不能随便手动修改宽度,因为这是由 PNG 的底层解压逻辑决定的:

  1. 像素对齐机制: PNG 图像数据在压缩时,是严格按照 宽度 × 每像素字节数 + 1 (Filter Byte) 来切分每一行数据的。
  2. 解码错位(Scanline Mismatch):
    • 假设原图真正宽度是 100 像素,解压算法会认为“每读完 100 个像素的数据,就是一行”。
    • 如果强行把宽度改成了 200,解码器就会强行去读 200 个像素的数据当作第一行。这会导致第二行的数据被当成第一行后半段,整个图像的每一行全都“错位”,引发解压流算法崩溃,最终导致图片报错拒绝加载或渲染成一片扭曲的噪点。

这个时候多用爆破方式,通过图片中余留的正确CRC32校验码试出正确的原始宽高。

这里塞一个一把梭脚本:

import zlib
import struct
import argparse
import itertools

parser = argparse.ArgumentParser()
parser.add_argument("-f", type=str, default=None, required=True,
                    help="输入同级目录下图片的名称")
args = parser.parse_args()

bin_data = open(args.f, 'rb').read()
crc32key = zlib.crc32(bin_data[12:29])
original_crc32 = int(bin_data[29:33].hex(), 16)

if crc32key == original_crc32:
    print('宽高没有问题!')
else:
    input_ = input("宽高被改了, 是否CRC爆破宽高? (Y/n):")
    if input_ not in ["Y", "y", ""]:
        exit()
    else:
        for i, j in itertools.product(range(4095),
                                      range(4095)):
            data = bin_data[12:16] + struct.pack('>i', i) + struct.pack('>i', j) + bin_data[24:29]
            crc32 = zlib.crc32(data)
            if (crc32 == original_crc32):
                print(f"\nCRC32: {hex(original_crc32)}")
                print(f"宽度: {i}, hex: {hex(i)}")
                print(f"高度: {j}, hex: {hex(j)}")
                exit(0)

需要注意的是,CRC32类似md5,是一种不可逆的单向散列/校验算法,无法通过数学公式直接从校验码反推出原始数据。

但在 PNG 图片的 IHDR 块中,参与 CRC32 计算的数据长约 13 个字节(包含:块标记 IHDR + 宽度 + 高度 + 色深/颜色类型等固定参数)。在这个数据包里:

未知量: 仅仅是被篡改的宽度(4 字节)或高度(4 字节)。

已知量: 块标记 IHDR、色深、颜色类型、以及文件末尾那个原作者留下的正确 CRC32 目标值。

虽然宽和高理论上可以是任意数字,但在实际应用中,图片的尺寸范围极其有限(通常在 14000 像素之间):

  • 如果只爆破高度(假定宽度未变):高度范围 1 ~4000,计算机只需尝试 4000 次。
  • 如果宽高同时爆破:4000* 4000 = 16,000,000(一千六百万次)。

对于现代 CPU 来说,计算一次 CRC32 只需要几纳秒。穷举一千六百万次组合,Python 脚本通常只需要 1 ~ 2 秒钟 就能跑完。

*大人,时代变了。

不过毕竟是爆破,了解爆破的底层逻辑后,就能明白它的局限性:

  • 目标 CRC32 被抹除/篡改: 如果出题人修改宽高的同时,把文件末尾的 4 字节 CRC 校验码也一起改成了 00 00 00 00,此时没有了“对比参考的目标”,CRC 爆破就会失效(只能通过分析图像压缩数据流解压推算)。
  • 搜索范围过大: 如果图片尺寸非常巨大(如几万像素),或者未知参数过多,穷举次数呈指数级爆发,爆破时间就会大幅拉长。

通道

图层/通道隐写是图像 CTF 隐写题中最基础、最直观的视觉类考点。其核心逻辑是利用人类肉眼对极低对比度或微小色彩差异不敏感的生理弱点,将 Flag、二维码或提示信息嵌入到特定的色彩通道、透明度通道或位平面中。

核心原理如下:

1. 颜色通道(RGB Color Channels)极低对比度隐写

  • 原理:RGB 图像由红(R)、绿(G)、蓝(B)三个独立通道叠加而成,每个通道取值范围为 0~255
  • 隐写方式:出题人将 Flag 或图像信息仅绘制在单一通道(如只在 R 通道),并将背景色设为 0,前景色设为 12
  • 视觉表现:在常规图片查看器中,三个通道混合后,全图看起来就是纯黑或极其接近纯色,肉眼完全无法察觉。

2. Alpha 透明度通道(A Channel)隐写

  • 原理:PNG 等格式除了 RGB 外,还包含第 4 个通道——Alpha 通道(即 RGBA),控制像素的透明度(0 表示完全透明,255 表示不透明)。
  • 隐写方式:
    • 透明度微调:将文字绘制在 Alpha 通道中,使背景为 Alpha=255,前景文字为 Alpha=254。
    • 全透明区域藏画:将 RGB 颜色画上 Flag,但把对应区域的 Alpha 强行设置为 0(完全透明)。在图片查看器中,因为完全透明,RGB 图像被遮盖;但通过工具提取 Alpha 通道或将透明度强行拉满,RGB 颜色就会显现。

3. 位平面隐写(Bit Plane Steganography / LSB 视觉化)

  • 原理:每个颜色通道的 8 个 Bit 中,高位(Bit 7)决定了绝大部分色彩,而低位(Bit 0,即 LSB)对色彩的影响极微小。
  • 视觉展现:当出题人将 Flag 绘制在某个通道的 Bit 0 上时,常规查看下毫无痕迹,但如果单独提取并放大(Binarize)该 Bit 平面,信息就会直接呈现在画面上。

常用分析与解题工具:

1.StegSolve

StegSolve是Java 开发的经典图像分析工具,专治各类通道与位平面隐写。

这个妙妙小工具可用于位平面隐写、单颜色通道极低对比度隐写、Alpha 透明度通道隐写、双图对比 / 逻辑运算隐写、GIF / 多帧图像隐藏等,详细使用方法可以见此篇https://www.cnblogs.com/cat47/p/11483478.html。非常详细,孩子爱看喵~(

另外补充文章里没有的几点:

  • 双图对比 / 逻辑运算隐写(Image Combiner / Frame XOR)

当手头有两张高度相似的图片(如原图 vs 隐写图,或者两部分拼接图)时,可以试试StegSolve 的 Analyse -> Image Combiner 功能。

支持的隐写算法:

  1. XOR(异或):两图对应像素做异或运算,完全相同的地方变黑,不同的隐藏区域亮起。
  2. SUB(相减):用隐写图减去原图,直接提取出增加的文字水印。
  3. ADD / OR / AND:其它逻辑位的叠加提取。

  • GIF / 多帧图像隐藏(Frame Browser)

原理:GIF 动图由多帧静态图拼接而成。出题人会将 Flag 拆分成单个字符,分别隐藏在动图的某几帧中,或者把闪烁极快、肉眼抓拍不到的一帧作为隐藏帧。

StegSolve 破解方式:点击 Analyse -> Frame Browser,可以逐帧分解 GIF,像看幻灯片一样一帧一帧提取被隐藏的画幅。

2.Adobe Photoshop (PS)

其实PS比较少用,它的定位是通过专业的图层、通道与色阶调节,进行深度视觉提取。

常用提取技巧

  • 色相/饱和度 / 反相(Ctrl + I:用于将接近透明或接近白色的暗纹翻转显示。
  • 通道面板(Channels):分别点击切换 R、G、B、Alpha 通道,单独观察隐藏信息。
  • 色阶(Levels, Ctrl + L)/ 曲线(Curves, Ctrl + M:强行拉高对比度。将输入色阶的滑块极度向内靠拢,将微小色差(如 01 的区别)放大为极其明显的黑白对比(0255)。

3.Python (PIL / OpenCV) 自动化处理

对于批量的通道分析或复杂的通道位运算,直接编写 Python 脚本提取最为快速,这里塞一个简单的提取脚本,适用于暴露 Alpha 通道隐写或提取 R 通道的 LSB(最低有效位)隐写(r_bit0):

from PIL import Image

# 打开 RGBA 图片
img = Image.open("stego.png").convert("RGBA")
r, g, b, a = img.split()

# 示例 1: 保存 Alpha 通道,暴露透明度隐写
a.save("alpha_channel.png")

# 示例 2: 提取 R 通道的最低有效位 (Bit 0)
r_data = r.getdata()
# 将 Bit 0 的 0 或 1 强行放大为 0 或 255 像素值
new_r_data = [(pixel & 1) * 255 for pixel in r_data]
r_bit0_img = Image.new("L", img.size)
r_bit0_img.putdata(new_r_data)
r_bit0_img.save("r_bit0.png")

*这个并非通用脚本,具体问题要具体分析喵(

exif

其实exif属于在图片中储存信息与数据,严格意义上算不上隐写术,不过可以用把一些隐藏的信息写入EXIF信息。由于EXIF信息可以被视为图像的一部分,因此这种方法可以被认为是一种隐写术(确信)。

  • 一些核心知识点与隐藏位置:

1. 经典元数据属性字段隐藏

出题人通过修改图片属性中的标准文本字段来存放隐写数据:

  • 常见字段:
    • UserComment(用户注释):最常用的隐藏位置,通常包含 Base64 编码的 Flag 或解密密钥。
    • ImageDescription(图像描述)
    • Artist / XPAuthor(作者)
    • Copyright(版权)
    • Software(软件)

2. GPS 地理坐标隐写(GPS Metadata)

出题人将 Flag 或提示信息隐藏在 GPS 坐标信息中:

  • 形式 A(经纬度转化):将经纬度的数值(度、分、秒)转换为 ASCII 码、Hex 16 进制或特定数字代换。
  • 形式 B(真实地点查找):GPS 坐标对应现实中的某个地点,地点的拼音或英文即为解密密码或 Flag 本身。

3. 缩略图隐写(EXIF Thumbnail / ExifTool)

EXIF 结构中允许内嵌一张极小的预览缩略图(Thumbnail):

原理:主图数据和缩略图数据在文件内部是分离存储的。

隐写方式:主图与缩略图使用不同的图像。你在查看器里看到的是主图,但剥离出 EXIF 缩略图后,缩略图上可能直接写着 Flag。

  • 分析与提取工具

1. 图形化工具 / 操作系统原生查看

  • Windows 属性:右键图片 $\rightarrow$ 属性 $\rightarrow$ 详细信息,可查看基础的“作者、标题、备注”等字段。
  • 010 Editor / Hex Editor:定位以 Exif (Hex: 45 78 69 66) 开头的块,直观观察其后的文本字符串。

2. ExifTool(最常用)

ExifTool 是处理元数据最强大的工具(Linux/Windows 均可用),基础命令如下:

# 1. 查看图片的所有 EXIF 元数据(包括隐藏/非标准字段)
exiftool stego.jpg

# 2. 搜索包含 "flag" 或 "comment" 关键字的字段
exiftool stego.jpg | grep -i "flag"

# 3. 提取 EXIF 中内嵌的缩略图
exiftool -b -ThumbnailImage stego.jpg > thumbnail.jpg

部分信息如下图,可以看到信息还是蛮多的,写入和读取都很直观。

3. Python 脚本提取 (PIL / exifread)

在自动化解题脚本中,也可以使用 Python 提取 EXIF 数据:

from PIL import Image
from PIL.ExifTags import TAGS

img = Image.open("stego.jpg")
exif_data = img._getexif()

if exif_data:
    for tag_id, value in exif_data.items():
        tag_name = TAGS.get(tag_id, tag_id)
        print(f"{tag_name}: {value}")

*想看什么自己搓(bushi

双图对比

双图对比隐写是 CTF 隐写题中的经典考点。

核心逻辑:出题人提供两张肉眼看起来完全一致(或极其相似)的图片,其中一张是原图,另一张是嵌入了 Flag 或加密数据的数据图;或者两张图各自存放了一部分隐藏数据。

解题的关键在于通过数学与逻辑运算,消除两图相同的背景干扰,强行提取出微小差异。

  • 核心算法与运算逻辑

双图隐写本质上是逐像素对两张图对应的色彩数值(RGB / Alpha)进行二进制或代数运算。

运算方式逻辑公式适用于什么隐写场景提取效果 / 现象
异或 (XOR)$P_{\text{result}} = P_A \oplus P_B$最常用。两图仅在 LSB 或特定像素有微小差异(如 0 与 1)。相同像素变为 0(纯黑),不同像素变亮(亮起 Flag 或二维码)。
减法 (SUB)$P_{\text{result}} = \lvert P_A – P_B \rvert$水印添加、图像叠加、亮度做微小修改(如像素值 $+1$)。完全一致的地方为 0(黑),差异极小处可通过乘以系数强行拉高对比度显影。
逻辑与 (AND) / 或 (OR)$P_A \ \& \ P_B$ / $P_A \mid P_B$多通道分层隐写,两图各自存放了一半的掩码(Mask)或二值图。用于提取重叠区域或特定遮罩覆盖下的图像。
盲水印 (Blind Watermark)频域变换 (DWT / DFT)较高级的题目。肉眼及像素级 XOR/SUB 均无效,水印被藏在傅里叶变换的频域中。需要通过傅里叶变换(FFT)或离散小波变换(DWT)反向解析频域频谱图。
  • 核心解题工具

1. StegSolve(最简单快速)

操作步骤:

  • 用 StegSolve 打开第一张图 A.png
  • 点击顶部菜单 Analyse -> Image Combiner
  • 选择第二张图 B.png
  • 点击下方箭头切换运算模式(XORSUBADDANDOR 等),并在不同颜色通道(Red/Green/Blue Plane)间翻页,观察是否有 Flag 亮起。

2. Blind Watermark 脚本(针对盲水印)

*事实上也可以随波逐流一把梭(

代表工具: bwmforpy3.py、blind_watermark

bwmforpy3.py基础命令如下:

python bwmforpy3.py decode pic1.png pic2.png flag.png

其中:

python bwmforpy3.py:运行基于 Python 3 重写的盲水印提取脚本(原版 bwm.py 早期多用于 Python 2)。

decode:脚本的操作模式指令,表示解码/提取水印。

pic1.png:原图,即没有藏 Flag 的干净图片。

pic2.png:含水印图,即嵌入了 Flag/二维码的隐写图片。

flag.png:输出结果图,脚本通过对比两图频域数据后,提取并渲染出来的最终 Flag 水印图片。

blind_watermark基础命令如下:

  • 提取文本水印(无需原图):
blind_watermark --extract --img stego.png --wm_len 25

*单图提取文本时,必须指定 --wm_len(水印字符串的字符长度),否则无法正确对齐频域系数。

  • 提取图片水印:
blind_watermark --extract --img stego.png --out_wm extracted_flag.png --wm_shape 100,100

blind_watermark:调用安装好的 blind_watermark Python 命令行工具。

--extract:执行提取/解密模式。

--img stego.png:指定输入的含水印图片文件。

--out_wm extracted_flag.png:指定提取出来的水印图片保存路径,工具会将还原出的 Flag 图片/二维码保存为 extracted_flag.png

--wm_shape 100,100:告诉提取程序隐藏的水印图片的像素尺寸为高 100px、宽 100px(需要按照实际尺寸修改)。

*当然,如图其他隐写一样,双图对比也可以脚本解题,这里就不放了()

音频相关
MP3 算法与编码隐写

这个属于基础解密,一般比较简单,使用命令行工具 MP3StegoDecode.exe 提取:

Decode.exe -X -P <密码> input.mp3

如果解密成功,会生成解密后的波形文件(input.mp3.pcm)以及隐藏的秘密文件(默认名通常为 input.mp3.txt)。

*若题目未给密码,可尝试空密码 -P "",或者结合字典使用 Python 脚本暴力破解。

频谱图隐写

这是一种将图像或文字像素信息映射到音频频域的视觉化隐写技术。正常听音频时可能只会听到刺耳的噪音、滑音或特定频率的蜂鸣声,但在时频分析工具(如 Audacity、Sonic Visualiser)中切换到频谱图视图时,就能直接观察到可视化的图案或 Flag。

理解频谱图隐写,关键于掌握音频从波形到频谱的转换过程:

  • 时频二元映射(Time-Frequency Mapping)
    • 横轴(时间轴 Time):对应图像的宽度(X 轴)。音频在第 $t$ 秒的时刻对应图像第 $X$ 列像素。
    • 纵轴(频率轴 Frequency):对应图像的高度(Y 轴)。低频在下,高频在上,对应图像第 $Y$ 行像素。
    • 颜色/亮度(能量/振幅 Intensity/Amplitude):对应图像的像素灰度或亮度。像素越亮,意味着该时刻该频率分量的振幅/能量越大。
  • 短时傅里叶变换(STFT)与 FFT
    • 计算机通过连续施加快速傅里叶变换(FFT)将连续的时域波形切割成小时间窗口,并计算出每个窗口内的频率分布。
  • 图像转音频算法(如 Coagula / Virtual ANS / Photosounder)
    • 生成此类音频时,工具会将图像的每一列像素拆解为多个不同频率的正弦波(Sine Wave),像素点越亮,对应频率正弦波的振幅就越大;把这些正弦波叠加在一起(正弦波合成),就生成了一段“听起来像噪声但频谱上是图案”的音频。

一般使用工具为Audacity 或 Sonic Visualiser ,注意直接打开音频可能看不清图像,能够进行调整的关键参数有:

FFT 窗口大小(Window Size / FFT Size)

  • 时间分辨率 vs. 频率分辨率的权衡
    • FFT Size 越大(如 2048, 4096):频率分辨率越高,纵向(Y 轴)字迹或图像越清晰,但横向可能出现拖尾。
    • FFT Size 越小(如 256, 512):时间分辨率越高,横向(X 轴)轮廓更清晰。
    • 经验技巧:如果文字在纵向上粘连在一起,适当调大 FFT 窗口。

频率刻度轴选择(Linear vs. Logarithmic)

  • 线性刻度(Linear):每个像素对应固定的 Hz 数(如 1000Hz, 2000Hz, 3000Hz 等间距)。大部分生成工具(如 Coagula)默认基于线性刻度生成。如果文字看起来上下拉伸变形,先检查是否切换到了线性轴。
  • 对数/钢琴刻度(Logarithmic):低频区域被放大,高频被压缩。常用于音乐分析,但可能导致高频区隐写的文字挤压变形。

增益(Gain)与颜色映射(Color Scheme / Contrast)

  • 微弱的频域信号可能被主声轨的能量掩盖。通过调高增益(Gain)或调高对比度(Contrast),把背景暗色调深、高亮区调亮,可以使隐藏的二值二维码或字符显现。

采样率与高频削减(Nyquist 采样定理)

  • 根据奈奎斯特定理,音频能记录的最大频率等于采样率的一半(如 44.1kHz 采样率最高能记录 22.05kHz)。
  • 若隐写文字藏在 >18kHz 的高频区,使用有损压缩格式(如低码率 MP3)会导致高频被 Low-pass 滤波器直接切掉,因此此类隐写大多使用无损 WAV 格式 或 高码率 MP3 载体。

常用分析工具有:

  • Audacity:最常用,切换轨道模式为 Spectrogram,在 Spectrogram Settings 中调节 Window Size、Gain 及 Frequency Range。
  • Sonic Visualiser:加 Layer 选 Add Spectrogram,渲染质量和参数调节极其细腻,适合模糊图像的精细修复。
  • Adobe Audition:商业级频谱编辑工具,具备强大的频域选区和对比度调整能力。
SSTV

SSTV(Slow-Scan Television,慢扫描电视)是一种将图像调制为音频信号进行长距离传输(常用于无线电通信、国际空间站 ISS 图像传输)的技术。出题人常将带有 Flag 的图片转换为 SSTV 音频,要求选手通过声音信号“复原”出原始图像。

1.信号与传输原理

SSTV 并不是将数据打包成文件传输,而是将图像的像素特征转换为模拟音频信号:

  • 频域调幅/调频(Frequency Modulation)
    • 亮度/颜色映射:SSTV 利用音频的频率(Hz)表示像素的亮度或颜色
      • 1200 Hz:同步脉冲(Sync Pulse,用于标识新的一行或一帧)。
      • 1500 Hz:代表最暗/黑色像素(Black)。
      • 2300 Hz:代表最亮/白色像素(White)。
    • 颜色通道:RGB 图像会按照特定顺序(如 R-G-B 分别传输,或 Y-C-B-C-R 分量)逐行扫描并转换成频率变化的音频。
  • 听觉特征
    • 播放 SSTV 音频时,会听到极其杂乱、刺耳且具有明显周期性节奏的“嗡嗡”声与“刺啦”噪音(类似老式 56k 猫拨号上网的声音)。

2. 核心模式与 VIS 码(VIS Code)

SSTV 拥有数十种不同的编码模式(Modes),不同模式的传输速度、色彩格式和行分辨率各不相同:

  • 常见 SSTV 模式
    • Robot 36 / Robot 72:CTF 比赛中最常见的模式(Robot 36 传输仅需 36 秒,适合比赛节奏)。
    • Martin 1 / Martin 2:业余无线电中最常用的高清模式。
    • Scottie 1 / Scottie 2:经典电台传输模式。
    • Pasoke S1 / S2 等。
  • VIS 码(Vertical Interval Signaling)
    • 音频最开头的段落包含一段特殊的数字信号(Header),用于告知接收端“当前音频使用的是哪种模式”。现代解密软件通常会自动识别 VIS 码并切换到对应的模式进行解码。

3. 解码与分析工具链

解密 SSTV 的关键在于将音频实时或离线“播放”给 SSTV 接收解码器。

推荐工具:

① MMSSTV

Windows 平台最经典、功能最全的 SSTV 接收与解码软件,支持自动识别模式(Auto-Detect)和手动微调。

  • 特点
    • 自动识别 VIS 码:只要音频开头完整,会自动匹配模式(如 Robot36、Martin1、Scottie1 等)并逐行渲染图像。
    • 强大的图像微调:如果解码出来的图像偏斜(斜切变形),可通过面板上的 SyncSkew 功能拉直。
    • 音量与频谱显示:自带 FFT 频谱图,可清晰看到 1200Hz(同步信号)、1500Hz~2300Hz(图像信号)的能量起伏。

② RX-SSTV

另一款轻量级 SSTV 解码工具,界面比 MMSSTV 更直观,对部分微弱或带噪声信号的容错率较高。

  • 特点:适合在 MMSSTV 无法正常识别模式时作为备选工具使用,支持手动强制指定 SSTV 模式进行解码。

end.

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇