Skip to content

Instantly share code, notes, and snippets.

@AndreCAndersen
Created March 27, 2017 10:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndreCAndersen/78b38ef60b402c7f1b7566e091941d0a to your computer and use it in GitHub Desktop.
Save AndreCAndersen/78b38ef60b402c7f1b7566e091941d0a to your computer and use it in GitHub Desktop.
Hiding Your Bits in the Bytes: A basic example of modern steganography using C#
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
namespace SteganographyTest
{
class Program
{
static void Main()
{
byte[] hiddenBytes = Util.BitmapToByteArray(Image.FromFile("hidden.png"));
Encode(hiddenBytes, "innocuous.png", "encoded.png");
byte[] loadedHiddenBytes = Decode("encoded.png");
Util.ByteArrayToBitmap(loadedHiddenBytes).Save("decoded.png", ImageFormat.Png);
CreateMask("innocuous.png", "encoded.png");
}
public static void Encode(byte[] hiddenBytes, string inputImageFileName, string outputImageFileName)
{
// Loading the data we want to hide to a byte array
byte[] hiddenLengthBytes = BitConverter.GetBytes(hiddenBytes.Length);
byte[] hiddenCombinedBytes = Util.Combine(hiddenLengthBytes, hiddenBytes);
// Loading an innocuous image we want to store the hidden data in to a byte array
Image innocuousBmp = Image.FromFile(inputImageFileName);
byte[] rgbComponents = Util.RgbComponentsToBytes(innocuousBmp);
// Encoding the hidden data into the innocuous image, and storing it to file.
byte[] encodedRgbComponents = EncodeBytes(hiddenCombinedBytes, rgbComponents);
Bitmap encodedBmp = Util.ByteArrayToBitmap(encodedRgbComponents, innocuousBmp.Width, innocuousBmp.Height);
encodedBmp.Save(outputImageFileName, ImageFormat.Png);
}
private static byte[] EncodeBytes(byte[] hiddenBytes, byte[] innocuousBytes)
{
BitArray hiddenBits = new BitArray(hiddenBytes);
byte[] encodedBitmapRgbComponents = new byte[innocuousBytes.Length];
for (int i = 0; i < innocuousBytes.Length; i++)
{
if (i < hiddenBits.Length)
{
byte evenByte = (byte)(innocuousBytes[i] - innocuousBytes[i] % 2);
encodedBitmapRgbComponents[i] = (byte)(evenByte + (hiddenBits[i] ? 1 : 0));
}
else
{
encodedBitmapRgbComponents[i] = innocuousBytes[i];
}
}
return encodedBitmapRgbComponents;
}
public static byte[] Decode(string imageFileName)
{
// Loading the seemingly innocuous image with hidden data into a byte array
Bitmap loadedEncodedBmp = new Bitmap(imageFileName);
byte[] loadedEncodedRgbComponents = Util.RgbComponentsToBytes(loadedEncodedBmp);
const int bytesInInt = 4;
byte[] loadedHiddenLengthBytes = DecodeBytes(loadedEncodedRgbComponents, 0, bytesInInt);
int loadedHiddenLength = BitConverter.ToInt32(loadedHiddenLengthBytes, 0);
byte[] loadedHiddenBytes = DecodeBytes(loadedEncodedRgbComponents, bytesInInt, loadedHiddenLength);
return loadedHiddenBytes;
}
private static byte[] DecodeBytes(byte[] innocuousLookingData, int byteIndex, int byteCount)
{
const int bitsInBytes = 8;
int bitCount = byteCount * bitsInBytes;
int bitIndex = byteIndex * bitsInBytes;
bool[] loadedHiddenBools = new bool[bitCount];
for (int i = 0; i < bitCount; i++)
{
loadedHiddenBools[i] = innocuousLookingData[i + bitIndex] % 2 == 1;
}
BitArray loadedHiddenBits = new BitArray(loadedHiddenBools);
byte[] loadedHiddenBytes = new byte[loadedHiddenBits.Length / bitsInBytes];
loadedHiddenBits.CopyTo(loadedHiddenBytes, 0);
return loadedHiddenBytes;
}
public static void CreateMask(string inputImageFileName1, string inputImageFileName2)
{
Image image1 = Image.FromFile(inputImageFileName1);
Image image2 = Image.FromFile(inputImageFileName2);
Bitmap bmp1 = new Bitmap(image1);
Bitmap bmp2 = new Bitmap(image2);
Bitmap maskDiff = new Bitmap(bmp1);
Bitmap maskParity1 = new Bitmap(bmp1);
Bitmap maskParity2 = new Bitmap(bmp2);
for (int i = 0; i < maskDiff.Height; i++)
{
for (int j = 0; j < maskDiff.Width; j++)
{
Color px1 = bmp1.GetPixel(j, i);
Color px2 = bmp2.GetPixel(j, i);
int maskDiffIntensity = 255 - Math.Abs(px2.R - px1.R) * 85 - Math.Abs(px2.G - px1.G) * 85 - Math.Abs(px2.B - px1.B) * 85;
maskDiff.SetPixel(j, i, Color.FromArgb(maskDiffIntensity, maskDiffIntensity, maskDiffIntensity));
int maskParityIntensity1 = (px1.R % 2) * 85 + (px1.G % 2) * 85 + (px1.B % 2) * 85;
maskParity1.SetPixel(j, i, Color.FromArgb(maskParityIntensity1, maskParityIntensity1, maskParityIntensity1));
int maskParityIntensity2 = (px2.R % 2) * 85 + (px2.G % 2) * 85 + (px2.B % 2) * 85;
maskParity2.SetPixel(j, i, Color.FromArgb(maskParityIntensity2, maskParityIntensity2, maskParityIntensity2));
}
}
maskDiff.Save("maskDiff.png");
maskParity1.Save("maskParity_" + inputImageFileName1);
maskParity2.Save("maskParity_" + inputImageFileName2);
}
}
class Util
{
public static byte[] BitmapToByteArray(Image img)
{
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
}
public static Image ByteArrayToBitmap(byte[] bytes)
{
using (MemoryStream ms = new MemoryStream(bytes))
{
return Image.FromStream(ms);
}
}
public static byte[] Combine(byte[] left, byte[] right)
{
byte[] combined = new byte[left.Length + right.Length];
Buffer.BlockCopy(left, 0, combined, 0, left.Length);
Buffer.BlockCopy(right, 0, combined, left.Length, right.Length);
return combined;
}
public static byte[] RgbComponentsToBytes(Image innocuousImg)
{
Bitmap innocuousBmp = new Bitmap(innocuousImg);
int counter = 0;
byte[] components = new byte[3 * innocuousBmp.Width * innocuousBmp.Height];
for (int y = 0; y < innocuousBmp.Height; y++)
{
for (int x = 0; x < innocuousBmp.Width; x++)
{
Color c = innocuousBmp.GetPixel(x, y);
components[counter++] = c.R;
components[counter++] = c.G;
components[counter++] = c.B;
}
}
return components;
}
public static Bitmap ByteArrayToBitmap(byte[] rgbComponents, int width, int hight)
{
Queue<byte> rgbComponentQueue = new Queue<byte>(rgbComponents);
Bitmap bitmap = new Bitmap(width, hight);
for (int y = 0; y < hight; y++)
{
for (int x = 0; x < width; x++)
{
bitmap.SetPixel(x, y, Color.FromArgb(rgbComponentQueue.Dequeue(), rgbComponentQueue.Dequeue(), rgbComponentQueue.Dequeue()));
}
}
return bitmap;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment