【HUST】网安|多媒体数据安全实验|LSB隐写和DCT域JSTEG+F4+F5隐写及检测
代码仓库:代码、嵌入提取使用的图像、jpeg_tool库、实验报告_Gitee。
实验环境:MATLAB 2022a。
LSB空域隐写
原理
我们知道,一个像素点是由R(red)B(blue)G(green)三原色组成的,通过调配这三种颜色,我们可以得到所有的颜色。
 比如白色(255,255,255),二进制就是bin(11111111,11111111,11111111),黑色(0,0,0),二进制就是(00000000,00000000,00000000)。
既然如此,我们将每个二进制的最后一位给替换成别的,比如将(255,255,255)替换成(254,254,254),bin(11111110,11111110,11111110),肉眼根本分辨不出。
因此我们将需要的信息转成二进制,再将每一位替换掉三元组的最后一位,便完成了LSB隐写。
值对现象原理
但是,LSB隐写有明显的统计特征——“值对现象”。
因为密文具有随机性,
 可认为密文的二进制比特流中的0和1是均匀的。
将二进制的最后一位替换成密文的比特后,
 原图像的灰度值的奇数和偶数将会趋于相同。
比如,颜色值为100的本来有10个,为101的有100个,在嵌入随机密文后,它们的个数有可能趋于相同——也就是颜色值为100的和101的都变成55个。
实验内容
载体选取:BMP格式灰度图像。
 嵌入信息:JPG图像。
 嵌入大小:227680bits。
若BMP图像不方便寻找,可用MATLAB将jpg图像转换成BMP:
% 将jpg转换成bmp图像
A=imread('avatar.jpg');
imwrite(A,'cover.bmp','bmp');
嵌入信息程序:
clear  % 清空变量
close all % 关闭打开的图像窗口
Picture = imread('cover.bmp');
Double_Picture = Picture;
Double_Picture = double(Double_Picture);
%读取秘密信息文件为二进制数字流,为嵌入图像做准备
wen.txt_id = fopen ('avatar2.jpg','r');
[msg, len] = fread(wen.txt_id, 'ubit1');
%根据LSB算法,将秘密信息的二进制数字流隐藏入连续的像素中
[m, n]=size(Double_Picture);
p=1;
for f2 = 1:n
    for f1 = 1:m
        Double_Picture(f1, f2) = Double_Picture(f1, f2)-mod(Double_Picture(f1,f2), 2)+msg(p,1);
        if p == len
            break;
        end
        p = p+1;
    end
    if p == len
        break;
    end
end
%将得到的隐密图像保存为stego.bmp,并利用Matlab将载体图与隐密图画在同一对话框中进行比较
Double_Picture=uint8(Double_Picture);
imwrite(Double_Picture, 'stego1.bmp');
subplot(121);imshow(Picture);title('未嵌入信息的图像');
subplot(122);imshow(Double_Picture);title('嵌入信息的图像');
fclose(wen.txt_id);
运行结果:
 
提取程序:
clear  %清空变量
% 利用Matlab自带函数读取隐密图像stego. bmp,得到隐密图像的信息并将图像转化为二进制
Picture = imread( 'stego1.bmp' );
Picture = double(Picture);
[m, n] = size(Picture);
% 打开存放秘密信息的文件,若没有则新建一个文件。顺序提取图像相应像素LSB的秘密信息,存储在打开的文件中并保存
frr = fopen('message.jpg', 'w');
len = 227680;
%test = [];
p = 1;
for f2 = 1:n
    for f1 = 1:m
        %lowbit = bitand(Picture(f1, f2), 1);
        %test = [test lowbit]; % 注意!如果做数组cat操作,会严重增加提取程序运行时长
        fwrite(frr, bitand(Picture(f1, f2), 1), 'ubit1');
        if p == len
            break;
        end
        p=p+1;
    end
    if p == len
        break;
    end
