ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

代码藏进图片的5种方法:从Notepad++拼接到LSB隐写

代码藏进图片的5种方法:从Notepad++拼接到LSB隐写 你有没有遇到过这种情况代码写好了想发给别人看又怕被随手复制改得面目全非或者有些敏感配置想临时在服务器之间传一下又不想留下明显的日志记录再或者就是单纯想在 CTF 比赛、逆向分析里藏点线索。我最早接触把代码藏进图片这件事是被一个老前辈在项目交付时秀了一手——他把一串部署脚本直接缝进了一张普通的产品截图里旁边的人愣是没看出来。后来我自己折腾了好几种玩法从 Windows 上最简单的 Notepad 手工拼接到 Linux 下一条 cat 命令完事再到现在常用的 LSB 隐写脚本越玩越觉得这里面门道挺多。这套东西本质上就是隐写术Steganography跟密码学不太一样密码学是让内容变成谁也看不懂的乱码但乱码本身就暴露了这里有秘密隐写术则追求让秘密藏得看不出痕迹肉眼看上去就是一张普通图片。今天这篇文章我会把 5 种不同层级的玩法从头到尾拆开讲从最容易上手的 Notepad 字节拼接到 Windows/Linux 下的命令行合并再到 Steghide 工具和 Python 自定义 LSB 隐写脚本每一招都附上实操命令、原理分析和避坑经验。不管你是刚接触命令行的小白还是已经在用 Linux 干活的老手都能在里面找到适合自己那一档的方法。1. 为什么要把代码藏进图片应用场景与核心思路1.1 这不是天方夜谭隐写术的现实用途先说个最朴素的需求很多人想把代码变成图片其实是希望代码能稳定地跨平台展示。比如你在微信、飞书里发一段代码对方手机上看很容易折行、乱码但如果你把代码渲染成图片再发过去排版就固定了而且别人想直接复制走就没那么容易——这是防伸手党和白嫖党的常见姿势。热词里那个plaintext代码怎么转换成图片搜得特别多就是这个原因。但隐藏和转换是两码事。真正意义上的隐藏指的是图片表面看起来完全没有异样代码却实实在在地存在于图片文件的某处。这类手段的典型用途包括CTF 比赛夺旗赛与渗透测试出题人把 flag 或者线索藏进图片参赛者需要靠工具和分析能力找出来。个人隐私备忘把密码、密钥、服务器地址写在代码文件里跟一张普通照片拼在一起相对不那么引人注目。合法场景下的数据携带比如把配置脚本附在一张验收照片后面传给别人时对方用工具提取文件管理更干净。防误删与防复制给代码披上图片的马甲在某些内容审核机制下能混过去但我不建议把这个当规避审查的手段合规性一定要自己把关。注意本文讨论的是技术原理和常见玩法请务必在合法合规的前提下使用。拿隐写术做坏事、藏恶意内容只会给自己惹麻烦。1.2 五类方法的整体脉络与选型思路我按技术门槛从低到高、隐蔽性从弱到强排了个序列方法工具/平台门槛隐蔽性本质原理方法一Notepad含 Hex 插件极低低把代码字节直接拼到图片字节流尾部方法二Windows copy /b 命令低中二进制文件流合并方法三Linux cat 命令低中多文件流顺序拼接输出方法四Steghide 工具中高LSB 最低有效位替换视觉无痕方法五Python Pillow 自写脚本中高高自定义 LSB 隐写可加密、可批量很多人一上来就追求最隐蔽的 LSB 隐写其实没必要。如果你的应用场景只是防止随手复制那方法二、方法三的拼接大法已经够用而且提取手段极其简单对方只要有基础 Linux 知识就能操作这种半透明的隐藏反而更适合协作。只有当你需要真正不被人察觉地传递信息时才需要上 Steghide 或者 Python 脚本。后面每一个方法我都会给出适用场景方便你对号入座。2. 方法一Notepad 上手——基础拼接型隐藏2.1 原理知识图片文件的结构与冗余空间先理解图片文件在计算机里到底是什么。不管 JPEG 还是 PNG本质上都是一个连续的二进制字节流文件头有固定标识JPEG 是FF D8 FFPNG 是89 50 4E 47文件尾也有结束标记JPEG 是FF D9PNG 是49 45 4E 44 AE 42 60 82。风险点在于图片查看器在解码时是从文件头开始读的读到图片数据结束标记后再往后的字节它根本不关心。这就给了我们可乘之机——把代码追加在结束标记之后图片软件照样能正常打开肉眼看到的就是一张完完整整的图但用文本编辑器打开文件时尾部多出来的内容就会暴露出来。这里有个关键区别要记住JPEG 的格式相对宽松很多解码器在遇到FF D9后就直接忽略后续内容所以往 JPEG 后面追加字节几乎不会导致打不开但 PNG 文件有 CRC循环冗余校验机制如果直接往文件尾追加内容某些严格的查看器可能会报错、甚至拒绝打开。我实测过Windows 自带的照片查看器对 PNG 追加字节通常能忍但 Chrome 浏览器直接打开就会报图片文件格式损坏。所以入门阶段强烈建议拿 JPEG 图片来练手别用 PNG 给自己添堵。2.2 实操步骤利用 Notepad 插件编辑图片字节官方 Notepad 本身是文本编辑器但配合 Hex Editor 插件就能以十六进制模式打开任意文件看到每个字节的原始值。操作路径是这样的打开 Notepad通过菜单插件 - Plugins Admin 搜索Hex Editor安装后重启编辑器。用 Notepad 直接打开一张示例图片sample.jpg打开时选择所有文件。如果你只装了 Hex Editor 插件文件会以十六进制显示如果没装插件或者你想用另一个思路也可以在纯文本模式下打开图片此时图片二进制会被转成乱码但别慌我们要找的是文件结尾的标记。按下Ctrl End跳到文件末尾在 JPEG 文件里你大概率能看到结尾的FF D9或者伴随其他填充字节。在文件末尾直接开始输入代码或者粘贴一串明文代码。注意如果是纯文本输入编码建议保持 UTF-8否则中文字符注释可能乱码。等保存关掉之后你就会得到一张看起来正常、但尾巴里藏着代码的图片。别人要用文本编辑器打开它滚动到最底部才能发现端倪——隐蔽性是初级的但用于快速应急分享完全够用。实操心得在 Notepad 里用纯文本模式直接编辑图片文件时文件会非常大滚动到底部很吃力。我通常先打开文件后立刻Ctrl End然后开始追加内容。另外建议在代码开头加一行BEGIN CODE、结尾加END CODE的标记提取时用搜索功能定位更省事。2.3 注意事项JPEG 和 PNG 的处理差异我在测试中踩过几个比较典型的坑你现在可以直接避开PNG 文件追加内容后用 Chrome 打不开前面说过 PNG 有 CRC 校验如果查看器严格校验尾部数据就会提示文件损坏。应对办法是隐藏完成后用图片处理工具另存一次让文件重新生成合法的 PNG 结构不过这样也可能会把追加数据清掉或者干脆更换为 JPEG 图片。不要把代码放在文件头图片文件头是固定的魔术字节magic number从文件头开始插入代码会让查看器直接判定无效图片谁拿到都打不开。所有拼接式隐藏的操作位置都是文件尾部。图片尺寸不要太小如果你用的图片只有几十 KB多出几十 KB 的代码会让文件体积明显变大懂行的人一看文件大小就能猜出不对劲。尽量选一张 1MB 以上、内容复杂的照片作为载体代码藏进去后体积变化几乎看不出来。3. 方法二Windows copy 命令——零工具拼接法3.1 原理说明二进制文件合并的本质如果嫌 Notepad 打开大文件卡顿那在 Windows 上还有更直接的办法——利用copy命令的二进制合并模式。DOS 时代流传下来的copy /b语法/b表示以二进制模式处理不对内容做任何转换纯粹地把多个文件一个接一个地合并成一个新文件。这意味着你可以把代码先写进一个文本文件或者压缩包然后执行一条 copy 命令把图片和代码文件拼接起来。重点来了这条命令不在乎你拼接的是文本、压缩包还是任何其他格式它只做字节级别的首尾相连。3.2 实操步骤copy 命令把代码藏进图片假设我在D:\test目录下有一张photo.jpg以及一个写好的代码文件script.py。打开 CMD 或者 PowerShell进到对应目录cd /d D:\test copy /b photo.jpg script.py output.jpg执行完成后output.jpg就是一张藏了代码的图片。用图片查看器打开它看到的还是原来的照片用记事本打开滚动到末尾就能看到脚本代码。如果你想藏的东西不止一个文件可以先把它们打包成 zip再拼接copy /b photo.jpg myscripts.zip output.jpg更实用的是Windows 自带的解压软件比如 WinRAR、7-Zip能直接从output.jpg中识别出 zip 压缩包——因为 zip 格式的文件头PK就藏在 JPEG 结尾标记之后。你把output.jpg的后缀直接改成.zip或者用解压工具打开时选择打开方式 - 压缩文件管理器就能拖出里面的代码文件不需要先拆分。3.3 提取方法与验证技巧提取方法很简单反过来拆开就行。方法有三文本提取用任意文本编辑器记事本、VS Code、Notepad打开图片滚到文件末尾能看到完整代码。压缩包提取如果拼的是 zip直接把图片扩展名改成.zip解压即可。binwalk 分离Linux 下binwalk output.jpg binwalk -e output.jpgbinwalk 能自动识别文件中嵌入的常见文件结构然后把隐藏的 zip 或文件提取出来这个工具做 CTF 题时几乎是标配。我在 Windows 上实测下来拼接法的一个好处是不挑图片格式JPEG、PNG、BMP 都能拼前提是图片查看器对尾部冗余数据的容忍度高。不过我仍然建议优先用 JPEG理由还是前面说的 PNG CRC 校验问题。注意复制粘贴代码时如果文件编码不是 UTF-8中文注释在记事本里可能显示为乱码提取后可以再用 Notepad 转换编码。更推荐的做法是先写代码文件再与图片拼接这样代码本身的编码是正常的。4. 方法三Linux cat 命令——一行命令完成隐藏4.1 核心逻辑cat 读入多个文件按顺序输出到了 Linux 上隐藏代码的方式更简单粗暴cat命令天生就是干这个的。cat的名场面一般是查看文件内容但它也支持把多个文件按顺序串联后重定向到一个新文件里。原理跟 Windows 的copy /b如出一辙——纯字节流顺序拼接。为什么推荐在 Linux 上用 cat 而不是其他命令因为它最短、最好记、不用装额外东西几乎每一个发行版都预装。对运维场景来说临时藏个配置文件到图片里一条命令搞定不用折腾任何编辑器。4.2 实操cat 拼接、strings 查看、file 验证假设当前目录下有cover.jpg和deploy.sh执行cat cover.jpg deploy.sh final.jpg执行后用file命令验证一下文件类型file final.jpg正常情况下会输出JPEG image data说明图片查看器可以正常识别。如果输出里带了奇怪的格式信息那说明图片本身有问题。查看图片里到底藏了什么内容用strings命令最方便它会输出二进制文件里所有可打印字符串strings final.jpg | grep -i BEGIN CODE -A 20当然这是明文拼接法隐蔽性很一般。任何人只要执行一次strings就能看到你追加的代码内容。所以更稳妥的做法是先把代码压缩或加密再拼接。比如zip -P mypassword deploy.zip deploy.sh cat cover.jpg deploy.zip final.jpg这样查看图片的人只会看到一个压缩包出现在末尾没有密码解不开。还有一招是把代码隐藏成图片的 EXIF 信息但这跟拼接不太一样更多的是一种元数据隐写咱们先不展开。4.3 对比表格三种拼接式隐藏横向对比到这里Notepad、Windows copy、Linux cat 三种方法都在做同一件事——往图片尾部追加数据。我把它们的差别整理成一个表方便你按平台选型对比维度NotepadWindows copy /bLinux cat操作系统WindowsWindowsLinux/macOS是否需要额外工具需要安装 Hex 插件无需安装无需安装操作复杂度中找文件尾低一条命令低一条命令适合隐藏格式纯文本代码文本、zip、任意文件文本、zip、任意文件中文编码风险有有可规避低全命令行提取难易度编辑器打开编辑器/解压工具strings/binwalk实际操作中我的建议是日常 Windows 环境下想快速隐藏一个代码文件优先用copy /b在服务器上操作就无脑用cat。Notepad 的方法只有在不方便跑命令、就想手动改文件的极端场景里才用得上。5. 方法四Steghide 工具——真正的无痕隐写5.1 工具安装与基础原理LSB 替换拼接式隐藏最大的软肋是尾巴太长随便一个strings命令就能露馅。如果想追求真正的无痕就得从图片本身的像素数据里做文章。这里就得聊到 LSBLeast Significant Bit最低有效位隐写了。一切位图BMP、PNG 等由像素构成每个像素又由红绿蓝三个颜色通道组成每个通道用 8 位0~255表示。人的眼睛对颜色变化并不敏感你把每个通道的最低二进制位改掉肉眼根本看不出差别。LSB 隐写的本质就是把要隐藏的数据转换成二进制流逐位替换到图片像素通道的最低位上。手动实现这个思路非常繁琐所以有现成工具可用——steghide就是 Linux 上最常用的 JPEG/BMP 隐写工具。它不光做 LSB 替换还会用加密算法保护隐藏内容没有正确密码就无法提取信息安全等级比拼接法高了一大截。安装很简单# Debian/Ubuntu sudo apt install steghide # CentOS/RHEL 系列可能需要编译安装或者使用 EPEL 源 sudo yum install steghide5.2 嵌入与提取的完整命令假设我有一张vacation.jpg这是载体图以及一个secret.txt里面是代码执行嵌入命令steghide embed -cf vacation.jpg -ef secret.txt -p MyPassw0rd参数说明-cfcarrier file即载体图片文件要求 JPEG、BMP、WAV 等格式。-efembed file即要隐藏的文件。-p密码口令提取时要用到。命令执行成功后vacation.jpg会被原地覆盖也就是说图片里已经注入了隐藏内容。注意steghide 默认会校验载体图片格式JPEG 是它最拿手的类型。提取时就简单了steghide extract -sf vacation.jpg -p MyPassw0rd执行后会在当前目录生成之前隐藏的secret.txt。如果密码不对或者图片里根本没藏内容会直接报错提示。5.3 为什么说比拼接法安全得多Steghide 在隐写过程中的一个关键特性是它不会往图片文件尾部追加任何数据而是把隐藏在图片的 DCT 系数JPEG 压缩域或者位图的 LSB 里图片文件的大小基本不变化。你拿strings搜不到隐藏文件的内容拿file检查文件类型也一切正常肉眼更看不出像素差异。我在 CTF 解题和日常测试中验证过strings、hexdump、file这几个常规体检手段对 Steghide 隐藏的结果完全无效。这也是为什么它适合真正需要保密传递代码或密钥的场景——比如你在一个团队里要私下分享一段数据库连接配置又不想让监看日志的人察觉Steghide 就很好用。当然Steghide 并非无懈可击有专门的工具如 StegDetect能通过统计分析猜测图片是否被 Steghide 处理过但它依然能挡住 99% 的普通人。这就像给门上了把锁不是绝对安全但比把钥匙贴在门上要强得多。实操心得Steghide 对载体图片的大小有要求。如果载体图太小、而隐藏文件过大会报not enough space错误。经验上隐藏文件大小最好不要超过载体图片体积的 10%~15%。另外JPEG 是 Steghide 最稳定的格式PNG 虽然也能用但我实测过某些 PNG 图片会报unsupported file format踩坑概率更高。6. 方法五Python 脚本实现 LSB 隐写——可定制可批量6.1 从原理到代码像素 RGB 的最低位工具虽好但毕竟不够灵活。如果你想批量处理 100 张图片、自定义嵌入位深度、或者在生产环境里集成这个功能就得自己写代码。Python Pillow 库是最顺手的组合。核心原理还是 LSB 替换。以 PNG 图片为例Pillow 读取后可以拿到每个像素的 RGB 三通道值。举个例子某个像素的红色通道值是200二进制是11001000它的最低位是0如果我想在这一点隐藏一个二进制位1就把最低位改成1得到11001001十进制是201。人眼看过去200 和 201 的颜色差别几乎为 0。就这样把代码文件的每一个 bit 依序塞进图片的像素最低位就完成了隐写。为什么用 PNG 而不是 JPEG因为 JPEG 是有损压缩格式保存时会对像素数据进行量化重构你硬塞进去的 LSB 数据大概率会被破坏。而 PNG 是无损压缩像素值原样保留塞进去的 bit 一个都不会丢。所以自写 LSB 脚本时载体图片务必选 PNG。6.2 完整 Python 脚本嵌入、提取先安装依赖pip install pillow这里给出两个函数分别用于嵌入和提取。我把注释写到代码里你可以直接复制去测试from PIL import Image # 嵌入把 data 字节流藏进 image_path def encode_image(image_path, data, output_path): # 打开图片并转为 RGB 模式确保每个像素都有三个通道 img Image.open(image_path).convert(RGB) pixels list(img.getdata()) # 先把数据转成二进制字符串每个字节取 8 位前面补 0 binary_data .join(format(byte, 08b) for byte in data) # 在数据末尾加上结束标志用 16 个 0 表示终止方便提取时判断 binary_data 0 * 16 # 检查容量是否足够 if len(binary_data) len(pixels) * 3: raise ValueError(图片容量不足请换一张更大的 PNG 图片) # 逐个像素处理把 binary_data 中的每一位写进 RGB 通道的最低位 new_pixels [] data_index 0 for pixel in pixels: new_pixel list(pixel) for channel in range(3): # R, G, B if data_index len(binary_data): # 取当前通道值把最低位改成数据位 bit int(binary_data[data_index]) new_pixel[channel] (pixel[channel] 0xFE) | bit data_index 1 new_pixels.append(tuple(new_pixel)) # 生成新图片并保存 new_img Image.new(RGB, img.size) new_img.putdata(new_pixels) new_img.save(output_path) print(f[] 嵌入完成结果保存至 {output_path}) # 提取从 image_path 中还原数据 def decode_image(image_path): img Image.open(image_path).convert(RGB) pixels list(img.getdata()) binary_data for pixel in pixels: for channel in range(3): # 取出每个通道的最低位 binary_data str(pixel[channel] 1) # 按 8 位一组还原成字节 bytes_list [] for i in range(0, len(binary_data), 8): chunk binary_data[i:i8] if len(chunk) 8: break byte int(chunk, 2) # 如果连续读到 2 个 0x00 字节也就是 16 个 0视为结束标志 if byte 0 and i 8 len(binary_data) and int(binary_data[i8:i16], 2) 0: break bytes_list.append(byte) # 转回原始数据 result bytes(bytes_list) print(f[] 提取完成共恢复 {len(result)} 字节) return result # 使用示例 if __name__ __main__: # 嵌入把 secret.txt 藏进 cover.png with open(secret.txt, rb) as f: file_data f.read() encode_image(cover.png, file_data, output.png) # 提取从 output.png 还原 secret.txt recovered decode_image(output.png) with open(recovered.txt, wb) as f: f.write(recovered)运行之后output.png看起来还是原来的图但你的代码已经全部藏在像素最低位里了。对比一下原图和新图肉眼根本看不出任何差异。6.3 参数设定与扩展思路这个脚本只是一个起点根据你的实际需求可以往几个方向扩展增加密码加密在嵌入前先对 file_data 用 AES 加密提取时输入密码解密。Python 可以用cryptography库实现这样即使别人猜到了 LSB 隐写也解不开真正的数据。随机像素散布上面的实现是逐像素顺序写入懂行的人分析像素 LSB 的规律性有可能猜测到这里藏了东西。改造成用随机数选择像素位置能显著提升抗检测能力。多层图片批量隐藏写一个循环把一批代码文件分别藏进一批图片里适合批量处理场景。嵌入信息到 EXIF 元数据Pillow 也可以直接修改图片的 EXIF 信息把代码写进UserComment等字段这种方式的隐蔽性介于拼接法和 LSB 之间。注意LSB 隐写对图片格式有硬性要求。如果你在 JPEG 上执行上面的脚本保存时 Pillow 会有质量损失隐藏的数据几乎肯定会被破坏。所以自写脚本务必使用 PNG 载体。另外微信、QQ 等聊天工具传输图片时会做压缩处理这也会破坏 LSB 数据——传输隐藏了代码的图片尽量用网盘、邮件附件这类不做二次压缩的方式。7. 常见问题与避坑实录7.1 问题速查表我把实际操作中高频遇到的情况整理成了一张速查表你可以直接把它收藏起来当手册用现象可能原因解决方案拼接后图片打不开PNG 文件被追加数据后触发 CRC 校验改为 JPEG 载体或用图片软件重新另存strings 命令搜不到代码代码被编辑器保存成了 UTF-16 编码包含空字节用iconv转成 UTF-8 再拼接Steghide 报 not enough space载体图片太小藏不下大文件换一张分辨率更高的图片或压缩隐藏文件Steghide 提示 unsupported file format部分 PNG 或 JPEG 变体不被识别用 Pillow 另存为标准 JPEG/PNG 格式再试提取出来的代码中文乱码拼接时文本编码不一致统一使用 UTF-8 编码写代码文件微信传输后隐藏内容消失聊天软件对图片做了转码/压缩改用网盘传输或把图片打包成 zip 发送LSB 隐藏后图片被保存为 JPEGPillow 保存时选择了有损格式指定formatPNG保存7.2 我踩过的几个坑与补救方案第一个坑是图片体积变化异常明显。早期我用一张 200KB 的小图去藏一个 1.5MB 的压缩包拼完之后文件变成了 1.7MB明眼人一看就知道不对劲。后来我学乖了要么先用 tar/zip 压缩代码要么选一张跟代码体积接近的大图。总之拼接式隐藏的核心原则是体积不能凭空暴涨太多。第二个坑是隐藏内容被二次保存破坏。有一次我用 Steghide 藏好代码后手贱把图片拖到 Windows 画图里画了一笔又保存结果画图软件重构了整个 JPEG 编码隐藏的数据直接报废。复盘下来的教训是完成隐写操作后的图片不能再经过任何图片编辑软件否则压缩或重新编码的过程会把数据冲掉。图片只是数据的载体要改图请在隐藏之前改好。第三个坑是提取时忘记结束标记。最初写 LSB 脚本时我没设计结束标志结果提取出来一长串无意义的尾部字节。后来在二进制数据末尾加上 16 个 0即两个空字节作为终止符提取逻辑才变得干净可靠。这个思路你在自己实现任何隐写脚本时都可以沿用。第四个坑是图片经过社交软件传输后数据被破坏。LSB 隐写特别怕有损压缩而微信、Telegram 这类软件为了省流量会在发送时压缩图片。我建议需要传递隐写图片时用云盘链接、邮件附件或 Git 仓库别走聊天软件。7.3 如何验证你的隐藏是否成功写完隐藏操作后别急着庆祝先按下面几步自检先看一眼图片能不能正常打开双击图片确认肉眼看到的画面没有异常文件能正常显示。对比文件大小隐写前后文件大小的变化要在合理范围内。拼接法会有明显增量LSB 法增量极小。用 strings 试搜关键字如果是拼接式隐藏搜一下代码文件里的特征字符串比如BEGIN CODE确认能搜到但也要意识到别人也能搜到如果是 LSB 隐藏这个搜索应该没有结果。做一次完整的提取测试把刚藏好的图片提取一次比对提取出来的文件与原始文件的 md5 是否一致确保数据没在写入过程中被破坏。准备一张副本测试多种查看器把图片丢进浏览器、Windows 照片查看器、Linux 的 ImageMagick 里各打开一遍确认兼容性没问题。按这套流程走完基本不会出现自己藏进去的东西自己也提取不出来的尴尬局面。8. 写在最后选对方法比学会更多方法更重要五种方法我全部在实际环境里跑过现在做项目时最常用的其实是两个极端日常传个临时脚本直接用 Linux 的 cat 拼接快且省事真要跟别人玩点藏东西的活儿或者处理敏感配置就用 Steghide 或者自写的 LSB 脚本。Notepad 和 Windows copy 那些更多是应急或者给不熟悉命令行的同事演示用。最后再分享一个小技巧不管用哪种方法隐藏代码都建议在代码文件头部写一个固定的标记行比如___HIDDEN_START___这样提取时可以用grep或搜索功能快速定位不用翻半天。我所有的隐写脚本里都会内置这个标记CTF 比赛里碰到队友藏的东西凭这一个标记就能秒提取。隐写术这个东西本质上就是信息伪装的艺术。你用得好它就是提升协作效率、保护数据安全的好帮手用歪了就可能触碰法律和道德的底线。我的建议是拿它练手、做题、做合法的个人项目都没问题但千万别动歪脑筋去隐藏什么不该藏的东西。技术本身是工具怎么用取决于你。
返回列表