Skip to content

Instantly share code, notes, and snippets.

@srakowski
Last active April 15, 2021 04:22
Show Gist options
  • Save srakowski/ee933222e6f77b7152ce58757d954f17 to your computer and use it in GitHub Desktop.
Save srakowski/ee933222e6f77b7152ce58757d954f17 to your computer and use it in GitHub Desktop.
namespace Steg
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
class Program
{
// Pixels of an image can be structured to have a Red, Green, Blue,
// and Alpha channels with each channel is stored as a byte of data
// representing 4 bytes of data per pixel.
// Example #3AE048FF
// Channel Hex Dec Bin
// Red 0x3A 58 0011 1010
// Green 0xE0 224 1110 0000
// Blue 0x48 72 0100 1000
// Alpha 0xFF 255 1111 1111
// Changing the 2 least significant bits doesn't have much effect on
// the shade of color. A byte of data may be distributed accross
// the lower 2 bits of each channel.
// Data = 23 = 0001 0111
// R 0011 10[10] <- 00 56
// G 1110 00[00] <- 01 225
// B 0100 10[00] <- 01 73
// A 1111 11[11] <- 11 255
// Storage Capacity of an Image
// 1 pixel = 1 byte
// Image width=160px height=205px
// 160 x 205 = 32,800 bytes of data storage
// A 4k image has 8,294,400 pixels = 8.2944Mb = ~5 floppy disks!
private static void CopyMessageToImage(byte[] imgData, byte[] msgData, byte terminator)
{
for (var s = 0; s < msgData.Length + 1; s++)
{
var b = s < msgData.Length ? msgData[s] : terminator;
var t = s * 4;
imgData[t + 0] = (byte)((imgData[t + 0] & 0xFC) | ((b & 0xC0) >> 6));
imgData[t + 1] = (byte)((imgData[t + 1] & 0xFC) | ((b & 0x30) >> 4));
imgData[t + 2] = (byte)((imgData[t + 2] & 0xFC) | ((b & 0xC) >> 2));
imgData[t + 3] = (byte)((imgData[t + 3] & 0xFC) | (b & 0x3));
}
}
private static byte[] ReadMessageFromImage(byte[] imgData, byte terminator)
{
var message = new List<byte>();
for (var i = 0; i < imgData.Length; i += 4)
{
byte b = 0;
b |= (byte)((imgData[i + 0] & 0x3) << 6);
b |= (byte)((imgData[i + 1] & 0x3) << 4);
b |= (byte)((imgData[i + 2] & 0x3) << 2);
b |= (byte)((imgData[i + 3] & 0x3));
if (b == terminator)
{
break;
}
message.Add(b);
}
return message.ToArray();
}
private static Command CMD = Command.WRITE;
private const string FILE_PATH = @"C:\Users\srako\Desktop\logo.png";
#region Application
enum Command
{
READ,
WRITE
}
private static byte[] ReadImageData(Bitmap image)
{
var bd = image.LockBits(
new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
var imgData = new byte[image.Width * image.Height * 4];
Marshal.Copy(bd.Scan0, imgData, 0, imgData.Length);
image.UnlockBits(bd);
return imgData;
}
private static void WriteToImage(string filePath)
{
using var image = new Bitmap(filePath);
var imgData = ReadImageData(image);
Console.Write("Message: ");
var message = Console.ReadLine();
var msgData = Encoding.UTF8.GetBytes(message);
if (imgData.Length / 4 < msgData.Length)
{
Console.WriteLine("The image does not contain enough storage for this message!");
return;
}
CopyMessageToImage(imgData, msgData, 0);
var p = 0;
for (var i = 0; i < imgData.Length; i += 4)
{
image.SetPixel(
p % image.Width,
p / image.Width,
Color.FromArgb(
imgData[i + 3],
imgData[i + 2],
imgData[i + 1],
imgData[i + 0]
)
);
p++;
}
image.Save(GetReadPath(filePath));
}
private static string GetReadPath(string filePath)
{
return Path.Combine(
Path.GetDirectoryName(filePath),
Path.GetFileNameWithoutExtension(filePath) + ".o.png"
);
}
private static void ReadFromImage(string filePath)
{
using var image = new Bitmap(filePath);
var imgData = ReadImageData(image);
var data = ReadMessageFromImage(imgData, 0);
var msg = Encoding.UTF8.GetString(data);
Console.WriteLine($"Message: {msg}");
Console.ReadKey();
}
static void Main(string[] args)
{
var cmd = CMD;
switch (cmd.ToString().ToLower())
{
case "write": WriteToImage(FILE_PATH); return;
case "read": ReadFromImage(GetReadPath(FILE_PATH)); return;
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment