利用BMP圖片水印技術寫入加密信息

文/圖 [黑防vip]lwt831120
本文主要和大家討論一下BMP圖片水印技術。其實BMP圖片水印技術就是在BMP圖片中寫入編碼後的數據,但寫入數據後的圖片在外觀上和原始圖片沒有區別(只有文件哈希值改變),並能用特定的程序將寫入的信息還原出來。
 
BMP圖片規格
    BMP圖片存放時按照每個像素的RGB信息存儲,每個像素的顏色用一個字節(8bit)存放。BMP圖片的文件頭如圖1和圖2所示。
圖1
圖2
0x42、0x4D表示BMP圖片的標識;0x36、0x09、0x00、0x00表示BMP圖片的文件大小;0x36、0x00、0x00、0x00表示BMP圖片的數據區從第幾個字節開始;0x00、0x02、0x00、0x00表示BMP圖片的像素寬度;0x80、0x01、0x00、0x00表示BMP圖片的像素高度;0x18、0x00、0x00、0x00表示BMP圖片的每個點的像素值。以上這些特殊字節都是記錄BMP圖片信息的地方,是不能進行修改的,這就是BMP圖片文件頭的格式。雖然BMP圖片中的一些特殊字節是我們不能修改的,但是BMP圖片的數據區是可以修改的,數據區如圖3所示,從55字節處開始,其中0xFF、0x00、0x00分別表示RGB信息(0~255)。
圖3
 
程序原理
我實現程序的基本原理就是將要寫入圖片的信息逐一進行二進制編碼後,分別寫入RGB字節的末尾。例如要將a寫入圖片中,a的ASCII碼97,二進制爲0110 0001,原始圖片的第55個字節數據區開始的8個字節的信息分別爲0x00、0xFF、0x00、0x00、0xFF、0x00、0x00和0xFF,將a的二進制編碼寫入(爲方便起見,編碼我採取從右到左的順序寫入),如圖4所示。
圖4
編碼時只需要將二進制寫入RGB的末尾,若1寫入0x00,只需將“0000 | 0001=0001”,此時0x00就變爲0x01;若0寫入0xFF,只需將“11111111 & 11111110=11111110”,此時0xFF就變爲0xFE。以此類推,這樣就可以將a的二進制編碼寫入RGB的末尾了。由於顏色變化僅僅爲0xFF變爲0xFE,肉眼是無法分辨出來的。寫入a後的圖片如圖5所示。但是在進行漢字編碼的時候,由於漢字使用的是Unicode編碼,且由兩個負數組成,所以必須加入漢字標識位。
圖5
寫入代碼實現
void write_information()
{
FILE *fp,*ip; //定義了指向BMP圖片和文本文件的文件指針
int i=0,input_temp[8]={0,0,0,0,0,0,0,0},j,t=0,x,y,text_ch=0;
int Re_size,input_pointer,bmp_size;
unsigned char bmp_data[8];
char input_data;
//打開BMP圖片文件
if((fp=fopen(FileName,"r+b"))==NULL)
{
printf(" -->打開BMP文件錯誤 ");
exit(1);
}
//打開txt文本文件
if((ip=fopen("input.txt","r"))==NULL)
{
printf(" -->打開input文件錯誤 ");
exit(1);
}
//判斷文本文件大小是否能寫入圖片,Re_size爲返回是否寫入判斷值
Re_size=Txt_size();
If(Re_size==0)
{
exit(1);
}
//得到輸入的文本文件的大小,input_pointer爲文件指針的位置
fseek(ip,0,SEEK_END);
input_pointer=ftell(ip);
if(input_pointer==0)
{
printf(" -->input.txt文件中的內容爲空,請寫入數據 ");
exit(1);
}
printf(" -->input.txt文件中現在有:%d個字節 ",input_pointer);
rewind(ip);
//得到BMP圖片的大小,bmp_size爲BMP圖片文件的大小
fseek(fp,0,SEEK_END);
bmp_size=ftell(fp);
rewind(fp);
}
//將文件指針指向數據區
fseek(fp,55,SEEK_SET);
//清除緩存中的數據流(此函數重要,沒有此函數讀數據時會有亂碼)
fflush(fp);
//進入寫入水印數據操作
do
{//讀取BMP圖片中8個字節的信息,並存入一個臨時數組中
for(j=0;j<8;j++)
{
fread(&bmp_data[j],sizeof(unsigned char),1,fp);
}
//將BMP圖片指針回8個字節
fseek(fp,-8,SEEK_CUR);
//讀取Txt文本文件中的數據
fread(&input_data,sizeof(char),1,ip);
//得到Txt文本讀取數據的ASCII值
input_data=(int)input_data;
//判斷是字符還是漢字
if(input_data<0)
{
//漢字標識位
text_ch=-1;
input_data=(input_data*-1);
}
j=7;
//將Txt文本數據轉換爲二進制
do
{
input_temp[j]=input_data%2;
//如果是二進制0,將0寫入RGB字節末尾
if(input_temp[j]==0)
{
bmp_data[t]=(bmp_data[t]&254);
fwrite(&bmp_data[t],sizeof(unsigned char),1,fp);
t++;
}
//如果是二進制1,將1寫入RGB字節末尾
if(input_temp[j]==1)
{
bmp_data[t]=(bmp_data[t]|1);
fwrite(&bmp_data[t],sizeof(unsigned char),1,fp);
t++;
}
input_data=input_data/2;
j--;
}while(input_data!=0);
For(y=0;y<(j+1);y++)
{//處理Unicode編碼的中文漢字,在中文標識位上寫入標識1
if(text_ch==-1&&y==j)
{
bmp_data[t]=(bmp_data[t]|1);
fwrite(&bmp_data[t],sizeof(unsigned char),1,fp);
t++;
text_ch=0;
break;
}
//轉換後的二進制不足八位其餘位補0
bmp_data[t]=(bmp_data[t]&254);
fwrite(&bmp_data[t],sizeof(unsigned char),1,fp);
t++;
}
j=7;
t=0;
fflush(fp);
//恢復臨時文本數組
for(x=0;x<8;x++)
{
input_temp[x]=0;
}
i++;
}while(i!=input_pointer);