end
%fwrite(frr, test);
fclose(frr);
提取结果存储为message.jpg。
注意,不要尝试将内容全部写入一个数组,然后再统一用
fwrite写入,因为matlab的数组cat操作很慢,数据量太大了耗时很长。我多加了一行cat操作,结果给老师检查的时候翻车了qwq。
值对现象展示程序:
I=imread('cover.bmp');
O=imread('stego1.bmp');
I=I(1:200000);
O=O(1:200000);
%subplot(121);
x=100;
histogram(I, 0:1:x);
set(gca, 'xtick', 0:2:x); % 横坐标每隔2显示刻度
grid on;
hold on;
%subplot(122);
histogram(O, 0:1:x);
set(gca, 'xtick', 0:2:x); % 横坐标每隔2显示刻度
grid on;
值对现象运行结果:
 
 上图中,蓝色是嵌入前,橙色是嵌入后。现象极其明显。
DCT域隐写
实验要求,完成3种嵌入方案:JSTEG、F4和F5。
载体选取:JPEG格式灰度图像。
 嵌入信息:随机生成的0-1比特流。
 嵌入长度:JSteg:28684bits;F4:45000bits;F5:32700bits。(长度与图像本身能够用于嵌入的AC系数个数、算法对AC系数的利用率有关)
 嵌入思路:
 
本实验使用jpeg_toolbox库对JPEG图像进行读写操作,因此,若jpeg_toolbox文件夹尚未添加到当前路径,将会出现如下报错:
 
 解决办法:右键文件夹-添加到路径-选定的文件夹。
 
JSteg
JSteg信息隐藏算法是LSB替换思想在DCT域的实现。
 嵌入过程的关键步骤:将原始图像的AC系数中最低的位平面“替换”为要隐藏的秘密信息。这里的“替换”遵循如下规则:
 (1)忽略-1、0、1;
 (2)若AC系数为2i,秘密比特为0,该系数不变;
 (3)若AC系数为2i,秘密比特为1,该系数变为2i+1;
 (4)若AC系数为2i+1,秘密比特为0,该系数变为2i;
 (5)若AC系数为2i+1,秘密比特为1,该系数不变。
 (6)AC系数为负数时,其二进制的实际含义是正数。(如表2-1所示)
 
 在本课程中,DCT系数为负时,认为它是相应正数的补码。
 
因此,负数的奇偶性和其他正常的JSteg说法不一样。
 如,AC系数为(-16)₁₀ = (01111)₂。当秘密比特为0时,载密系数是(-17)₁₀ = (01110)₂。
 
嵌入程序stego_JSteg.m:
% 更改嵌入算法时,需要将下文JSteg_simulation替换成其他函数
% 并修改算法名称name、嵌入信息长度messageLen等变量
COVER='cover.jpg';
STEGO='stego.jpg';
name='JSteg';
messageLen=28684;
message=randi([0 1],1,messageLen);%0000); %生成随机数,作为隐藏信息
save('message','message','-ascii'); %保存秘密信息
tic;
[nzAC]=JSteg_simulation(COVER,STEGO,message);
T=toc;
fprintf('-----------------------------------\n');
fprintf('%s simulation finished\n', name);
fprintf('cover image: %s\n',COVER);
fprintf('stego image: %s\n',STEGO);
fprintf('number of nzACs in cover: %i\n',nzAC);
fprintf('bits of message: %d\n',length(message));
fprintf('elapsed time: %.4f seconds\n',T);
fprintf('-----------------------------------\n');
嵌入程序调用的函数JSteg_simulation.m:
function [ncAC]=JSteg_simulation(COVER,STEGO,message)
try
	jobj=jpeg_read(COVER);	%读取cover图片
	PrimeDCT=jobj.coef_arrays{1};%读取DCT系数
    DCT=PrimeDCT;
catch
	error('ERROR (problem with the cover image)');
end
AC_Location=DCT; % 复制DCT
AC_Location(1:8:end,1:8:end)=false; % 将DC系数置0
AC_Location(abs(AC_Location)<=1)=0; % 将绝对值小于等于1的位置置为0
AC_Location=find(AC_Location);	%找出DCT中不为DC系数、绝对值大于1的位置
ncAC=numel(AC_Location);	% 得到绝对值大于1的AC系数个数
messageLen=length(message);
%信息过长
if(messageLen>ncAC)
	error('ERROR (too long message)');
end
i_DCT=1;
for i_MSG=1:messageLen
    if(i_DCT>ncAC) % 没有更多可供注入密文的AC系数
        fprintf('Max messageLength is %d.\n', i_MSG-1);
        error('ERROR (too long message)');
    end
    DCTInfo=DCT(AC_Location(i_DCT));
    if(DCTInfo>0)
        DCT(AC_Location(i_DCT))=DCTInfo+message(i_MSG)-mod(DCTInfo,2);
    else
        DCT(AC_Location(i_DCT))=DCTInfo+message(i_MSG)-mod(DCTInfo+1,2);
    end
    i_DCT=i_DCT+1;
end
%%% save the resulting stego image
try
    jobj.coef_arrays{1} = DCT;
    jobj.optimize_coding = 1;
    jpeg_write(jobj,STEGO);
catch
    error('ERROR (problem with saving the stego image)');
end
%显示图像
subplot(2,2,1);imshow(COVER);
title('未嵌入信息的图像');
subplot(2,2,2);histogram(PrimeDCT);axis([-10,10,0,2*1e4]);
title('嵌入前的图像DCT系数直方图');
subplot(2,2,3);imshow(STEGO);
title('嵌入信息的图像');
subplot(2,2,4);histogram(DCT);axis([-10,10,0,2*1e4]);
title('嵌入后的图像DCT系数直方图');
运行结果:
 
提取程序extract_JSteg.m:
% 更改提取算法时,需要将下文JSteg_extract替换成其他函数
% 并修改算法名称name、嵌入信息长度messageLen等变量
STEGO='stego.jpg';
name='JSteg';
messageLen=28684;
tic;
messageHiden=JSteg_extract(STEGO,messageLen);
T=toc;
save('messageHiden','messageHiden','-ascii'); %保存提取出来的秘密信息
fprintf('-----------------------------------\n');
fprintf('%s extract finished\n', name);
fprintf('elapsed time: %.4f seconds\n',T);
fprintf('-----------------------------------\n');
提取程序调用的提取函数JSteg_extract.m:
function message=JSteg_extract(STEGO,messageLen)
try
	jobj=jpeg_read(STEGO);	%读取stego图片
	DCT=jobj.coef_arrays{1};%读取DCT系数
catch
	error('ERROR (problem with the cover image)');
end
AC_Location=DCT; % 复制DCT
AC_Location(1:8:end,1:8:end)=false; % 将DC系数置0
AC_Location(abs(AC_Location)<=1)=0; % 将绝对值小于等于1的位置置为0
AC_Location=find(AC_Location);	%找出DCT中不为DC系数、绝对值大于1的位置
i_DCT=1;
for i_MSG=1:messageLen
    DCTInfo=DCT(AC_Location(i_DCT));
    if(DCTInfo>0)
        message(1,i_MSG)=mod(DCTInfo,2);
    else
        message(1,i_MSG)=mod(DCTInfo+1,2);
    end
    i_DCT=i_DCT+1;
end
后面的两个算法不贴出完整代码,代码仓库里面都有。也懒得解释实验现象了,实验报告都给得很清楚。
简单介绍一下剩下两个实验的原理吧。
F4
F4信息隐藏算法是JSteg的改进。

在F4算法中,当嵌入系数的最低位和密文不符时,将系数的绝对值一律减1,而非对最低位直接取反,从而较好地避免了值对现象。
它的替换,和JSteg的区别是:
①它对1和-1也进行操作。当嵌入系数的最低位和密文不符时,1和-1都将变成0。此时,该位的信息嵌入被视为无效,需要继续取下一位AC系数进行嵌入。
②嵌入时,统一直接减1。如原值是6,对应二进制是110,在JSteg中,嵌入后是111;在F4中,嵌入后是101。
体现出来的变换规则如下表2-2:
 
F5
F5的特点是矩阵编码,能够减小数据的修改量,并置乱DCT系数(本实验无置乱要求)。

