Covert Messages (Hiding Apps in Web Pages)[Steganography] One way of passing secret messages is to hide them within normal looking content. In this example we will take an EXE program, and then stuff it into a series of GIF files. First we will split the EXE into fragments and then add a GIF header. Next we will compress the fragments, and then finally we will add an EX-OR key. [Source Code] ApplicationThe application, in the following example, spans across three bullet files (bullet01.gif, bullet02.gif and bullet03.gif) [Link]: |
Source code
using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; // We create a dummy GIF file, and then add the .EXE program with a compression of the bit stream // and then a simple invert of each byte and writes to OUT.GIF // The reverse, ignores the header, and reads each byte back, and flips them, and then decompresses the stream // A good code cracker knows the GIF header format, and analyses the next part and runs a simple bit flip to // reveal the GZip header. Once this is spotted the stream, the compressed stream is simply extracted // where the "MZ" magic number and all the .NET program text appear, and it can then be extract into an EXE // The EXE is split across two files (bullet01.gif and bullet02.gif) namespace graphics { class Program { const int BUFFSIZE = 4096; const byte MYKEY = 0xff; const bool CompressIt =true; static void Main(string[] args) { int n_gifs = 2; string exename=""; try { int val = parseArgs(args, ref n_gifs, ref exename); if (val==0) { showHelp(); return; } if (val==1) { Console.WriteLine("Application Obfuscator Version 1.0f"); Console.WriteLine("***************************"); creategif(exename, n_gifs); } else if (val==2) { Console.WriteLine("Application Obfuscator Version 1.0f"); Console.WriteLine("***************************"); readgif(n_gifs, "out.exe"); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } public static void showHelp() { Console.WriteLine("****************************************"); Console.WriteLine("Application Obfuscator 1.0f"); Console.WriteLine("****************************************"); Console.WriteLine(""); Console.WriteLine("The cipher -e option converts the EXE into a compress GIF (bullet01.gif, bullet02.gif, etc)"); Console.WriteLine("The cipher -d option converts the GIF back into an EXE (out.exe)"); Console.WriteLine("The cipher -n option defines the number of GIF shares"); Console.WriteLine(""); Console.WriteLine("Syntax:"); Console.WriteLine("Converting EXE to GIF (output file - out.gif) : Cipher.exe -e"); Console.WriteLine("Converting GIF to EXE (output file - out.exe) : Cipher.exe -d"); Console.WriteLine(""); } public static byte[] FileToByteArray(string fileName) { byte[] buff = null; FileStream fs = new FileStream(fileName, FileMode.Open,FileAccess.Read); BinaryReader br = new BinaryReader(fs); long numBytes = new FileInfo(fileName).Length; buff = br.ReadBytes((int)numBytes); fs.Close(); return buff; } public static byte[] split(byte[] inbyte, int start, int end) { byte[] newbyte = new byte[end - start+1]; int j=0; for (int i = start; i <= end; i++) { newbyte[j] = inbyte[i]; j++; } return newbyte; } public static void creategif(string fname,int n_gifs ) { byte [] infile = FileToByteArray(fname); string outfile = ""; int chunksize = infile.Length / n_gifs; int running_total = 0; Console.Write("Cipher Encrypt from " + fname + " to "); for (int i=0;i // 8 2 bytes // 10 1 byte bit 0: Global Color Table Flag (GCTF) // bit 1..3: Color Resolution // bit 4: Sort Flag to Global Color Table // bit 5..7: Size of Global Color Table: 2^(1+n) // 11 1 byte // 12 1 byte // 13 ? bytes RR GG BB // ? bytes // 1 bytes (0x3b) using (BinaryWriter writer = new BinaryWriter(File.Open(fname, FileMode.Create))) { byte[] newfile = null; if (CompressIt) newfile = Compress(b); // Compress to hide the size of the Base-64 image else newfile = b; writer.Write((byte)0x47); writer.Write((byte)0x49); writer.Write((byte)0x46); writer.Write((byte)0x38); writer.Write((byte)0x39); writer.Write((byte)0x61); writer.Write((short)(1024)); writer.Write((short)(1024)); writer.Write((byte)0xff); writer.Write((byte)0); writer.Write((byte)0); Random rnd = new Random(); for (int i = 0; i < 256; i++) { int val1 = rnd.Next(0, 256); int val2 = rnd.Next(0, 256); int val3 = rnd.Next(0, 256); writer.Write((byte)val1); writer.Write((byte)val2); writer.Write((byte)val3); } // Image block // Offset Length Contents // 0 1 byte Image Separator (0x2c) // 1 2 bytes Image Left Position // 3 2 bytes Image Top Position // 5 2 bytes Image Width // 7 2 bytes Image Height // 8 1 byte bit 0: Local Color Table Flag (LCTF) // bit 1: Interlace Flag // bit 2: Sort Flag // bit 2..3: Reserved // bit 4..7: Size of Local Color Table: 2^(1+n) // ? bytes Local Color Table(0..255 x 3 bytes) if LCTF is one // 1 byte LZW Minimum Code Size //[ // Blocks // 1 byte Block Size (s) // (s)bytes Image Data //]* // 1 byte Block Terminator(0x00) writer.Write((byte)0x2c); // 1 writer.Write((short)0x00); // 2 writer.Write((short)0x00); // 2 writer.Write((short)(1024)); // 2 writer.Write((short)(1024)); // 2 writer.Write((short)0x00); // 1 LCT Flag writer.Write((short)0x00); // 1 LZW // writer.Write((short)0x00); // 1 LZW for (int i = 0; i < newfile.Length; i++) { writer.Write((byte)(newfile[i] ^ MYKEY)); // Invert bits } writer.Write((byte)0x00); // Block terminator writer.Write((byte)0x3b); // Trailer } } // This method strips off the header and trailer of the GIF and returns just the content public static byte [] getSplit(string fname) { byte[] infile = FileToByteArray(fname); int startbyte = 14 + (256 * 3) - 1 + 13; int count = (infile.Length -startbyte -2); byte[] outbytes = new byte[count]; int j = 0; try { int Offset = 2; for (int i =startbyte; i < infile.Length - Offset; i++) { outbytes[j] = (byte)(infile[i] ^ MYKEY); // Invert bits j++; } } catch (Exception ex) { Console.Write("Exception..."); } byte[] filebytes = null; if (CompressIt) filebytes = Decompress(outbytes); // Compress to hide the size of the Base-64 image else filebytes= outbytes; // byte[] filebytes = Decompress(outbytes); return (filebytes); } // Read in the two GIF files and join public static void readgif(int n_gifs, string outfile) { Console.Write("Cipher Decrypt to " + outfile+ " from "); using (BinaryWriter writer = new BinaryWriter(File.Open(outfile, FileMode.Create))) { for (int i = 0; i < n_gifs; i++) { string fname = "bullet0" + (i + 1) + ".gif"; Console.Write(fname+" "); byte[] split01 = getSplit(fname); for (int j = 0; j < split01.Length; j++) { byte val = (byte)(split01[j]); writer.Write(val); } } } Console.WriteLine(); } // Standard Zlib compress/decompress public static byte[] Compress(byte[] inBytes) { using (MemoryStream newbuff = new MemoryStream()) { using (GZipStream glib = new GZipStream(newbuff, CompressionMode.Compress, true)) { glib.Write(inBytes, 0, inBytes.Length); } return newbuff.ToArray(); } } static byte[] Decompress(byte[] zipBytes) { using (GZipStream stream = new GZipStream(new MemoryStream(zipBytes), CompressionMode.Decompress)) { byte[] newbuffer = new byte[BUFFSIZE]; using (MemoryStream inbuff = new MemoryStream()) { int count = 0; do { count = stream.Read(newbuffer, 0, BUFFSIZE); if (count > 0) { inbuff.Write(newbuffer, 0, count); } } while (count > 0); return inbuff.ToArray(); } } } public static int parseArgs(string[] args, ref int n_gifs, ref string exename) { if (args.Length == 0) return (0); if (args[0] == "-n") { int i; bool result = int.TryParse(args[1], out i); if (result == true) { n_gifs = Convert.ToInt32(args[1]); Console.WriteLine("Number of GIFs: "+n_gifs); } else Console.WriteLine("Problem with number of GIFs"); } string tocheck = "-e"; if (args.Contains(tocheck)) { exename = args[args.Length - 1]; return (1); } tocheck = "-d"; if (args.Contains(tocheck)) { return (2); } return(0); } } }