Last active
April 15, 2021 04:22
-
-
Save srakowski/ee933222e6f77b7152ce58757d954f17 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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