您的位置:首页 > 其它

利用BMP图片水印技术写入加密信息

2013-12-04 17:02 274 查看
本文主要和大家讨论一下BMP图片水印技术。其实BMP图片水印技术就是在BMP图片中写入编码后的数据,但写入数据后的图片在外观上和原始图片没有区别(只有文件哈希值改变),并能用特定的程序将写入的信息还原出来。

BMP图片规格
BMP图片存放时按照每个像素的RGB信息存储,每个像素的颜色用一个字节(8bit)存放。BMP图片的文件头如图1和图2所示。



[align=center]图1[/align]



[align=center]图2[/align]
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)。



[align=center]图3[/align]

程序原理
我实现程序的基本原理就是将要写入图片的信息逐一进行二进制编码后,分别写入RGB字节的末尾。例如要将a写入图片中,a的ASCII码97,二进制为0110 0001,原始图片的第55个字节数据区开始的8个字节的信息分别为0x00、0xFF、0x00、0x00、0xFF、0x00、0x00和0xFF,将a的二进制编码写入(为方便起见,编码我采取从右到左的顺序写入),如图4所示。



[align=center]图4[/align]
编码时只需要将二进制写入RGB的末尾,若1写入0x00,只需将“0000 | 0001=0001”,此时0x00就变为0x01;若0写入0xFF,只需将“11111111 & 11111110=11111110”,此时0xFF就变为0xFE。以此类推,这样就可以将a的二进制编码写入RGB的末尾了。由于颜色变化仅仅为0xFF变为0xFE,肉眼是无法分辨出来的。写入a后的图片如图5所示。但是在进行汉字编码的时候,由于汉字使用的是Unicode编码,且由两个负数组成,所以必须加入汉字标识位。



[align=center]图5[/align]
写入代码实现

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),这样图片的变化情况是无法用肉眼来分辨的,只有文件的哈希值会改变,这样就能将一些加密信息写入其中了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: