BMP圖像信息隱藏


圖像隱寫算法LSB—Least Significant Bits,又稱最不顯著位。LSB算法就是將秘密信息嵌入到載體圖像像素值得最低有效位,改變這一位置對載體圖像的品質影響最小。

原理如下:

 

以實驗用的24位真彩圖為例,每個像素用3Byte表示,每Byte分別表示R、G、B三色的亮度,亮度取值范圍位0~0xFF。采用LSB算法就是將圖像信息的每一Byte的最后一位二進制替換為待嵌入的秘密信息的一位,按順序進行。因為對最后一位的替換操作其實就是對亮度信息的加一或減一,對顏色影響甚微,所以肉眼難以察覺,這就達到了隱藏信息的目的。對於24位真彩圖,所能嵌入的最大秘密信息的大小為像素數量的3/8字節

 

步驟如下:

  1. 以二進制的方式讀取載體圖像並分別頭數據與像素數據
  2. 用二進制秘密信息中的每一筆特信息替換與之對應的載體數據的最低有效位
  3. 利用得到的新的二進制數據構造圖像,即得到含有秘密信息的隱秘圖像
/***
change.c
***/
#include<stdio.h>
#include<stdlib.h>
#include"define.h"

int main()
{
    
    //創建頭文件,信息頭結構變量
    BMP_FILE_HEADER fileHeader;
    BMP_INFO_HEADER infoHeader;
    
    
    //打開載體圖像文件,新建修改后的圖像文件
    FILE *file = fopen("football.bmp","rb");
    FILE *newFile = fopen("change.bmp","wbx");
    
    //讀取文件頭,信息頭
    fread(&fileHeader,14,1,file);
    fread(&infoHeader,40,1,file);
    
    //讀取24位真彩圖像的像素信息
    RGB *img = (RGB *)malloc(infoHeader.sizeImage);
    fread(img,infoHeader.sizeImage,1,file);

    printf("Picture Size(width x height):%d x %d \n", infoHeader.width, infoHeader.height);

    //對圖片內容進行修改,每隔五個像素染黑一個像素
    int i = 0;
    for (i = 0; i < infoHeader.sizeImage / 3; i += 5)
    {
        img[i].red = 0;
        img[i].green = 0;
        img[i].blue = 0;
    }
    printf("fwirte\n");

    //將新的二進制文件寫入到新文件中
    fwrite(&fileHeader,14,1,newFile);
    fwrite(&infoHeader,40,1,newFile);
    fwrite(img,infoHeader.sizeImage,1,newFile);

    fclose(file);
    fclose(newFile);
    return 0;
}

修改圖像內容的核心代碼如下:

int i = 0;
for (i = 0; i < infoHeader.sizeImage / 3; i += 5)
{
    img[i].red = 0;
    img[i].green = 0;
    img[i].blue = 0;
}

infoHeader.sizeImage表示圖像數據的字節數,在24位真彩位圖中,表示每個像素需要三個字節,infoHeader.sizeImage/3表示位圖總像素數

循環體中將每個顏色分量的亮度賦值為0,讓像素變黑,最終得到修改后的圖像。

以上是處理BMP圖像信息的原理。

 

下面通過LSB算法隱藏秘密信息,這里把define.h文件作為秘密信息隱藏,也可以選擇任何大小合適的文件作為秘密信息。

/***
define.c
***/

typedef unsigned short WORD;    //2 byte
typedef unsigned int DWORD;        //4 byte
typedef unsigned char BYTE;        //1    byte


//head of file
typedef struct BMP_FILE_HEADER
{
    WORD    type;
    DWORD    size;
    WORD    reserved1;
    WORD    reserved2;
    DWORD    offBits;
}BMP_FILE_HEADER;


//head of infomation
typedef struct BMP_INFO_HEADER
{
    DWORD    size;
    int        width;
    int        height;
    WORD    planes;
    WORD    bitCount;
    DWORD    compression;
    DWORD    sizeImage;
    int        xPelsPerMeter;
    int        yPelsPerMeter;
    DWORD    colorUsed;
    DWORD    colorImportant;
}BMP_INFO_HEADER;

//RGBQUAD
typedef struct RGBQUAD
{
    BYTE    blue;
    BYTE    green;
    BYTE    red;
    BYTE    reserved;
}RGBQUAD;

//RGB
typedef struct RGB
{
    BYTE    blue;
    BYTE    green;
    BYTE    red;
}RGB;

隱藏信息代碼:

/***
hide.c
***/
#include<stdio.h>
#include<stdlib.h>

#include<sys/stat.h>
#include<unistd.h>

#include"define.h"

//獲取文件大小
int getFileSizeSystemCall(char *strFileName)
{
    struct stat temp;
    stat(strFileName,&temp);
    return temp.st_size;
}

