信息隐藏指在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是透明的。
传统的信息隐藏起源于古老的隐写术。如在古希腊战争中,为了安全地传送军事情报,奴隶主剃光奴隶 的头发,将情报文在奴隶的头皮上,待头发长起后再派出去传送消息。我国古代也早有以藏头诗、藏尾诗、漏格诗以及绘画等形式,将要表达的意思和“密语”隐藏在诗文或画卷中的特定位置,一般人只注意诗或画的表面意境,而不会去注意或破解隐藏其中的密语。 信息隐藏的发展历史可以一直追溯到"匿形术(Steganography)"的使用。"匿形术"一词来源于古希腊文中"隐藏的"和"图形"两个词语的组合。虽然"匿形术"与"密码术(Cryptography)"都是致力于信息的保密技术,但是,两者的设计思想却完全不同。"密码术"主要通过设计加密技术,使保密信息不可读,但是对于非授权者来讲,虽然他无法获知保密信息的具体内容,却能意识到保密信息的存在。而"匿形术"则致力于通过设计精妙的方法,使得非授权者根本无从得知保密信息的存在与否。相对于现代密码学来讲,信息隐藏的最大优势在于它并不限制对主信号的存取和访问,而是致力于签字信号的安全保密性。
我们进行信息交换的时候,需要保证数据的安全,所以需要进行适当的信息隐藏。
我们发送一个图片。但是图片里面隐含的加密信息,是就是信息隐藏,只有专属工具能够读出。
所以信息隐藏技术很重要。针对信息安全。
我们来实现一个案例代码。
BMP图信息隐藏头文件,请见代码分析
// BMPHider.h: interface for the CBMPHider class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_BMPHIDER_H__6287D87F_F0AA_4D0C_9502_4674B639CBB5__INCLUDED_) #define AFX_BMPHIDER_H__6287D87F_F0AA_4D0C_9502_4674B639CBB5__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <complex> using namespace std; class CBMPHider { public: CBMPHider(); virtual ~CBMPHider(); public: int m_BitCount; //位图的图像位数 WORD * m_pWordData; CPalette m_Palette; unsigned char *m_pDib, *m_pDibBits; unsigned char *m_pOldDibShow; DWORD m_dwDibSize; BITMAPINFOHEADER *m_pBIH; RGBQUAD *m_pPalette; int m_nPaletteEntries; UINT bitmap_size; UINT embfile_size; unsigned char *p; //指向宿主图像数据 int tag; //此tag用以标记打开的图像中是否含有隐藏信息0:无 else:有 unsigned char *q; //指向隐藏文件数据 unsigned char *m_pFile; public: BOOL Draw( CDC *pDC, int nX = -1, int nY = -1, int nWidth = -1, int nHeight = -1, int Style = 1); BOOL Save( const char *pszFilename ); BOOL Load( const char * ); void BackUpDib(); //备份图像 void Pick(); //提取图像 void Embed(); //嵌入图像 BOOL LoadEmbFile(const char *); BOOL DrawContrast(CDC *pDC,int rect_width, int rect_height); void SavePicked( const char *pszFilename ); }; #endif // !defined(AFX_BMPHIDER_H__6287D87F_F0AA_4D0C_9502_4674B639CBB5__INCLUDED_)
请见详细代码实现
#include "stdafx.h" #include "BMPHider.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CBMPHider::CBMPHider() { m_pDib = NULL; m_pWordData = NULL; m_pFile = NULL; m_pOldDibShow = NULL; } CBMPHider::~CBMPHider() { if( m_pDib != NULL ) delete [] m_pDib; if( m_pWordData != NULL ) delete [] m_pWordData; if (m_pFile != NULL) delete [] m_pFile; if (m_pOldDibShow != NULL) delete [] m_pOldDibShow; } BOOL CBMPHider::Load( const char *pszFilename ) { CFile cf; if( !cf.Open( pszFilename, CFile::modeRead ) ) return( FALSE ); DWORD dwDibSize; dwDibSize = cf.GetLength() - sizeof( BITMAPFILEHEADER ); unsigned char *pDib; pDib = new unsigned char [dwDibSize]; if( pDib == NULL ) return( FALSE ); BITMAPFILEHEADER BFH; try { if( cf.Read( &BFH, sizeof( BITMAPFILEHEADER ) ) != sizeof( BITMAPFILEHEADER ) || BFH.bfType != 'MB' || cf.Read( pDib, dwDibSize ) != dwDibSize ) { delete [] pDib; return( FALSE ); } } catch( CFileException *e ) { e->Delete(); delete [] pDib; return( FALSE ); } cf.Close(); if( m_pDib != NULL ) delete m_pDib; if (BFH.bfReserved1 != 0) //含有隐藏信息,保存其大小 { if (BFH.bfReserved2 == 0) embfile_size = BFH.bfReserved1; else embfile_size = BFH.bfReserved1 + 65535; } m_pDib = pDib; m_dwDibSize = dwDibSize; m_pBIH = (BITMAPINFOHEADER *) m_pDib; m_pPalette =(RGBQUAD *) &m_pDib[sizeof(BITMAPINFOHEADER)]; m_nPaletteEntries = 1 << m_pBIH->biBitCount; if( m_pBIH->biBitCount > 8 ) m_nPaletteEntries = 0; else if( m_pBIH->biClrUsed != 0 ) m_nPaletteEntries = m_pBIH->biClrUsed; m_pDibBits = &m_pDib[sizeof(BITMAPINFOHEADER)+ m_nPaletteEntries*sizeof(RGBQUAD)]; if( m_Palette.GetSafeHandle() != NULL ) m_Palette.DeleteObject(); if( m_nPaletteEntries != 0 ) { LOGPALETTE *pLogPal = (LOGPALETTE *) new char [sizeof(LOGPALETTE)+ m_nPaletteEntries*sizeof(PALETTEENTRY)]; if( pLogPal != NULL ) { pLogPal->palVersion = 0x300; pLogPal->palNumEntries = m_nPaletteEntries; for( int i=0; i<m_nPaletteEntries; i++ ) { pLogPal->palPalEntry[i].peRed = m_pPalette[i].rgbRed; pLogPal->palPalEntry[i].peGreen = m_pPalette[i].rgbGreen; pLogPal->palPalEntry[i].peBlue = m_pPalette[i].rgbBlue; } m_Palette.CreatePalette( pLogPal ); delete [] pLogPal; } } m_BitCount = 24; //24位位图 p = m_pDibBits; //指向位图数据的指针,用来执行处理操作用 bitmap_size = m_dwDibSize - (m_pDibBits - m_pDib);//真正的位图数据的大小(即除头结构外) tag = BFH.bfReserved1; return( TRUE ); } BOOL CBMPHider::Save( const char *pszFilename ) //保存含有隐藏信息的bmp { if( m_pDib == NULL ) return( FALSE ); CFile cf; if( !cf.Open( pszFilename, CFile::modeCreate | CFile::modeWrite ) ) return( FALSE ); try { BITMAPFILEHEADER BFH; memset( &BFH, 0, sizeof( BITMAPFILEHEADER ) ); BFH.bfType = 'MB'; BFH.bfSize = sizeof( BITMAPFILEHEADER ) + m_dwDibSize; BFH.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + m_nPaletteEntries * sizeof( RGBQUAD ); if (embfile_size <= 65535) //由于bfReserved1是unsigned short型的,大小可能不能满足要求,可能要用到reserved2 BFH.bfReserved1 = embfile_size; else { BFH.bfReserved1 = embfile_size - 65535; BFH.bfReserved2 = 1; //标记 } cf.Write( &BFH, sizeof( BITMAPFILEHEADER ) ); cf.Write( m_pDib, m_dwDibSize ); } catch( CFileException *e ) { e->Delete(); return( FALSE ); } return( TRUE ); } BOOL CBMPHider::Draw( CDC *pDC, int nX, int nY, int nWidth, int nHeight, int Style ) { if( m_pDib == NULL ) return( FALSE ); long vWidth = m_pBIH->biWidth; if( nWidth == -1 ) nWidth = m_pBIH->biWidth; if( nHeight == -1 ) nHeight = m_pBIH->biHeight; if (Style) { StretchDIBits( pDC->m_hDC, nX, nY, nWidth, nHeight, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight, m_pDibBits, (BITMAPINFO *) m_pBIH, BI_RGB, SRCCOPY ); } else { SetDIBitsToDevice( pDC->m_hDC, nX, nY, m_pBIH->biWidth, m_pBIH->biHeight, 0, 0, 0, m_pBIH->biHeight, m_pDibBits, (BITMAPINFO *) m_pBIH, BI_RGB); } return( TRUE ); } BOOL CBMPHider::LoadEmbFile(const char * pszFilename) { CFile cf; if( !cf.Open( pszFilename, CFile::modeRead ) ) return( FALSE ); DWORD dwFileSize; dwFileSize = cf.GetLength(); embfile_size = dwFileSize; unsigned char *pFile; pFile = new unsigned char [dwFileSize]; cf.Read( pFile, dwFileSize ); //将文件中内容读入数组,解下来就开始嵌入操作 m_pFile = pFile; q = pFile; //记录下位置 return true; } void CBMPHider::Embed()//嵌入 { unsigned char bmdata;//bitmap data unsigned char efdata;//embeddedfile data int t = 7; int x[8]; int s[8]; int last_bit; //记录字节最低位本来的bit for(UINT i1 = 0, i2 = 0; i1 <= bitmap_size - 1, i2 <= embfile_size - 1; i1++) { bmdata = *p; for (int j = 0; j <= 7; j++) //计算各bit位 { x[j] = bmdata & 1; bmdata >>= 1; } last_bit = x[0]; x[0] = x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7]; if (t == 7) //宿主图片每走过八个字节,计算一次s[] { efdata = *q; for (j = 0; j <= 7; j++) { s[j] = efdata & 1; efdata >>= 1; } } x[0] ^= s[t];//隐藏信息 if (last_bit == 0) //嵌入隐藏信息 { *p |= x[0]; } else { *p &= 254 + x[0]; } p++; t--; if (t == -1) //需要计算一次s[] { t = 7; q++; i2++; } } } void CBMPHider::Pick()//提取 { m_pFile = new unsigned char [embfile_size]; unsigned char *q = m_pFile; unsigned char bmdata;//bitmap data int x[8]; int s[8]; int t = 7; for (UINT i1 = 0, i2 = 0; i1 <= bitmap_size - 1, i2 <= embfile_size - 1; i1++) { bmdata = *p; for (int j = 0; j <= 7; j++) //计算各bit位 { x[j] = bmdata & 1; bmdata >>= 1; } s[t] = x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7]; t--; if (t == -1) //s[7]到s[0]组成一个字节 { *q = s[7] * 128 + s[6] * 64 + s[5] * 32 + s[4] * 16 + s[3] * 8 + s[2] * 4 + s[1] * 2 + s[0]; t = 7; i2++; q++; } p++; } } void CBMPHider::SavePicked( const char *pszFilename ) { CFile cf; cf.Open( pszFilename, CFile::modeCreate | CFile::modeWrite ); cf.Write( m_pFile, embfile_size ); } void CBMPHider::BackUpDib() { m_pOldDibShow = new unsigned char [bitmap_size]; ::CopyMemory(m_pOldDibShow, m_pDibBits, bitmap_size); //将原始的数据单独保存以便对比显示 } BOOL CBMPHider::DrawContrast(CDC *pDC, int rect_width, int rect_height) { //看原图,如果容纳得下两个图,则不要压缩,否则要压缩 if (m_pOldDibShow == NULL) return FALSE; if (rect_width >= 2*m_pBIH->biWidth + 30 && rect_height >= m_pBIH->biHeight) { StretchDIBits( pDC->m_hDC, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight, m_pOldDibShow, (BITMAPINFO *) m_pBIH, BI_RGB, SRCCOPY ); // 原图 StretchDIBits( pDC->m_hDC, m_pBIH->biWidth+30, 0, m_pBIH->biWidth, m_pBIH->biHeight, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight, m_pDibBits, (BITMAPINFO *) m_pBIH, BI_RGB, SRCCOPY ); // 嵌入隐藏信息的图 } else { int scale_i = m_pBIH->biWidth * 5 / (rect_width*2); int scale_j = m_pBIH->biHeight / rect_height; if (scale_i < scale_j) scale_i = scale_j; StretchDIBits( pDC->m_hDC, 0, 0, m_pBIH->biWidth / scale_i, m_pBIH->biHeight / scale_i, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight, m_pOldDibShow, (BITMAPINFO *) m_pBIH, BI_RGB, SRCCOPY ); // 原图 StretchDIBits( pDC->m_hDC, m_pBIH->biWidth / scale_i+30, 0, m_pBIH->biWidth / scale_i, m_pBIH->biHeight / scale_i, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight, m_pDibBits, (BITMAPINFO *) m_pBIH, BI_RGB, SRCCOPY ); // 嵌入隐藏信息的图 } return TRUE; }
具体代码调用实现
#include "stdafx.h" #include "DataHideInBMP.h" #include "DataHideInBMPDoc.h" #include "DataHideInBMPView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CDataHideInBMPView IMPLEMENT_DYNCREATE(CDataHideInBMPView, CView) BEGIN_MESSAGE_MAP(CDataHideInBMPView, CView) //{{AFX_MSG_MAP(CDataHideInBMPView) ON_COMMAND(ID_NORMAL, OnNormal) ON_COMMAND(ID_STRETCH, OnStretch) ON_COMMAND(ID_FILE_OPEN, OnFileOpen) ON_COMMAND(ID_EMBED, OnEmbed) ON_UPDATE_COMMAND_UI(ID_EMBED, OnUpdateEmbed) ON_COMMAND(ID_PICK, OnPick) ON_UPDATE_COMMAND_UI(ID_PICK, OnUpdatePick) //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDataHideInBMPView construction/destruction CDataHideInBMPView::CDataHideInBMPView() { // TODO: add construction code here ImageStyle = 0; have_open_a_file = false; embed = false; pick = false; show_contrast = false; } CDataHideInBMPView::~CDataHideInBMPView() { } BOOL CDataHideInBMPView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs WNDCLASSEX wndcls; wndcls.cbSize=sizeof(WNDCLASSEX); HINSTANCE hinst=AfxGetInstanceHandle(); if(CView::PreCreateWindow(cs)&&cs.lpszClass!=NULL) { HBRUSH hbkbrush=CreateSolidBrush(RGB(0,0,0)); if(!GetClassInfoEx(hinst,cs.lpszClass,&wndcls)) return FALSE; UnregisterClass(cs.lpszClass,hinst); wndcls.hbrBackground=hbkbrush; RegisterClassEx(&wndcls); return TRUE; } else return FALSE; return CView::PreCreateWindow(cs); } ///////////////////////////////////////////////////////////////////////////// // CDataHideInBMPView drawing void CDataHideInBMPView::OnDraw(CDC* pDC) { CDataHideInBMPDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here RECT Rect; GetClientRect( &Rect ); if (!show_contrast) m_BMPHider.Draw( pDC, 0, 0, Rect.right, Rect.bottom, ImageStyle ); else { m_BMPHider.DrawContrast(pDC, Rect.right, Rect.bottom); pDC->SetBkColor((0,0,0)); pDC->SetTextColor((255,255,255)); pDC->TextOut(0, Rect.bottom-30, "左图为原始图,右图为嵌入了隐藏信息的图"); } } ///////////////////////////////////////////////////////////////////////////// // CDataHideInBMPView printing BOOL CDataHideInBMPView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CDataHideInBMPView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } void CDataHideInBMPView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } ///////////////////////////////////////////////////////////////////////////// // CDataHideInBMPView diagnostics #ifdef _DEBUG void CDataHideInBMPView::AssertValid() const { CView::AssertValid(); } void CDataHideInBMPView::Dump(CDumpContext& dc) const { CView::Dump(dc); } CDataHideInBMPDoc* CDataHideInBMPView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDataHideInBMPDoc))); return (CDataHideInBMPDoc*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CDataHideInBMPView message handlers void CDataHideInBMPView::OnNormal() { // TODO: Add your command handler code here ImageStyle = 0; CMenu *pMnu = AfxGetMainWnd()->GetMenu( ); pMnu->CheckMenuItem(ID_STRETCH,MF_UNCHECKED); pMnu->CheckMenuItem(ID_NORMAL,MF_CHECKED); InvalidateRect( NULL, TRUE ); UpdateWindow(); } void CDataHideInBMPView::OnStretch() { // TODO: Add your command handler code here ImageStyle = 1; CMenu *pMnu = AfxGetMainWnd()->GetMenu( ); pMnu->CheckMenuItem(ID_STRETCH,MF_CHECKED); pMnu->CheckMenuItem(ID_NORMAL,MF_UNCHECKED); InvalidateRect( NULL, TRUE ); UpdateWindow(); } void CDataHideInBMPView::OnFileOpen() { // TODO: Add your command handler code here show_contrast = false; //非对比显示 static char szFilter[] = "BMP Files(*.BMP)|*.BMP||"; CFileDialog FileDlg( TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter ); if( FileDlg.DoModal() == IDOK && m_BMPHider.Load( FileDlg.GetPathName() ) ) { InvalidateRect( NULL, TRUE ); UpdateWindow(); have_open_a_file=true; if (m_BMPHider.tag == 0)//该图无隐藏信息 { embed = true; pick = false; //菜单亮暗控制 }// else { embed = false; pick = true; //菜单亮暗控制 }// } } void CDataHideInBMPView::OnEmbed() { // TODO: Add your command handler code here //弹出打开对话框,用户选择嵌入的文件 static char szFilter[] = "All Files(*.*)|*.*||"; CFileDialog FileDlg( TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter ); if( FileDlg.DoModal() == IDOK && m_BMPHider.LoadEmbFile( FileDlg.GetPathName() ) ) { if (m_BMPHider.bitmap_size / m_BMPHider.embfile_size < 8) { AfxMessageBox("文件太大,无法嵌入!"); } else { show_contrast = true; m_BMPHider.BackUpDib(); //图片原始数据备份供对比显示 m_BMPHider.Embed(); //嵌入 Invalidate(true);//刷屏,显示加入隐藏信息后的图像 static char szFilter[] = "BMP Files(*.BMP)|*.BMP||"; CFileDialog FileDlg( FALSE, "bmp", NULL, //保存 OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, szFilter ); if( FileDlg.DoModal() == IDOK ) { m_BMPHider.Save( FileDlg.GetPathName() ); } embed = false; } } } void CDataHideInBMPView::OnUpdateEmbed(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable(embed); } void CDataHideInBMPView::OnPick() { // TODO: Add your command handler code here //提取完毕,弹出保存对话框,用户进行保存 static char szFilter[] = "All Files(*.*)|*.*||"; CFileDialog FileDlg( FALSE, "", NULL, //保存 OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, szFilter ); if (FileDlg.DoModal() == IDOK) { m_BMPHider.Pick(); m_BMPHider.SavePicked(FileDlg.GetPathName()); pick = false; } } void CDataHideInBMPView::OnUpdatePick(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable(pick); }