Qt5 最低有效位隐写LSB

6 篇文章 0 订阅

LSB基本原理

LSB隐写LSB全称least significant bit,最低有效位PNG文件中的图像像素一般是由RGB三原色(红绿蓝)组成,每一种颜色占用8位,取值范围为0x00~0xFF,即有256种颜色,一共包含了256的3次方的颜色,即16777216 种颜色人类的眼睛可以区分约1000万种不同的颜色这意味着人类的眼睛无法区分余下的颜色大约有6777216种LSB隐写就是修改RGB颜色分量的最低二进制位(LSB),而人类的眼睛不会注意到这前后的变化,所以可以利用这一特性进行信息隐藏

载体图片选择

图片选择 png图片是一种无损压缩的位图片形格式,也只有在无损压缩或者无压缩的图片(BMP)上实现lsb隐写。如果图像是jpg图片的话,就没法使用lsb隐写了,原因是jpg图片对像数进行了有损压缩,我们修改的信息就可能会在压缩的过程中被破坏。而png图片虽然也有压缩,但却是无损压缩,这样我们修改的信息也就能得到正确的表达,不至于丢失。BMP的图片也是一样的,是没有经过压缩的。BMP图片一般是特别的大的,因为BMP把所有的像数都按原样储存,没有进行压缩。

LSB隐写示例

例如想把字符’a’隐藏起来,可以将其转换成16进制的数字0x61,然后转换成二进制的01100001,将其隐藏到红色通道中每一个颜色的最低位
在这里插入图片描述

我的实现思路

隐藏秘密信息:

1.对秘密信息编码,转换为由01组合而成的字符串
2.把编码后的生成的01字符串写入图片R通道的最低位

提取秘密信息:

1.提取R通道的最低位,还原出01字符串
2.根据编码规则由01字符串还原出秘密信息

具体编码规则

**编码(**没有使用ASCII码)
//密钥数组,里面保存a-z,A-Z,还有标点符号们,某个字符的index就是那个字符对应的编码,改变字符在数组中的位置就会实现加密,这个数组相当于一个密钥
QString _keyArray;
index:0123456789…
_keyArray=“abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.!?”;

abc编码为:000000000001000010,前8位用来存字符个数abc是3
实际存储的是00000011000000000001000010

解码
根据前8bit 00000011记录的字符个数3,计算出存储的字符串长度3*6,提取出:000000000001000010 也就是000000 000001 000010
对应:abc
index:0123456789…
_keyArray=“abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.!?”;

安全性(只有使用与编码时用的_KeyArray才可以正确解码)
改变_keyArray如下, 则000000000010000011就会解码为def
_keyArray=“defabcghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ
RSTUVWXYZ ,.!?”;
_KeyArray相当于一个对称密钥

项目构成在这里插入图片描述

需要放在运行目录的载体图片
在这里插入图片描述

程序运行

startwindow
在这里插入图片描述
mainwindow
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
lsbdecode
lsbdecode
在这里插入图片描述
在这里插入图片描述

源代码

startwindow.h

#ifndef STARTWINDOW_H
#define STARTWINDOW_H
#include "mainwindow.h"
#include "lsbdecode.h"

#include <QMainWindow>
#include <QString>
#include <QFileDialog>

namespace Ui {
class startwindow;
}

class startwindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit startwindow(QWidget *parent = 0);
    ~startwindow();

private slots:
    void on_ptn_gotocreate_clicked();

    void on_ptn_gotodecode_clicked();

private:
    Ui::startwindow *ui;
    lsbdecode *_LSB;
    MainWindow *_MWD;
};

#endif // STARTWINDOW_H

startwindow.cpp

#include "startwindow.h"
#include "ui_startwindow.h"

startwindow::startwindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::startwindow)
{
    ui->setupUi(this);
}

startwindow::~startwindow()
{
    delete this->_LSB;
    delete this->_MWD;
    delete ui;
}

void startwindow::on_ptn_gotocreate_clicked()
{
    this->_MWD=new MainWindow;
    _MWD->show();
}

void startwindow::on_ptn_gotodecode_clicked()
{
    this->_LSB=new lsbdecode;
    _LSB->show();

}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QImage>
#include <QPixmap>
#include <QDebug>
#include <QColor>
#include <QRgb>
#include <QString>
#include <QTime>
#include <QMessageBox>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    void WritePicture(QString keystring);//把明文编码后生成的01字符串(_KeyString)写入图片
    ~MainWindow();

private slots:
    void on_ptb_create_clicked();

private:
    Ui::MainWindow *ui;
    QImage _srcimg;
    QImage _oldimg;
    QImage _newimg;
    QPixmap _mypixmap;
    QString _imgAddr;//图片地址
    //密钥数组,里面保存a-z,A-Z,还有标点符号们,某个字符的index就是那个字符对应的编码,改变字符在数组中的位置就会实现加密,这个数组相当于一个密钥
    QString _keyArray;
    QString  _keyString;//编码后的总密文
};

