一、隐写原理

LSB隐写原理就是图片中的像素一般是由三种颜色组成,即三原色(红绿蓝)。由这三种原色可以组成其他各种颜色,在png图片的存储中,每个颜色占有8bit,即有256种颜色,一共包含256的三次方颜色,即16777216中颜色。人类的眼睛可以区分约1,000万种不同的颜色,剩下无法区分的颜色就有6777216。

LSB隐写就是修改了像素中的最低位,把一些信息隐藏起来。png图片是一种无损压缩,也只有在无损压缩或无压缩的图片(bmp图片是一种无压缩)上实现LSB隐写,

举例:观察图一、图二,没有发现区别,图二是经过图一加特定的信息(测试123aaa)生成新的图片,使用解密函数,可以重新恢复图片中的信息。

图一

图二

二、实现代码

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;/*** 图像掩蔽 RGB依次取Bit*/
public class ImageMasker {public boolean[] content = null;private BufferedImage image = null;private void setContent(Color color, int start) {content[start] = (1 == color.getRed() % 2);content[start + 1] = (1 == color.getGreen() % 2);content[start + 2] = (1 == color.getBlue() % 2);}private int changeToColor(int rgb, int start) {if (content[start]) rgb = rgb | 0x00010000;else rgb = rgb & 0xFFFEFFFF;if (content[start + 1]) rgb = rgb | 0x00000100;else rgb = rgb & 0xFFFFFEFF;if (content[start + 2]) rgb = rgb | 0x00000001;else rgb = rgb & 0xFFFFFFFE;return rgb;}/*** 读取文件* @param file* @return*/public boolean read(String file) {try {image = ImageIO.read(new File(file));} catch (IOException e) {e.printStackTrace();return false;}int height = image.getHeight();int width = image.getWidth();content = new boolean[height * width * 3];for (int h = 0; h < height; h++) {for (int w = 0; w < width; w++) {int color = image.getRGB(w, h);Color c = new Color(color);setContent(c, (h * width + w) * 3);}}return true;}public boolean write(String file) {if (null == image) return false;int height = image.getHeight();int width = image.getWidth();for (int h = 0; h < height; h++)for (int w = 0; w < width; w++)image.setRGB(w, h, changeToColor(image.getRGB(w, h), (h * width + w) * 3));try {String format = file.substring(file.lastIndexOf(".") + 1);ImageIO.write(image, format, new File(file));} catch (IOException e) {e.printStackTrace();return false;}return true;}}
public class StringEncoder {public static boolean[] encodeString(String s) {try {byte[] b = s.getBytes("GBK");boolean[] bl = new boolean[b.length * 8];for (int i = 0; i < b.length; i++) {bl[8 * i] = (((b[i]) & 0x1) == 1);bl[8 * i + 1] = (((b[i] >> 1) & 0x1) == 1);bl[8 * i + 2] = (((b[i] >> 2) & 0x1) == 1);bl[8 * i + 3] = (((b[i] >> 3) & 0x1) == 1);bl[8 * i + 4] = (((b[i] >> 4) & 0x1) == 1);bl[8 * i + 5] = (((b[i] >> 5) & 0x1) == 1);bl[8 * i + 6] = (((b[i] >> 6) & 0x1) == 1);bl[8 * i + 7] = (((b[i] >> 7) & 0x1) == 1);}return bl;} catch (Exception e) {e.printStackTrace();}return null;}/*** 根据隐写的图片,还原写入的数据* @param bl* @return*/public static String decodeString( boolean[] bl) {try {byte[] b = new byte[bl.length / 8];for (int i = 0; i < b.length; i++) {b[i] = 0;if (bl[8 * i]) b[i] = (byte) (b[i] | 0x1);if (bl[8 * i + 1]) b[i] = (byte) (b[i] | 0x2);if (bl[8 * i + 2]) b[i] = (byte) (b[i] | 0x4);if (bl[8 * i + 3]) b[i] = (byte) (b[i] | 0x8);if (bl[8 * i + 4]) b[i] = (byte) (b[i] | 0x10);if (bl[8 * i + 5]) b[i] = (byte) (b[i] | 0x20);if (bl[8 * i + 6]) b[i] = (byte) (b[i] | 0x40);if (bl[8 * i + 7]) b[i] = (byte) (b[i] | 0x80);}return new String(b, "GBK");} catch (Exception e) {e.printStackTrace();}return "Failed to decode string!";}}

