http://blog.csdn.net/yiruoyun/archive/2004/10/18/141514.aspx
//使用方法:
// BmpSafe.exe /file2bmp (input BMP) (input file to hide) [output file]
//BmpSafe.exe /bmp2file (data BMP) [output file]
using System;
using System.IO;
using System.Drawing;
public class Bitmap24Writer
{
protected Bitmap bmp;
protected int curX, curY, iRGB;
protected uint bitsLeft, bitsTotal;
protected byte r, g, b;
public Bitmap24Writer(Bitmap bmp)
{
if (bmp.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
throw new ArgumentException();
// assign vars
curX = curY = iRGB = 0;
this.bmp = bmp;
bitsLeft = bitsTotal = (uint)bmp.Height * (uint)bmp.Width * 3;
}
public uint GetUnusedBitCount()
{
return bitsLeft;
}
public uint GetMaxBitStorageCount()
{
return bitsTotal;
}
public Bitmap GetBitmap()
{
return bmp;
}
public bool WriteByte(byte by)
{
if (bitsLeft < 8)
return false;
uint bits2Do = 8;
for (; curX < bmp.Width; curX++)
{
if (curY >= bmp.Height)
curY = 0;
for (; curY < bmp.Height; curY++)
{
if (bits2Do == 0)
return true;
Color col = bmp.GetPixel(curX, curY);
r = col.R;
g = col.G;
b = col.B;
for ( ; ; )
{
byte curBit = (byte)(by & 1);
switch( iRGB )
{
case 0:
r = (byte)(r & 0xFE);
r |= curBit;
break;
case 1:
g = (byte)(g & 0xFE);
g |= curBit;
break;
case 2:
b = (byte)(b & 0xFE);
b |= curBit;
break;
}
--bits2Do;
--bitsLeft;
by >>= 1;
bmp.SetPixel(curX, curY, Color.FromArgb(r, g, b));
if (iRGB == 2)
{
iRGB = 0;
break;
}
iRGB++;
if (bits2Do == 0)
return true;
}
}
}
return true;
}
}
public class Bitmap24Reader
{
protected Bitmap bmp;
protected int curX, curY, iRGB;
protected uint bitsLeft, bitsTotal;
public Bitmap24Reader(Bitmap bmp)
{
if (bmp.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
throw new ArgumentException();
curX = curY = iRGB = 0;
this.bmp = bmp;
bitsLeft = bitsTotal = (uint)bmp.Height * (uint)bmp.Width * 3;
}
public uint GetUnusedBitCount()
{
return bitsLeft;
}
public uint GetMaxBitStorageCount()
{
return bitsTotal;
}
public Bitmap GetBitmap()
{
return bmp;
}
public bool ReadByte(out byte by)
{
by = 0;
if (bitsLeft < 8)
return false;
byte bit = 0;
uint bits2Do = 8;
for (; curX < bmp.Width; curX++)
{
if (curY >= bmp.Height)
curY = 0;
for (; curY < bmp.Height; curY++)
{
if (bits2Do == 0)
return true;
Color col = bmp.GetPixel(curX, curY);
for ( ; ; )
{
switch( iRGB )
{
case 0:
bit = (byte)(col.R & 1);
break;
case 1:
bit = (byte)(col.G & 1);
break;
case 2:
bit = (byte)(col.B & 1);
break;
}
--bits2Do;
--bitsLeft;
by |= (byte)(bit << 7);
if (bits2Do != 0)
by >>= 1;
if (iRGB == 2)
{
iRGB = 0;
break;
}
iRGB++;
if (bits2Do == 0)
return true;
}
}
}
return true;
}
}
public class BitmapWorks
{
public static bool Data2Bmp(FileStream dataStream, FileStream bmpStream, string outFName, out string error)
{
error = "";
Bitmap bmp;
try
{
bmp = new Bitmap(bmpStream);
}
catch
{
error = "Are you sure the specified bitmap is a valid bitmap ?";
return false;
}
if (bmp.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
error = "Please drop only 24bit bitmaps,thanks !";
return false;
}
error += "-> Bitmap info: height=" + bmp.Height + " width=" + bmp.Width + "...\n";
if (dataStream.Length == 0)
{
error = "Input data has a not supported size of 0 !";
return false;
}
uint maxByteStorage = ((uint)bmp.Height * (uint)bmp.Width * 3) / 8;
error += "-> Up to " + (maxByteStorage-4) + " bytes could be stored in this bitmap...\n";
if (maxByteStorage < dataStream.Length + 4)
{
error = "Not enough pixel in target bitmap to hide the input data block !";
return false;
}
Bitmap24Writer bmpWriter = new Bitmap24Writer(bmp);
uint dataSize = (uint)dataStream.Length;
try
{
for (uint u = 0; u < 4; u++)
{
bmpWriter.WriteByte( (byte)dataSize );
dataSize >>= 8;
}
for (uint u = 0; u < dataStream.Length; u++)
bmpWriter.WriteByte( (byte)dataStream.ReadByte() );
}
catch
{
error = "Error while modifing the bitmap's pixels !";
return false;
}
double maxBitStorage = bmpWriter.GetMaxBitStorageCount();
double spaceUsed = (100 * (maxBitStorage - bmpWriter.GetUnusedBitCount())) / maxBitStorage;
error += "-> Space used: " + Convert.ToUInt32(spaceUsed) + "%...\n";
try
{
if ( File.Exists( outFName ) )
File.Delete( outFName );
bmpWriter.GetBitmap().Save(outFName);
}
catch
{
error = "Couldn't save output to " + outFName + " !";
return false;
}
error += "-> Output saved to: " + outFName + "...\n";
return true;
}
public static bool Bmp2Data(FileStream bmpStream, string outFName, out string error)
{
error = "";
Bitmap bmp;
try
{
bmp = new Bitmap(bmpStream);
}
catch
{
error = "Are you sure the specified bitmap is a valid bitmap ?";
return false;
}
Bitmap24Reader bmpReader;
try
{
bmpReader = new Bitmap24Reader(bmp);
}
catch (ArgumentException)
{
error = "This isn't a 24bit bitmap !";
return false;
}
FileStream outStream;
try
{
outStream = File.Create( outFName );
}
catch
{
error = "Couldn't create output file: " + outFName + " !";
return false;
}
uint dataSize = 0;
byte outByte;
try
{
for (uint u = 0; u < 4; u++)
{
if ( !bmpReader.ReadByte( out outByte ) )
throw new Exception();
dataSize |= (uint)( outByte << 8*3 );
if (u != 3)
dataSize >>= 8;
}
error += "-> Size of hidden data: " + dataSize + " bytes...\n";
for (uint u = 0; u < dataSize; u++)
{
if ( !bmpReader.ReadByte( out outByte ) )
throw new Exception();
outStream.WriteByte( outByte );
}
}
catch
{
error = "Exception caught while reading the hidden data !";
return false;
}
finally
{
outStream.Close();
}
error += "-> Output saved to: " + outFName + "...\n";
return true;
}
}
class BmpSafe
{
public static string cmdLine =
"Command line:\n" +
" BmpSafe.exe /file2bmp (input BMP) (input file to hide) [output file]\n" +
" BmpSafe.exe /bmp2file (data BMP) [output file]";
private static string serviceOne = "/file2bmp";
private static string serviceTwo = "/bmp2file";
[STAThread]
static void Main(string[] args)
{
Console.WriteLine(
"BmpSafe - hide files in 24bit bitmaps\n" +
" a little steganografie implementation\n" +
" by yoda\n" +
"-------------------------------------------------------------------------------\n");
string inFile = "", inBmp, outFile;
bool bFile2Bmp;
if (args.Length < 2)
{
Console.WriteLine("!! Invalid number of arguments :(");
Console.WriteLine(cmdLine);
return; // ERR
}
if ( String.Compare(args[0], serviceOne, true) == 0 )
bFile2Bmp = true;
else if ( String.Compare(args[0], serviceTwo, true) == 0)
bFile2Bmp = false;
else
{
Console.WriteLine("!! First parameters must be either \"/file2bmp\" or \"/bmp2file\" !");
return;
}
inBmp = args[1];
if (bFile2Bmp)
{
if (args.Length < 3)
{
Console.WriteLine("!! Invalid number of arguments :(");
Console.WriteLine(cmdLine);
return;
}
inFile = args[2];
if (args.Length > 3)
outFile = args[3];
else
outFile = "Secret.BMP";
}
else
{
if (args.Length > 2)
outFile = args[2];
else
outFile = "Secret.bin";
}
Console.WriteLine("-> Processing input...");
try
{
string err;
bool ret;
if (bFile2Bmp)
ret = BitmapWorks.Data2Bmp(
File.OpenRead(inFile),
File.OpenRead(inBmp),
outFile,
out err );
else
ret = BitmapWorks.Bmp2Data(
File.OpenRead(inBmp),
outFile,
out err);
if (!ret)
{
Console.WriteLine("!! " + err);
return;
}
else
Console.Write( err );
}
catch(FileNotFoundException)
{
Console.WriteLine("!! At least one file could not be found :(");
return;
}
catch
{
Console.WriteLine("!! An exception occurred :(");
return;
}
Console.WriteLine("-> Job done...");
return;
}
}