前言
网易云上非常有意思的课程,教如何将文件隐藏在BMP位图中。
http://study.163.com/course/courseLearn.htm?courseId=1367011#/learn/video?lessonId=1632135&courseId=1367011
基本知识
Q:文件格式,大端、小端
A:这个非计算机专业,不是很懂。感兴趣的同学可参考 http://blog.csdn.net/goldfighter/article/details/8289215
Q:BMP文件格式
A:前10个字节存放文件信息,包括文件大小。之后每6个字节为一个像素点,包括前三个字节(RGB控制图片颜色显示)和后三个字节(阿尔法值,即透明通道值,控制图片透明度值)。主要利用透明通道的空间来隐藏文件内容。
Q:其他文件格式?
A:其他文件格式也有类似的空间做隐藏。理论上24位BMP位图隐藏空间是图片大小的一半。
注:本文以24位位图为例。16位和256色位图只有1位存放透明通道值,能隐藏的数据量只有1/4。
实现基本思路
1. 隐藏文件内容 Hide(秘密文件名,BMP文件名)
·读取文件内容函数ReadFileContent。其中第二个参数DWORD * filesize有什么用,不太清楚。(待解答)
·读取BMP位图内容存入pBMP,读取secret文件内容存入pSecretFile
·找到第一个像素偏移位置并打印,为第11个字节。即(DWORD *)(pBMP+10)
·pCurrentBMP, pCurrentSecret为BMP文件和secret文件的游标,为进行后续转存内容做准备
·在第一个像素位的阿尔法通道写入秘密文件大小(在恢复时作为重要参数生成恢复文件大小),pCurrentBMP游标后移6位。
·循环:在不超过BMP文件大小下写秘密文件的内容,每次循环结束后pCurrentBMP后移6位,pCurrentSecret后移3位
·用SaveFile函数保存操作后的pBMP文件
·释放内存(pBMP和pSecretFile)
2.恢复文件 Recovery(BMP文件,秘密文件名)
·读取BMP文件,使用ReadFileContent函数
·定位第一个像素偏移位置,在3个字节后的阿尔法通道位置读取秘密文件大小
·pCurrentBMP后移6个位置,定义buf存入读取出来的秘密文件内容
·循环将pCurrentBMP所指的3位阿尔法通道中内容存入buf
·SaveFile保存buf文件,并以secretFileName(传入参数)命名
3.主函数
·判断命令行参数传入,如错误,给出使用提示
·判断模式为『Encrypt』,即隐藏文件,执行Hide函数
·判断模式为『Decrypt』,即恢复文件,执行Recovery函数
http://blog.csdn.net/goldfighter/article/details/8289215
源码
#include “stdafx.h”
#include
//传入文件名,读取文件内容
char * ReadFileContent(char * filename, DWORD * filesize)
{
HANDLE file = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
if (hfile== INVALID_HANDLE_VALUE)
{
printf(“failed open %s\n”,filename);
return NULL;
}
DWORD dwRead; //定义变量,统计读取的字节数
DWORD dwSize = GetFileSize(hfile, &dwRead);
*filename = dwSize;
char * but = new char[dwSize];
RtlZeroMemory(buf, dwSize);
ReadFile(hfile, buf, dwSize, &dwRead, 0);
if (dwRead != dwSize)
{
printf(“Error when reading\n”);
return NULL;
}
CloseHandle(hfile);
return buf;
}
//写入文件
bool SaveFile(char * filename, char * buf, int len)
{
HANDLE file = CreateFileA(filename, GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ,0,CREATE_ALWAYS,0,0);
}
if (hfile== INVALID_HANDLE_VALUE)
{
printf(“failed open %s\n”,filename);
return NULL;
}
DWORD dwWrite; //保存写入了多少字节到文件中
SetFilePointer(file,0,0,FILE_BEGIN); //将文件指针指到最开头
WriteFile(hfile, buf, len, &dwWrite, 0);
CloseHandle(hfile);
return true;
bool Hide(char * secretFileName, char * bmpFileName)
{
DWORD dwBMPSize, dwSecretSize;
char * pBMP = ReadFileContent(bmpFileName, &dwBMPSize); //存储原始图片文件
char * pSecretFile = ReadFileContent(secretFileName, &dwSecretSize);
DWORD * lpFirstPointOffset = (DWORD *)(pBMP + 10) //找到第一像素偏移
printf(“The first point offset is %d\n”, *lpFirstPointOffset);
char * pCurrentBMP = pBMP + *lpFirstPointOffset; //保存在原始图片中的游标位置
char * pCurrentSecret = pSecretFile
//机密文件有多大?(重要参数)
*((DWORD *)pCurrentBMP+3) = dwSecretSize; //机密文件大小存在最开头
pCurrentBMP += 6;
for(; pCurrentBMP < (pBMP + dwBMPSize) && pCurrentSecret <= (pSecretFile + dwSecretSize); pCurrentBMP +=6)
{
*pCurrentBMP = *pCurrentSecret;
*(pCurrentBMP+1) = *(pCurrentSecret+1);
*(pCurrentBMP+2) = *(pCurrentSecret+2);
pCurrentSecret +=3;
}
SaveFile(bmpFileName,pBMP,dwBMPSize);
//释放内存
delete[] pBMP;
delete[] pSecretFile;
return true;
}
bool Recovery(char * bmpFileName, char * secretFileName)
{
DWORD dwBMPSize;
char * pBMP = ReadFileContentA(bmpFileName, &dwBMPSize);
DWORD * pFirstPoint= (DWORD *)(pBMP + 10);
printf(“The first point offset is %d\n”, *pFirstPoint);
DWORD dwSecretSize = *(DWORD *)(pBMP + *pFirstPoint + 3);
char * buf = new char[dwSecretSize];
char * pCurrentBMP = pBMP + *pFirstPoint + 6;
for (int i = 0;pCurrentBMP<(pBMP +dwBMPSize; pCurrentBMP +=6)
{
buf[i] = *pCurrentBMP;
buf[i+1] = *(pCurrentBMP + 1);
buf[i+2] = *(pCurrentBMP + 2);
i +=3;
}
SaveFile(secretFileName,buf,dwSecretSize);
delete[] pBMP;
delete[] buf
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 3)
{
printf(“Usage: %s Encrypt secret_filename BMP_filename\n”, argv[0]);
pritnf(“Usage: %s Decrypt BMP_filename secret_filename\n”, argv[0]);
return -1;
}
if (strcmp(argv[1],”Encrypt”)==0)
{
Hide(argv[2],argv[3]);
}
else if (strcmp(argv[1],”Decrypt”)==0)
Recovery(argv[2],argv[3]);
else
printf(“Error”);
printf(“Done!”);
return 0;
}
最后
我一直用mac,其中头文件引用了windows的API,暂未验证源码,应该是没有bug的,有机会找台windows做测试。
最近几个月比较熟悉的是python,打算这两天用python改写一下~到时贴出地址和大家交流~