三、测试

 public static void main(String[] args) {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));ImageMasker imageMasker = new ImageMasker();try {System.out.print("请输入文件路径:");String s = bufferedReader.readLine();imageMasker.read(s);System.out.print("写入内容还是读取(r/w):");s = bufferedReader.readLine();if (s.equals("r")) {System.out.print(StringEncoder.decodeString(imageMasker.content).trim());} else if (s.equals("w")) {System.out.print("输入数据(<=" + imageMasker.content.length / 8 + "):");s = bufferedReader.readLine();boolean[] value = StringEncoder.encodeString(s);for (int i = 0; i < value.length; i++)imageMasker.content[i] = value[i];for (int i = value.length; i < imageMasker.content.length; i++) {imageMasker.content[i] = false;}System.out.print("输出文件路径:");s = bufferedReader.readLine();imageMasker.write(s);}} catch (IOException e) {e.printStackTrace();}}

图一:C:\Users\Administrator\Desktop\sorce.png

图二:C:\Users\Administrator\Desktop\sorce1.png

中文乱码,大家帮我看看原因。

来自网络,可以试试

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;public class LSB {public static void main(String[] args) throws IOException {Scanner scan = new Scanner(System.in);System.out.println("***************LSB图像隐写编码程序****************");System.out.println("*********请选择想要使用的功能(输入数字)*************");System.out.println();System.out.println("******************1.LSB编码********************");System.out.println("******************2.LSB解码********************");System.out.println();System.out.print("请输入你想选择的功能:");String choice = scan.next();switch (choice) {case "1":System.out.print("请输入需要加密的文件的路径:");String textPath = scan.next();System.out.print("请输入png图像辅助文件的路径:");String imagePath = scan.next();System.out.print("最后再输入一下生成的png图片的保存路径:");String imageOutputPath = scan.next();LSBEncoder(textPath, imagePath, imageOutputPath);scan.close();break;case "2":System.out.print("请输入待解码的png图片的路径:");String imageInputPath = scan.next();System.out.print("请输入解码后,存放数据的文件名称");String textFilePath = scan.next();LSBDecoder(imageInputPath, textFilePath);scan.close();break;default:System.out.print("谁叫你乱按键盘的啊!!!活该不能执行,哼?!");scan.close();break;}}public static void LSBEncoder(String textPath, String imagePath, String imageOutputPath) throws IOException {//读取png图像BufferedImage image = ImageIO.read(new File(imagePath));int width = image.getWidth();int height = image.getHeight();int[][][] rgb = new int[width][height][3];//将图像每个点的像素(R,G,B)存储在数组中for (int w = 0; w < width; w++) {for (int h = 0; h < height; h++) {int pixel = image.getRGB(w, h);//读取的是一个24位的数据//数据三个字节分别代表R、B、Grgb[w][h][0] = (pixel & 0xff0000) >> 16;//Rrgb[w][h][1] = (pixel & 0xff00) >> 8;//Brgb[w][h][2] = (pixel & 0xff);//G}}//导入待加密机密文件FileInputStream fis = new FileInputStream(textPath);int byteLen = fis.available();byte[] buf = new byte[byteLen];fis.read(buf);//我用两个字节(16位)表示数据部分的长度,也就是说,图像转像素点后,前16个像素点的R对应的字节的最低有效位都是默认表示数据字节数组的长度//规定,数据的长度最大为2^16-1int[] bufLen = new int[2];bufLen[0] = (byteLen & 0xff00) >> 8;bufLen[1] = (byteLen & 0xff);for (int i = 0; i < 2; i++) {for (int j = 7; j >= 0; j--) {int h = (i * 8 + (7 - j)) / width;int w = (i * 8 + (7 - j)) % width;//只取每个像素点的R,的字节的最低位if ((bufLen[i] >> j & 1) == 1) {rgb[w][h][0] = rgb[w][h][0] | 1;} else {rgb[w][h][0] = rgb[w][h][0] & 0xe;}}}//按照规则将数据的二进制序列全部放到每一个像素点的第一个字节的最后一位上for (int i = 2; i < byteLen + 2; i++) {for (int j = 7; j >= 0; j--) {//高int h = (i * 8 + (7 - j)) / width;//宽int w = (i * 8 + (7 - j)) % width;if ((buf[i - 2] >> j & 1) == 1) {rgb[w][h][0] = rgb[w][h][0] | 1;//变1} else {rgb[w][h][0] = rgb[w][h][0] & 0xe;}}}//        构建通过编码生成的png图片的类型BufferedImage imageOutput = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int w = 0; w < width; w++) {for (int h = 0; h < height; h++) {int[] color = new int[3];color[0] = rgb[w][h][0] << 16;color[1] = rgb[w][h][1] << 8;color[2] = rgb[w][h][2];int pixel = 0xff000000 | color[0] | color[1] | color[2];imageOutput.setRGB(w, h, pixel);}}ImageIO.write(imageOutput, "png", new File(imageOutputPath));}public static void LSBDecoder(String imageInputPath, String textFilePath) throws IOException {BufferedImage imageInput = ImageIO.read(new File(imageInputPath));int width = imageInput.getWidth();int height = imageInput.getHeight();int[] bufLen = new int[2];//将图像每个点的像素(R,G,B)存储在数组中for (int i = 0; i < 2; i++) {int[] bits = new int[8];for (int j = 7; j >= 0; j--) {int h = (i * 8 + (7 - j)) / width;int w = (i * 8 + (7 - j)) % width;int pixel = imageInput.getRGB(w, h);int r = (pixel & 0xff0000) >> 16;bits[j] = (r & 1) << j;}bufLen[i] = bits[7] | bits[6] | bits[5] | bits[4] | bits[3] | bits[2] | bits[1] | bits[0];}int byteLen = ((bufLen[0] << 7) | bufLen[1]);byte[] buf = new byte[byteLen];for (int i = 2; i < byteLen + 2; i++) {int[] bits = new int[8];for (int j = 7; j >= 0; j--) {int h = (i * 8 + (7 - j)) / width;int w = (i * 8 + (7 - j)) % width;int pixel = imageInput.getRGB(w, h);int r = (pixel & 0xff0000) >> 16;bits[j] = (r & 0x1) << j;}buf[i - 2] = (byte) (bits[7] | bits[6] | bits[5] | bits[4] | bits[3] | bits[2] | bits[1] | bits[0]);}FileOutputStream fos = new FileOutputStream(textFilePath);fos.write(buf);fos.close();}
}

图片LSB隐写(java)相关推荐

  1. [CTF-Misc]LSB隐写-隐藏在图片中的秘密

    目录 前言 理论 像素通道 Pillow库 LSB隐写 代码 验证-读取数据 实战 杭州师范大学第四届网络与信息安全竞赛-Misc-babyLSB 代码 收尾 总结 前言 今天要做的事情还挺多的,主要 ...

  2. java lsb隐写_LSB隐写工具对比(Stegsolve与zsteg)

    起因 很久很久以前,有一道送分题没做出来,后来看writeup,只要zsteg就行了. 命令运行的结果 root@LAPTOP-GE0FGULA:/mnt/d# zsteg 瞅啥.bmp [?] 2 ...

  3. 基于MatLab实现LSB(最低有效位)算法完成图片数字水印隐写功能

    文章目录 前言 一.图像处理基础 二.LSB数字隐写算法 三.LSB数字隐写算法实现 四.数字隐写和提取过程 总结 前言 已经好久没写博客了,最近是在有些忙,但是忙里偷闲写一篇新学的知识点,所以准备写 ...

  4. LSB隐写(最低有效位隐写)

    LSB隐写(最低有效位隐写) 我们先思考如下几个问题,然后再去实现 1.图片在计算机中存储的方式 2.什么原因可以是实现隐写 3.为什么选择最低有效位? 4.具体实现思路 5.如果用代码实现LSB隐写 ...

  5. 【信息隐藏】LSB隐写算法的实现与性能分析

    LSB隐写算法的实现与性能分析 Presented by R.G. 本项目所有的代码文件均可以在我的Github上找到,建议运行我git仓库里的代码文件,不要直接复制本文展示的代码跑 项目地址:htt ...

  6. 隐写术----LSB隐写

    0x00 前言 前文介绍过PNG的文件格式,以及如何在不影响正常浏览的前提下在PNG文件中隐写payload.这次介绍另一种隐写技巧--LSB隐写 其实LSB隐写术已经有很多大佬发了文章介绍,我只是一 ...

  7. 隐写术浅谈(二):LSB隐写与IDAT隐写

    Misc 学习(番外篇) - 隐写分析:隐写术(2) 在本系列的其他文章中,我主要讲了讲自己对于隐写分析的一些浅薄理解,但是大都是针对于如何反隐写(做题嘛,不寒碜),基本上很少讲如何去隐写.上一篇我们 ...

  8. 浅谈LSB隐写解题与出题

    点击"蓝字"关注,获取更多技术内容! 前言:LSB隐写在CTF中属于出现得比较多的类型.这篇文章对LSB隐写的原理,解题方法,出题脚本,以及LSB隐写特性进行研究. LSB隐写原理 ...

  9. buuctf(misc) FLAG [LSB隐写]

    用stegSolve 打开是LSB隐写 保存为bin 用解压软件打开 将其用IDA打开 hctf{dd0gf4c3tok3yb0ard4g41n~~~}

最新文章

  1. 【网络流24题】D、魔术球问题(有向无环图的最小路径覆盖、思维)
  2. GTK+重拾--08 GTK+中的对话框
  3. 【零基础学习iOS开发】【02-C语言】10-函数
  4. GIT项目管理工具(part8)--版本控制
  5. Xamarin 从零开始部署 iOS 上的 Walterlv.CloudKeyboard 应用
  6. 编写优雅代码,从挖掉恶心的if/else 开始
  7. 32岁,程序员,年薪60W,果断辞职考进体制内,月薪5K
  8. AVR 工具指南(一)
  9. 华为电脑c盘哪些文件可以删除,c盘可以删除哪些文件
  10. 拓扑学(代数拓扑学)的有趣应用
  11. 【DATAGUARD】物理dg在主库丢失归档文件的情况下的恢复(七)
  12. 运算放大电路设计实验
  13. java date类赋值日期_Java中Date时间类
  14. 炫酷恶趣强大的制作神器小程序源码_支持多种流量主模式
  15. 灰度图像压缩 DP算法 位运算详解
  16. 2021Java笔试真题,满满干货指导
  17. 使用Python模拟社会财富分配问题,得出了几个有趣的结论
  18. 【总结】 ---ACM为什么这么难
  19. 正確的中文名翻譯英文名【下】
  20. 还在自学黑客?一般人我劝你还是算了吧!

热门文章

  1. 基于Visio Studio Code打造go的IDE
  2. 为什么element ui 中表单验证validate验证成功不执行验证成功的逻辑代码
  3. FileInputStream与BufferedInputStream的区别
  4. PPP over Ethernet(PPPoE)协议
  5. 切换到ZSH以后遇到的坑
  6. 4、OOA 面向对象分析
  7. 二级网站域名怎么申请?
  8. 点餐系统架构模型_餐馆点餐系统课程设计
  9. matlab不是内部或外部命令_如何解决Telnet不是内部或外部命令(电脑提示telnet不是内部或外部命令...
  10. Android版本兼容性问题