#endif // MAINWGEINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //初始化密钥数组26+26+‘ ’+‘,’+'.'+'?'+'!'=57,一个字符需要6位二进制位表示,比如'c'的编码就是2(000010),可以通过打乱数组里面的顺序来加强安全性
    _keyArray="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.!?";

    _imgAddr="bigear.bmp";
    if(!_srcimg.load(_imgAddr))
    {
        qDebug()<<"图片加载失败";
    }

    //相片统一为578*578,本程序只使用rbg中的r通道最低的一位存储秘密信息,6(每个字符的二进制位)*95=570,570+8=578;
    //也就是本程序设置为最大存储量为95个字符
    _srcimg=_srcimg.scaled(ui->lbl_img->width(),ui->lbl_img->height());
    _oldimg=_srcimg;
    _newimg=_srcimg;

    ui->lbl_img->setPixmap(QPixmap::fromImage(_srcimg));

}

//把明文编码后生成的01字符串(_KeyString)写入图片的第一行的Rgb的R通道的最低位
void MainWindow:: WritePicture(QString keystring)
{
    //这里其实应该验证一下keystring的长度
    if(keystring.size()>_oldimg.width())//写不下了,程序只给了一行的r通道用来写
     {
             QMessageBox::information(NULL,"错误","本程序只支持存储95个字符",QMessageBox::Cancel);

     }

    else
     {
          for(int i=0;i<keystring.size();i++)//把每一位写入图片
          {
              int R,G,B,newR;
              QColor pixelcolor=_oldimg.pixel(i,0);//写一行,横着写
              R=pixelcolor.red();
              G=pixelcolor.green();
              B=pixelcolor.blue();

              //改变r的最后一位无非也就是改变最后一位为0还是1
              if(R%2==0)//这一点的R本身是偶数(最后一位是0)
              {
                  if(keystring[i]=='1')//要写1
                      newR=R+1;
                  if(keystring[i]=='0')//要写0
                      newR=R;
              }
              else//R本身是奇数(最后一位是1)
              {
                  if(keystring[i]=='1')//要写1
                      newR=R;
                  if(keystring[i]=='0')//要写0
                      newR=R-1;
              }

              _oldimg.setPixelColor(i,0,QColor(newR,G,B));
          }
        }
 }


void MainWindow::on_ptb_create_clicked()//对明文进行编码和并且写入图片,并且保存图片
{
  QString rawString=ui->ldt_key->text();
  qDebug()<<"明文是:"<<rawString;
  _keyString="";//保存的是编码后的密文
  qDebug()<<"rawString:"<<rawString;
  qDebug()<<"_keyArray:"<<_keyArray;
  for(int i=0;i<rawString.size();i++)//为每一个明文编码
  {
      for(int index=0;index<_keyArray.size();index++)//根据keyArray编码
      {
          if(rawString[i]==_keyArray[index])//找到字符对应的下标index,index就是该字符的编码
          {
              QString temp=QString::number(index,2);//把编码转化为二进制
              switch (temp.size()) //不足6位的补足6bit,方便切割
              {
              case 1:temp="00000"+temp;
                     break;
              case 2:temp="0000"+temp;
                     break;
              case 3:temp="000"+temp;
                     break;
              case 4:temp="00"+temp;
                     break;
              case 5:temp="0"+temp;
                     break;
              default:
                  break;
              }
              _keyString=_keyString+temp;
              break;//编码下一个字符
          }
      }
  }

  //记录字符数目,添加在_KeyString的开头
    QString charallcount=QString::number(_keyString.size()/6,2);//转换为二进制字符串

    switch (charallcount.size()) //用头8位代表明文的字符数,以便于提取密文时确定密文在图片的哪里结束
    {
    case 1:charallcount="0000000"+charallcount;
           break;
    case 2:charallcount="000000"+charallcount;
           break;
    case 3:charallcount="00000"+charallcount;
           break;
    case 4:charallcount="0000"+charallcount;
           break;
    case 5:charallcount="000"+charallcount;
           break;
    case 6:charallcount="00"+charallcount;
           break;
    case 7:charallcount="0"+charallcount;
           break;
    default:
        break;
    }

    qDebug()<<"字符数目:"<<charallcount<<endl;
    qDebug()<<"密文:"<<_keyString;
    _keyString=charallcount+_keyString;//字符个数(8bit)+编码后的字符
    qDebug()<<"字符数目+密文:"<<_keyString;

    WritePicture(_keyString);//把_keyString写入图片
    if(_oldimg.save("newbigear.bmp"))
    {
        QMessageBox::information(NULL,QString("提示"),QString("秘密图片已经保存到运行路径下"),QMessageBox::Ok);

    }
    else
    {
        QMessageBox::information(NULL,QString("提示"),QString("保存失败"),QMessageBox::Ok);
    }



}

MainWindow::~MainWindow()
{
    delete ui;
}



lsbdecode.h

#ifndef LSBDECODE_H
#define LSBDECODE_H

#include <QMainWindow>
#include <QImage>
#include <QPixmap>
#include <QDebug>
#include <QColor>
#include <QRgb>
#include <QString>
#include <QTime>
#include <QMessageBox>
#include <cmath>
#include <QByteArray>
#include <QFileDialog>
namespace Ui {
class lsbdecode;
}

class lsbdecode : public QMainWindow
{
    Q_OBJECT

public:
    explicit lsbdecode(QWidget *parent = 0);
    QString Decode(QImage newimg);//从图片中提取密文01序列
    QString getraw(QString rawbyte);//根据密文01序列,依据_keyArray转换出原文
    ~lsbdecode();

private:
    Ui::lsbdecode *ui;
    QImage _newimg;
    QString _imgAddr;//图片地址,public是为了startwindow可以选择要破解的图片
    QPixmap _mypixmap;
    //QString _imgAddr;//图片地址
    //密钥数组,里面保存a-z,A-Z,还有标点符号们,某个字符的index就是那个字符对应的编码,改变字符在数组中的位置就会实现加密,这个数组相当于一个密钥
    QString _keyArray;
    QString  _rawbyte;//编码后的总密文
    QString  _raw;//还原出来的原文

};

#endif // LSBDECODE_H

lsbdecode.cpp

#include "lsbdecode.h"
#include "ui_lsbdecode.h"

lsbdecode::lsbdecode(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::lsbdecode)
{
    ui->setupUi(this);

    //初始化密钥数组26+26+‘ ’+‘,’+'.'+'?'+'!'=57,一个字符需要6位二进制位表示,比如'c'的编码就是3,可以通过打乱数组里面的顺序来加强安全性
    _keyArray="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.!?";

    QString pictureaddr=QFileDialog::getOpenFileName(this,"选择图片","C:\\","*.png *.bmp");
    //_imgAddr="newbigear.bmp";//存储着秘密的图片
    _imgAddr=pictureaddr;
       if(!_newimg.load(_imgAddr))
       {
           qDebug()<<"图片加载失败";
       }

       //相片统一为578*578,本程序只使用rbg中的r通道最低的一位存储秘密信息,6(每个字符的二进制位)*95=570,570+8=578;
       //也就是本程序设置为最大存储量为95个字符
       _newimg=_newimg.scaled(ui->lbl_decodeimg->width(),ui->lbl_decodeimg->height());
       ui->lbl_decodeimg->setPixmap(QPixmap::fromImage(_newimg));
       _rawbyte=lsbdecode::Decode(_newimg);
       qDebug()<<"密文:"<<_rawbyte;
       _raw=lsbdecode::getraw(_rawbyte);
       qDebug()<<"提取出的内容:"<<_raw;

       ui->ldt_result->setText(_raw);

}



QString lsbdecode::Decode(QImage newimg)
{
    QString charallcount="";//取出前8个像素点的RGB的R的最后一位,得到密文总字符数,密文长度=密文总字符数*6
    for(int i=0;i<8;i++)
    {
        QColor color=newimg.pixel(i,0);
        int R=color.red();
        if(R%2==0)
            charallcount=charallcount+"0";
        else
            charallcount=charallcount+"1";
    }

    qDebug()<<"前八位R8bit:"<<charallcount;
    int allcount=0;

    for(int i=0;i<8;i++)//得到密文的字符数
    {
        if(charallcount[i]=='1')
         allcount=allcount+pow(2,7-i);
    }

    QString raw="";//保存提取出来的原文
    qDebug()<<"总字符数"<<allcount;
    for(int start=8;start<8+allcount*6;start++)//每个字符6位,提取出密文串
    {
        QColor color=newimg.pixel(start,0);
        int R=color.red();
        if(R%2==1)
            raw=raw+"1";
        else
            raw=raw+"0";
    }
    return raw;
}


QString lsbdecode::getraw(QString rawbyte)//根据密文01序列,依据_keyArray转换出原文
{
    QString raw="";//最终的还原结果
    int count=rawbyte.size()/6;//总字符个数
    QString *charbytecode=new QString[count];
    int nowcount=0;//当前已经截取出来的个数
    while(nowcount!=count)//直到全部截取出来
    {
       charbytecode[nowcount]=rawbyte.mid(nowcount*6,6);//从nowcount往后切割6个,也就是一个字符的编码
        nowcount++;
    }

    for(int i=0;i<count;i++)
    {
        qDebug()<<charbytecode[i];
    }

    int *indexArray=new int[count];
    for(int i=0;i<count;i++)
    {   indexArray[i]=0;
        QString tempsix=charbytecode[i];
        for(int j=0;j<6;j++)
        {
            if(tempsix[j]=='1')
                indexArray[i]=indexArray[i]+pow(2,5-j);
        }
    }

    for(int i=0;i<count;i++)
    {
        raw=raw+_keyArray[indexArray[i]];
    }
    return raw;
}

lsbdecode::~lsbdecode()
{
    delete ui;
}

main.cpp

#include "mainwindow.h"
#include "lsbdecode.h"
#include "startwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    /*MainWindow w;
    w.show();
    lsbdecode l;
    l.show();*/
    startwindow s;
    s.show();

    return a.exec();
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值