【学习笔记】将文件隐藏在BMP位图中


前言

网易云上非常有意思的课程,教如何将文件隐藏在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改写一下~到时贴出地址和大家交流~


你可能感兴趣的:(C++)