矩阵编码:将k比特秘密消息嵌入到(2^k-1)个AC系数中,只修改1个位置。
其实,它的原理就和海明码纠错原理一样。
海明码(也叫汉明码)具有一位纠错能力。和海明码有些不同的是,矩阵编码不要求将AC系数计算得到的校验码添加到AC系数中去纠错,而是以计算得到的校验码作为秘密信息。
假设一共有H1,H2,H3,H4,H5,H6,...,H15这十五个数,它们的位置对应着二进制0001,0010,0011,…,1111。
将所有二进制最低位为1位置的数选出来异或,也就是b1=H1⊕H3⊕H5⊕H7⊕H9⊕H11⊕H13⊕H15,能得到1比特信息。
 同理,将所有二进制第二位为1位置的数选出来,也就是b2=H2⊕H3⊕H6⊕H7⊕H10⊕H9⊕H9,也能得到1比特信息。
 …
 综上,一共能得到4比特信息。
倘若这4比特和4比特密文相符合,那就不用改密文。
 假如不同,b1、b2、b3、b4的某些与密文不符合的可能组合一共有15种,这15种组合分别对应着需要修改的AC系数的位置。如下图所示。
 
 这就是F5的修改原则——每(2^k-1)位AC系数,只需要最多改1位,就能得到k比特的密文信息。其中AC系数的分组大小可自行选择,修改的方式与F4保持一致,也是绝对值直接减1。
在本实验中,我选择以3个AC系数为一组,一次嵌入密文比特量k为2,分别定义为a1,a2,a3,b1,b2。嵌入过程的关键步骤是矩阵编码,遵循如下规则:
 (1)忽略0;
 (2)当系数被修改成0时,换位置重新嵌入。
 (3)b1=a1⊕a2, b2=a2⊕a3,则不修改数据;
 (4)b1≠a1⊕a2, b2=a2⊕a3,则修改a1;
 (5)b1=a1⊕a2, b2≠a2⊕a3,则修改a3;
 (6)b1≠a1⊕a2, b2≠a2⊕a3,则修改a2;
 (7)正奇数和负偶数代表秘密消息1,正偶数和负奇数代表秘密消息0;
 (8)AC系数为负数时,其二进制的实际含义是正数。
 提取秘密消息时,只需令b1=a1⊕a2, b2=a2⊕a3。
简单贴一下F5的信息隐藏和提取关键代码:
信息隐藏的关键代码:
AC_Location=DCT; % 复制DCT
AC_Location(1:8:end,1:8:end)=false; % 将DC系数置0
AC_Location=find(AC_Location);	%找出DCT中不为DC系数、不为0的位置
ncAC=numel(AC_Location);	% 得到AC系数总数
messageLen=length(message); % 得到密文的数量
%信息过长
if(messageLen/2>ncAC/3)
	error('ERROR (too long message)');
end
i_DCT=1;
i_MSG=1;
while i_MSG+1<=messageLen
    if(i_DCT+2>ncAC) % 没有更多可供注入密文的AC系数
        fprintf('Max messageLength is %d.\n', i_MSG-2);
        error('ERROR (too long message)');
    end
    DCTInfo=DCT(AC_Location(i_DCT:i_DCT+2));
    DCTInfo(DCTInfo<0)=DCTInfo(DCTInfo<0)+1; % 将所有负数+1,改变最低位
    xor1=bitxor(mod(DCTInfo(1),2),mod(DCTInfo(2),2)); % a1按位异或a2
    xor2=bitxor(mod(DCTInfo(2),2),mod(DCTInfo(3),2)); % a2按位异或a3
    tobe_change=i_DCT;
    if(message(i_MSG)==xor1) % 判断异或值,确定修改位
        if(message(i_MSG+1)==xor2)
            tobe_change=-1;
        else
            tobe_change=tobe_change+2;
        end
    else
        if(message(i_MSG+1)~=xor2)
            tobe_change=tobe_change+1;
        end
    end
    if(tobe_change~=-1) % 需要修改
        if(DCT(AC_Location(tobe_change))>0) % 正数减1
            DCT(AC_Location(tobe_change))=DCT(AC_Location(tobe_change))-1;
        else % 负数加1
            DCT(AC_Location(tobe_change))=DCT(AC_Location(tobe_change))+1;
        end
        if(DCT(AC_Location(tobe_change))==0) % 如果嵌入后是0,这两位密文重新嵌入
            i_MSG=i_MSG-2;
            AC_Location(tobe_change)=[]; % 删除这一位置
            ncAC=ncAC-1;
            i_DCT=i_DCT-3; % 复用未被修改的AC系数
        end
    end
    i_DCT=i_DCT+3;
    i_MSG=i_MSG+2; % for循环内循环索引改变不生效
end
若AC系数分组较大,如选择分组大小为15,则不建议继续采用
if-else写“确定修改位”部分,而应该反向打表。
首先将异或值不同的位按序直接转换成二进制,比如b1,b2,b3不同,应直接计算得到1110,然后查1110对应的是ACT系数7,则直接修改第七个ACT系数。
提取的关键代码:
AC_Location=DCT; % 复制DCT
AC_Location(1:8:end,1:8:end)=false; % 将DC系数置0
AC_Location=find(AC_Location);	%找出DCT中不为DC系数、不为0的位置
i_DCT=1;
for i_MSG=1:2:messageLen
	DCTInfo=DCT(AC_Location(i_DCT:i_DCT+2));
    DCTInfo(DCTInfo<0)=DCTInfo(DCTInfo<0)+1; % 将所有负数+1后,改变最低位
    message(1,i_MSG)=bitxor(mod(DCTInfo(1),2),mod(DCTInfo(2),2)); % a1按位异或a2
    message(1,i_MSG+1)=bitxor(mod(DCTInfo(2),2),mod(DCTInfo(3),2)); % a2按位异或a3
    i_DCT=i_DCT+3;
end
可以看到,提取算法非常简单。其实嵌入算法也是很简单的,就是在F4的基础上,加了一个异或判断修改位。
【HUST】网安|多媒体数据安全实验|LSB隐写和DCT域JSTEG+F4+F5隐写及检测的更多相关文章
- 轻松月薪过万,NISP证书含金量有多重|NISP管理中心|网安伴|nisp
		nisp一级证书含金量 NISP一级证书是面向各个行业工作人员信息安全意识普及化和网络信息安全基础培训的国家级验证.持NISP一级证书可以从信息安全保密较高的单位得到加分.证书由中国信息安全测评中心授 ... 
- ssh远程端口转发&&windows系统提权之信息收集&&网安工具分享(部分)
		一.ssh远程端口转发 背景:当我们在渗透过程中,获取到内网的一台仅有内网IP的服务器后,我们可以通过ssh隧道,将内网某个主机的端口进行远程转发 1.网络拓扑图 假设获取的服务器为web服务器,we ... 
- 想学渗透测试,应该考CISP-PTE还是NISP-PT?|网安伴nisp和cisp
		其实两者都可,但要看考生的实际需求! 为什么说两者都可以? 两个证书都由中国信息安全测评中心颁发,CISP-PTE全称国家注册渗透测试工程师,NISP-PT全称国家信息安全水平考试-渗透测试工程师专项 ... 
- 2022年NISP考试时间|NISP一级考试时间|NISP|网安伴|NISP管理中心
		NISP一级~~国家信息安全水平考试一级证书 NISP一级证书是由中国信息安全测评中心颁发的国家级认证证书.面向全社会各行各业通用的信息安全意识普及和信息安全保护知识培训,是在任何单位和工作中都应具备 ... 
- F5隐写工具使用
		0x00 前言 今天在实验吧看到一个图片隐写的题目,用了stegslove和winHex分析一通发现并没有什么有效信息.看了评论区大佬的提示说用到了F5隐写工具,所以百度教程用了一下,发现确实解决 ... 
- 用Python做2048游戏  网易云课堂配套实验课。通过GUI来体验编程的乐趣。
		第1节 认识wxpython 第2节 画几个形状 第3节 再做个计算器 第4节 最后实现个2048游戏 实验1-认识wxpython 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiy ... 
- 2019 第二届 科成安洵杯 官方WriteUp -17网安
		长文预警:对应源码请加企鹅群获取:861677907 0x01 WEB 1.1 勇闯贪吃蛇大冒险 一进去就看出来是一道web页面JS的小游戏,提示说输入CDUESTC CTF即可闯关成功,但是存在着d ... 
- 全国高校网安联赛Web专场~WriteUp
		1.Sign 题目:Good Luck!flag{X-nuca@GoodLuck!} Flag直接写在题目上了,flag{X-nuca@GoodLuck!} 2.BaseCoding 提示:这是编码不 ... 
- 360网安学习笔记——Web安全原理与实践
		网络安全 基本技能: 1.编程语言 2.计算机网络 3.操作系统 4.office 专业技能 1.web安全 2.网络安全 3.渗透测试 4.代码审计 能力提升 1.书籍 2.站点 3.安全平台 We ... 
- 网安日记③之通过iis搭建ftp并使用通过serv-u搭建ftp
		通过iis搭建ftp并使用通过serv-u搭建ftp 安装iis的ftp访问 由于在安装iis时勾选了ftp服务,我们直接在iis界面右键ftp服务打开属性查看本地路径 在电脑目录下打开安装目录,并在 ... 
随机推荐
- Linux嵌入式设备怎么确定网络端口的速率
			Linux嵌入式设备怎么确定网络端口的速率 突发奇想,就是Linux下面我能不能查询到端口的速率,以此来判断要不要频繁的发送网络数据包呢? 或者更换包利用率更高的协议呢. 于是抱着这样的想法,我开始学 ... 
- QT5笔记:6. QT 与 C++
			QT 对标准的C++进行了扩展,引入了一些新的概念和功能 QT 的元对象编译器(Meta-Object Compiler, MOC)是一个预处理器,它预处理QT项目,先将QT的一些特性代码转换为标准的 ... 
- Zoom视频会议软件使用指南
			引言 在远程工作和在线教育日益普及的今天,Zoom视频会议软件已成为全球数百万用户沟通协作的首选工具.以其稳定的连接.清晰的音视频质量和便捷的操作界面,Zoom极大地促进了跨地域的实时交流.本文将为您 ... 
- 从零开始!Jupyter Notebook的安装详细教程
			本文将引导你完成从零开始安装Jupyter Notebook的过程.Jupyter Notebook是一个开源的Web应用程序,允许用户创建和共享包含实时代码.方程.可视化和叙述文本的文档.它广泛应用 ... 
- 大量小文件不适合存储于HDFS的原因
			1.小文件过多,会过多占用namenode的内存,并浪费block. - 文件的元数据(包括文件被分成了哪些blocks,每个block存储在哪些服务器的哪个block块上),都是存储在namenod ... 
- Flink学习(二) 应用场景和架构模型
			实时计算最好的时代 在过去的十年里,面向数据时代的实时计算技术接踵而至.从我们最初认识的 Storm,再到 Spark 的异军突起,迅速占领了整个实时计算领域.直到 2019 年 1 月底,阿里巴巴内 ... 
- postman 如何比较两台电脑的脚本是否一样
- selenium自动化测试-获取动态页面小说
			有的网站页面是动态加载的资源,使用bs4库只能获取静态页面内容,无法获取动态页面内容,通过selenium自动化测试工具可以获取动态页面内容. 参考之前的"bs4库爬取小说工具"文 ... 
- SpringBoot前后端接口加解密--解决方案
			开放接口 - 通信方式采用HTTP+JSON或消息中间件进行通信. - 调用接口之前需要使用登录鉴权接口获得token. - 当鉴权成功之后才能调用其他接口(携带Token). 登录接口: Code ... 
- linux怎么关闭selinux
			关闭方法:1.临时关闭,只需执行"setenforce 0"命令即可.2.永久关闭,需要执行"vi /etc/selinux/config"命令打开config ... 