讀取代碼實現
void read_information()
{
FILE *fp;
int i,j=55,t,temp=1,k,add=0,show[2]={0,0},inshow,x=0,y;
unsigned char bmp_data[8]={0,0,0,0,0,0,0,0};
if((fp=fopen(FileName,"r+b"))==NULL)
{
printf(" -->打開BMP圖片文件錯誤 ");
exit(1);
}
fseek(fp,55,SEEK_SET);
//清除緩存中的數據流(此函數重要,沒有此函數讀數據時會有亂碼)
fflush(fp);
do
{
//將BMP圖片RGB信息讀取出來
for(i=0;i<8;i++)
{
fread(&bmp_data[i],sizeof(unsigned char),1,fp);
j++;
}
//判斷讀取完畢的條件
if(j>end_pointer)
{
break;
}
//將二進制信息從RGB末字節處取出
for(t=0;t<8;t++)
{
bmp_data[t]=(bmp_data[t]&1);
}
//將二進制信息還原
add=bmp_data[0]*1;
for(k=1;k<7;k++)
{
temp*=2;
add+=(bmp_data[k]*temp);
}
//判斷是否是中文編碼的字符
if(bmp_data[7]==0)
{
inshow=add;
printf("%c",inshow);
}
if(bmp_data[7]==1)
{
show[x]=add;
show[x]=(show[x]*-1);
x++;
}
//還原臨時BMP圖片數組初始值
for(y=0;y<8;y++)
{
bmp_data[y]=0;
}
temp=1;
add=0;
//顯示中文字符
if(x==2)
{
printf("%c%c",show[0],show[1]);
x=0;
}
}while(1);
printf(" -->讀出BMP圖片文件中的信息完成 ");
fcloseall();
}
總結
在BMP圖片中寫入水印,只要從數據區55個字節以後都可以每8個RGB字節末尾寫入二進制信息,如0xFF變爲0xFE(Red值從255變爲254),這樣圖片的變化情況是無法用肉眼來分辨的,只有文件的哈希值會改變,這樣就能將一些加密信息寫入其中了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章