int main()
{
    //創建文件頭,信息頭結構體變量
    BMP_FILE_HEADER fileHeader;
    BMP_INFO_HEADER infoHeader;

    //打開載體圖像文件,讀取文件頭和信息頭,打開隱秘圖像信息
    FILE *file = fopen("football.bmp","rb");
    FILE *newFile = fopen("hide.bmp","wbx");
    fread(&fileHeader,14,1,file);
    fread(&infoHeader,40,1,file);

    //讀取秘密信息文件“define.h”
    int infoSize = getFileSizeSystemCall("define.h");
    printf("info size : %d\n",infoSize);
    BYTE *info = (BYTE *)malloc(infoSize);
    FILE *infoFile = fopen("define.h","rb");
    fread(info,infoSize,1,infoFile);

    //讀取24位真彩圖像像素信息
    BYTE *img = (BYTE *)malloc(infoHeader.sizeImage);
    fread(img,infoHeader.sizeImage,1,file);

    printf("Picture Size (Width x height) : %d x %d\n",infoHeader.width,infoHeader.height);
    printf("Can hide %d byte infomation\n",infoHeader.sizeImage/8);

    //LBS算法實現,把隱秘信息的每一個字節8bit按照低字節到高字節的順序隱藏到像素信息的
    //每8byte中的最低位
    int i = 0,j = 1;
    BYTE tmp = 0x00;
    for(i = 0; i < infoSize; i++)
    {
        for(j = 0; j < 8; j++)
        {
            tmp = info[i] &0x01;
            if(tmp)
            {
                img[i*8+j] = img[i * 8 + j] | 0x01;
            }
            else
            {
                img[i*8+j] = img[i * 8 + j] & 0xfe;
            }
            info[i] = info[i] >> 1;
        }
    }

    //將修改后的二進制數據寫入到隱秘圖像文件中
    fwrite(&fileHeader,14,1,newFile);
    fwrite(&infoHeader,40,1,newFile);
    fwrite(img,infoHeader.sizeImage,1,newFile);

    fclose(file);
    fclose(newFile);
    return 0;
}

LSB算法實現:

int i = 0,j = 1;
BYTE tmp = 0x00;
for(i = 0; i < infoSize; i++)
{
    for(j = 0; j < 8; j++)
    {
        tmp = info[i] &0x01;
        if(tmp)
        {
            img[i*8+j] = img[i * 8 + j] | 0x01;
        }
        else
        {
            img[i*8+j] = img[i * 8 + j] & 0xfe;
        }
        info[i] = info[i] >> 1;
    }
}

info[i]是存儲秘密信息的數組,通過把像素Byte數據與0x01按位或運算使得最后一位為1,通過把像素Byte數據與0xfe(1111,1110)做按位與運算使得最后一位為0(0 & x = 0)

 

stat函數用來獲取指定路徑下文件或文件夾的屬性。路徑可以不指定。

 

使用md5sum命令查看兩張圖片的MD5 HASH值

 

 

再對比一下文件的16進制內容,先使用xxd命令生成16進制內容:

xxd football.bmp > football.hex

xxd hide.bmp > hide.hex

 

再使用sed命令查看對比第五行內容

sed -n 5p football.hex

sed -n 5p hide.hex

 

 

很明顯的可以看到像素數據的每一字節的最后一位發生了變化,驗證了LSB算法的原理。

 

提取信息

/***
extract.c
***/
#include<stdio.h>
#include<stdlib.h>
#include"define.h"

int main()
{
    //創建頭文件,信息頭結構體變量
    BMP_FILE_HEADER fileHeader;
    BMP_INFO_HEADER infoHeader;

    //讀取隱秘圖像文件,創建秘密信息文件
    FILE *file = fopen("hide.bmp","rb");
    FILE *extractFile = fopen("extract.txt","wbx");
    BYTE *info = (BYTE *)malloc(738);

    //讀取頭文件,信息頭
    fread(&fileHeader,14,1,file);
    fread(&infoHeader,40,1,file);

    //讀取24位真彩圖像的像素信息
    BYTE *img = (BYTE *)malloc(infoHeader.sizeImage);
    fread(img,infoHeader.sizeImage,1,file);

    printf("Picture size : %d x %d \n",infoHeader.width,infoHeader.height);

    //信息提取部分,根據秘密信息的長度,依次讀取隱秘圖像信息像素信息的最低bit,憑借成Byte
    int i = 0,j = 0;
    BYTE tmp = 0x00,ttmp = 0x00;
    for(i = 0; i < 738; i++)
    {
        tmp = 0x00;
        for(j = 0; j < 8; j++)
        {
            /*取每8位bit像素信息的最后一位拼接為1Byte的秘密信息*/
            ttmp = img[i*8+j] & 0x01; 
            ttmp = ttmp << j;        //左移j位
            tmp += ttmp;            //每一位累加得到1Byte的tmp值
        }
        info[i] = tmp;
    }

    //將提取的信息寫入到秘密文件中
    fwrite(info,738,1,extractFile);

    fclose(file);
    fclose(extractFile);
    return 0;
}

使用md5sum查看兩個文件是否相同

md5sum define.h extract.txt

 

 

提取過程就是隱藏過程的逆過程,實現原理和隱藏過程類似。需要注意的是,此處使用的文件長度是固定的秘密信息的長度,並且提前知道了隱藏信息包含在了指定圖片中。在實際處理中,在隱藏秘密信息時,往往還需要一個嵌入標識和長度信息,來幫助程序判斷圖片中是否包含秘密信息,並說明秘密信息長度

 

 

拓展

修改 hide.c 並使用手動輸入的數字作為密鑰來規定秘密信息隱藏與載體圖片的起始位置。

修改 extract.c 並根據輸入的秘鑰提取秘密信息

每 Byte 像素信息隱藏 2bit 的秘密信息(MLSB 算法)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM